# Лабораторна 2 ## Завдання Зробити клієнт-серверний додаток на мові javascript, який повинен запускатися в локальній мережі (localhost) та у глобальній мережі (glitch.com, replit.com), на рівні протоколу HTTP, використовуючи методи I) GET II) POST, Додаток повинен тестувати часи подорожі та обробки матриць з числами $x_{i,j}$ типу double ($x_{i,j} \in [1..100]$). Матриці повинні залежно від запиту оброблятися на сервері по-різному: a) сервер повинен обчислити за вхідною матрицею матрицю зі зверненими елементами b) сервер повинен обчислити за вхідною матрицею звернену матрицю Перевірити роботу програми для розмірів матриць 256x256, 512x512, 1024x1024, 2048x2048, 4096x4096, 8192x8192 Програма має оцінити такі параметри каналу як пряму та зворотню затримки 𝛿 передачі між термінальними вузлами з'єднання, пряму та зворотню пропускну здатності каналу (Bandwidth=B), пряму та зворотню затримки передавання в каналі – transmission delay (𝜏) пряму та зворотню відносну затримку каналу 𝑟, пряму та зворотню ефективність каналу 𝜂 Програма повинна передбачати час ${T_1}_\Sigma$ передачі даних на сервер, час $T_s$ обробки даних на сервері, час ${T_2}_\Sigma$ передачі оброблених даних із сервера на клієнт та загальний сумарний час $T' = {T_1}_\Sigma + T_s + {T_2}_\Sigma$ та порівнювати його з реальним часом $T$ отримання результату після початку запиту клієнта до сервера. Результати представити у вигляді таблиць ## Рішення index.html ```html=1 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>lab2</title> <style> body { font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif; color: #333; margin: 0; padding: 0 5px 15px 5px; min-height: 100vh; } .container { background-color: #fff; border-radius: 12px; padding: 0 30px ; width: 100%; text-align: center; margin: 0 auto; } .header { margin-top: 8px; display: flex; align-items: center; justify-content: space-evenly; } h1 { color: #2c3e50; margin: 0; font-size: 2.5em; } .button { background-color: #3498db; color: white; padding: 12px 24px; border: none; border-radius: 6px; cursor: pointer; font-size: 1em; transition: background-color 0.3s ease; } .button:hover { background-color: #2980b9; } .button:disabled { background-color: #777; cursor: not-allowed; } .table-container { overflow-x: auto; } table { width: 100%; border-collapse: collapse; margin-top: 20px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.05); margin-top: 8px; } th, td { border: 1px solid #ddd; padding: 10px 15px; text-align: center; } th { background-color: #34495e; /* Dark gray header */ color: white; font-weight: bold; /* text-transform: uppercase; */ font-size: 12px; } tbody tr:nth-child(even) { background: lightblue; } #estimation { display: none; } </style> </head> <body> <div class="container"> <div class="header"> <h1>Results</h1> <p id="estimation"></p> <button class="button" onclick="run()" id="runButton">Run Test</button> </div> <div class="table-container"> <table> <thead> <tr> <th>Operation</th> <th>Data Size</th> <th>Transmission Time (ms)</th> <th>Processing Time (ms)</th> <th>Total Time T′ (ms)</th> <th>Measured Real Total Time T (ms)</th> <th>Delay δ (s)</th> <th>Transmission Delay τ (s)</th> <th>Bandwidth (bit/s)</th> <th>Relative Delay</th> <th>Channel Efficiency η</th> </tr> </thead> <tbody id="results"></tbody> </table> </div> </div> <script src="script.js"></script> </body> </html> ``` script.js ```js=1 const sizes = [256, 512, 1024, 2048, 4096]; const operations = ["invertElements", "invertMatrix"]; async function run() { let previousProcessTime = 0; const btn = document.getElementById("runButton"); const tableBody = document.getElementById("results"); const estimationEl = document.getElementById("estimation") btn.disabled = true; tableBody.innerHTML = ''; for (const operation of operations) { previousProcessTime = 0; estimationEl.style.display = 'none'; for (const size of sizes) { if (operation === "invertMatrix" && size > 2048) continue; if(previousProcessTime !== 0) { const estimatedSec = Math.round(previousProcessTime * (operation === 'invertElements' ? 4 : 8) / 1000) estimationEl.innerHTML = `you will see next line in about ${estimatedSec} sec`; estimationEl.style.display = 'block'; } const { transferTime, processingTime, summaryTime, totalTime, transferDelay, tau, bandwidth, relativeDelay, channelEfficiency, } = await test(operation, size); const tr = document.createElement("tr"); tr.innerHTML = ` <td>${operation}</td> <td>${size}</td> <td>${transferTime.toFixed(2)}</td> <td>${processingTime.toFixed(2)}</td> <td>${summaryTime.toFixed(2)}</td> <td>${totalTime.toFixed(2)}</td> <td>${transferDelay.toFixed(4)}</td> <td>${tau.toFixed(4)}</td> <td>${bandwidth.toFixed(2)}</td> <td>${relativeDelay.toFixed(4)}</td> <td>${channelEfficiency.toFixed(4)}</td> `; tableBody.appendChild(tr); previousProcessTime = processingTime; } } btn.disabled = false; estimationEl.style.display = 'none'; } async function test(operation, size) { const requestData = generateMatrix(size); const data = JSON.stringify({ matrix: requestData, operation: operation }); const start = performance.now(); const response = await fetch(`/test`, { method: "POST", headers: { "Content-Type": "application/json" }, body: data, }); const endTransfer = performance.now(); const responseData = await response.json(); const endTotal = performance.now(); const processingTime = responseData.processingTime; const transferTime = endTransfer - start - processingTime; const summaryTime = transferTime + processingTime; // T′ const totalTime = endTotal - start; // T // channel parameters const rtt = await getRTT(); // RTT const totalBitsSize = size * size * 64; // Розмір у бітах (double = 64 біт) const transferDelay = rtt / 2000; // Затримка δ const tau = (transferTime - rtt) / 1000; const bandwidth = totalBitsSize / tau; const relativeDelay = transferDelay / tau; // Відносна затримка const channelEfficiency = 1 / (1 + 2 * relativeDelay); // Ефективність каналу // Повертаємо всі обчислені значення return { transferTime, processingTime, summaryTime, totalTime, transferDelay, tau, bandwidth, relativeDelay, channelEfficiency, }; } async function getRTT() { const rttResults = []; const testMatrix = generateMatrix(10); const data = JSON.stringify({ matrix: testMatrix, operation: "invertElements" }); for (let i = 0; i < 10; i++) { const start = performance.now(); try { const response = await fetch("/test", { method: "POST", headers: { "Content-Type": "application/json" }, body: data, }); const end = performance.now(); if (response.ok) { const responseData = await response.json(); const processingTime = responseData.processingTime; const rtt = (end - start) - processingTime; rttResults.push(rtt); } else { console.error(`RTT request error: ${response.status}`); } } catch (error) { console.error("Error during RTT request:", error); } await new Promise((resolve) => setTimeout(resolve, 100)); } rttResults.sort((a, b) => a - b); const mid = Math.floor(rttResults.length / 2); const medianRTT = rttResults.length % 2 !== 0 ? rttResults[mid] : (rttResults[mid - 1] + rttResults[mid]) / 2; return medianRTT; } function generateMatrix(size) { return Array(size).fill().map(() => Array(size).fill().map(() => Math.random() * 99 + 1)); } ``` server.js ```js=1 const http = require('http'); const fs = require('fs'); const path = require('path'); const math = require('mathjs'); const { performance } = require('perf_hooks'); const PORT = 3000; const server = http.createServer( /////////////// (req, res) => { if (req.method === 'GET') { if (req.url === '/') { fs.readFile(path.join(__dirname, 'index.html'), //////////////// (err, data) => { if (err) { res.writeHead(500); res.end('Error while loading HTML file'); } else { res.writeHead(200, { 'Content-Type': 'text/html' }); res.end(data); } } ); } else if (req.url === '/script.js') { fs.readFile(path.join(__dirname, 'script.js'), //////////////// (err, data) => { if (err) { res.writeHead(500); res.end('Error while loading JS file'); } else { res.writeHead(200, { 'Content-Type': 'application/javascript' }); res.end(data); } } ); } } else if (req.method === 'POST' && req.url === '/test') { let body = ""; req.on("data", (chunk) => body += chunk.toString()); req.on("end", /////////////// () => { try { const { matrix, operation } = JSON.parse(body); let result; const startProcessingTime = performance.now(); if (operation === "invertElements") { result = matrix.map(row => row.map(value => 1 / value)); } else if (operation === "invertMatrix") { result = math.inv(matrix); } const endProcessingTime = performance.now(); const processingTime = endProcessingTime - startProcessingTime; res.writeHead(200, { "Content-Type": "application/json" }); res.end(JSON.stringify({ result, processingTime })); } catch (error) { res.writeHead(400, { "Content-Type": "application/json" }); res.end(JSON.stringify({ error: error.message })); } } ); } } ); server.listen(PORT, () => { console.log(`Server running at http://localhost:${PORT}`); }); ``` ## Результати replit ![lab2_1replit](https://hackmd.io/_uploads/S1YINDwlgx.png) ![lab2_1replit2](https://hackmd.io/_uploads/rJkdBwPllx.png) localhost ![lab2_1local](https://hackmd.io/_uploads/ryDT-wDlll.png) ![lab2_1local2](https://hackmd.io/_uploads/HywpWDwxex.png) ## Висновок Час обробки на сервері (Ts) значною мірою залежить від типу операції та розміру матриці. - Для операції обернення матриці спостерігається значне збільшення часу, що відповідає теоретичній складності `O(n³)`. - Операція інвертування елементів значно менш витратна, що узгоджується з оцінкою складності `O(n²)`. Мережева інфраструктура Replit показує обмеження при роботі з великими матрицями, зокрема значні затримки при передачі даних і низьку пропускну здатність у пік навантаження. Локальний сервер забезпечує значно кращу ефективність, стабільність і передбачуваність часу. Для реальних задач обробки даних доцільно використовувати операцію invert для великих матриць як в хмарному, так і в локальному середовищі. Однак операцію inverse слід застосовувати лише для невеликих матриць, особливо якщо обробка виконується в онлайн-середовищі з обмеженими ресурсами, такими як Replit.