| <!DOCTYPE html> |
| <html> |
| <head> |
| <meta charset="UTF-8" /> |
| <title>IPC Performance Tests</title> |
| <script src="https://cdn.plot.ly/plotly-2.26.0.min.js"></script> |
| <style> |
| body { |
| font-family: Tahoma, Serif; |
| font-size: 10pt; |
| } |
| |
| .left { |
| text-align: left; |
| } |
| |
| .right { |
| text-align: right; |
| } |
| |
| .positive { |
| color: green; |
| font-weight: bold; |
| } |
| |
| .negative { |
| color: red; |
| font-weight: bold; |
| } |
| |
| .center { |
| text-align: center; |
| } |
| |
| table.resultTable { |
| border: 1px solid black; |
| border-collapse: collapse; |
| empty-cells: show; |
| width: 100%; |
| } |
| |
| table.resultTable td { |
| padding: 2px 4px; |
| border: 1px solid black; |
| } |
| |
| table.resultTable > thead > tr { |
| font-weight: bold; |
| background: lightblue; |
| } |
| |
| table.resultTable > tbody > tr:nth-child(odd) { |
| background: white; |
| } |
| |
| table.resultTable > tbody > tr:nth-child(even) { |
| background: lightgray; |
| } |
| |
| .hide { |
| display: none; |
| } |
| </style> |
| </head> |
| |
| <body background-color="white"> |
| <h1>IPC Performance Tests</h1> |
| |
| <table> |
| <tr> |
| <td> |
| <p> |
| There is no progress indication of the tests because it |
| significantly influences measurements. <br />It usually takes 15 |
| seconds (for 1000 samples) to complete the tests. <br /><b>AL</b> - |
| ArgumentList-based process messages. <b>SM</b> - |
| SharedMemoryRegion-based process messages. |
| </p> |
| </td> |
| </tr> |
| <tr> |
| <td> |
| Samples: |
| <input |
| id="sSamples" |
| type="text" |
| value="1000" |
| required |
| pattern="[0-9]+" |
| /> |
| <button id="sRun" autofocus onclick="runTestSuite()">Run</button> |
| </td> |
| </tr> |
| </table> |
| |
| <div style="padding-top: 10px; padding-bottom: 10px"> |
| <table id="resultTable" class="resultTable"> |
| <thead> |
| <tr> |
| <td class="center" style="width: 8%">Message Size</td> |
| <td class="center" style="width: 8%">AL Round Trip Avg, μs</td> |
| <td class="center" style="width: 8%">SM Round Trip Avg, μs</td> |
| <td class="center" style="width: 10%">Relative Trip Difference</td> |
| <td class="center" style="width: 8%">AL Speed, MB/s</td> |
| <td class="center" style="width: 8%">SM Speed, MB/s</td> |
| <td class="center" style="width: 10%">Relative Speed Difference</td> |
| <td class="center" style="width: 8%">AL Standard Deviation</td> |
| <td class="center" style="width: 8%">SM Standard Deviation</td> |
| </tr> |
| </thead> |
| <tbody> |
| <!-- result rows here --> |
| </tbody> |
| </table> |
| </div> |
| |
| <div id="round_trip_avg_chart"> |
| <!-- Average round trip linear chart will be drawn inside this DIV --> |
| </div> |
| |
| <div id="box_plot_chart"> |
| <!-- Box plot of round trip time will be drawn inside this DIV --> |
| </div> |
| |
| <script type="text/javascript"> |
| let tests = []; |
| let box_plot_test_data = []; |
| let round_trip_avg_plot_data = []; |
| |
| function testSendProcessMessageResult( |
| testIndex, |
| fromRendererToBrowser, |
| fromBrowserToRenderer |
| ) { |
| const test = tests[testIndex]; |
| const roundTrip = fromRendererToBrowser + fromBrowserToRenderer; |
| test.totalRoundTrip += roundTrip; |
| test.sample++; |
| box_plot_test_data[testIndex].x.push(roundTrip); |
| |
| setTimeout(execTest, 0, testIndex); |
| } |
| |
| function sendRequest(size, testIndex) { |
| window.testSendProcessMessage({ |
| size: size, |
| testId: testIndex, |
| }); |
| } |
| |
| function sendSMRRequest(size, testIndex) { |
| window.testSendSMRProcessMessage({ |
| size: size, |
| testId: testIndex, |
| }); |
| } |
| |
| function getStandardDeviation(array, mean) { |
| const n = array.length; |
| if (n < 5) return null; |
| return Math.sqrt( |
| array.map((x) => Math.pow(x - mean, 2)).reduce((a, b) => a + b) / |
| (n - 1) |
| ); |
| } |
| |
| function execTest(testIndex) { |
| const test = tests[testIndex]; |
| |
| if (test.sample >= test.totalSamples) { |
| setTimeout(execNextTest, 0, testIndex); |
| } else { |
| test.func(test.messageSize, test.index); |
| } |
| } |
| |
| function column(prepared, value) { |
| return ( |
| "<td class='right'>" + (!prepared ? "-" : value.toFixed(2)) + "</td>" |
| ); |
| } |
| |
| function relativeDiffColumn(prepared, value, isBiggerBetter) { |
| if (!prepared) return "<td class='right'>-</td>"; |
| |
| const isPositive = value > 0 == isBiggerBetter; |
| return [ |
| "<td class='right ", |
| isPositive ? "positive" : "negative", |
| "'>", |
| value > 0 ? "+" : "", |
| value.toFixed(2), |
| "%</td>", |
| ].join(""); |
| } |
| |
| function displayResult(test) { |
| const id = "testResultRow_" + test.index; |
| |
| const markup = [ |
| "<tr id='", |
| id, |
| "'>", |
| "<td class='left'>", |
| test.name, |
| "</td>", |
| column(test.prepared, test.avgRoundTrip), |
| column(test.prepared, test.avgRoundTripSMR), |
| relativeDiffColumn(test.prepared, test.relativeTripDiff, false), |
| column(test.prepared, test.speed), |
| column(test.prepared, test.speedSMR), |
| relativeDiffColumn(test.prepared, test.relativeSpeedDiff, true), |
| "<td class='right'>", |
| !test.prepared || test.stdDeviation == null |
| ? "-" |
| : test.stdDeviation.toFixed(2), |
| "</td>", |
| "<td class='right'>", |
| !test.prepared || test.stdDeviationSMR == null |
| ? "-" |
| : test.stdDeviationSMR.toFixed(2), |
| "</td>", |
| "</tr>", |
| ].join(""); |
| |
| const row = document.getElementById(id); |
| if (row) { |
| row.outerHTML = markup; |
| } else { |
| const tbody = document.getElementById("resultTable").tBodies[0]; |
| tbody.insertAdjacentHTML("beforeEnd", markup); |
| } |
| } |
| |
| function buildTestResults(tests) { |
| testResults = []; |
| |
| let oldRoundTrip = { |
| x: [], |
| y: [], |
| type: "scatter", |
| name: "ArgumentList", |
| }; |
| |
| let newRoundTrip = { |
| x: [], |
| y: [], |
| type: "scatter", |
| name: "SharedMemoryRegion", |
| }; |
| |
| for (let i = 0; i < tests.length / 2; i++) { |
| const index = testResults.length; |
| |
| const test = tests[i * 2]; |
| const testSMR = tests[i * 2 + 1]; |
| |
| const avgRoundTrip = test.totalRoundTrip / test.totalSamples; |
| const avgRoundTripSMR = testSMR.totalRoundTrip / testSMR.totalSamples; |
| const relativeTripDiff = |
| ((avgRoundTripSMR - avgRoundTrip) / avgRoundTrip) * 100; |
| |
| // In MB/s |
| const speed = test.messageSize / avgRoundTrip; |
| const speedSMR = testSMR.messageSize / avgRoundTripSMR; |
| const relativeSpeedDiff = ((speedSMR - speed) / speed) * 100; |
| |
| const stdDeviation = getStandardDeviation( |
| box_plot_test_data[test.index].x, |
| avgRoundTrip |
| ); |
| const stdDeviationSMR = getStandardDeviation( |
| box_plot_test_data[testSMR.index].x, |
| avgRoundTripSMR |
| ); |
| |
| testResults.push({ |
| name: humanFileSize(test.messageSize), |
| index: index, |
| prepared: true, |
| avgRoundTrip: avgRoundTrip, |
| avgRoundTripSMR: avgRoundTripSMR, |
| relativeTripDiff: relativeTripDiff, |
| speed: speed, |
| speedSMR: speedSMR, |
| relativeSpeedDiff: relativeSpeedDiff, |
| stdDeviation: stdDeviation, |
| stdDeviationSMR: stdDeviationSMR, |
| }); |
| |
| oldRoundTrip.x.push(test.messageSize); |
| newRoundTrip.x.push(test.messageSize); |
| oldRoundTrip.y.push(avgRoundTrip); |
| newRoundTrip.y.push(avgRoundTripSMR); |
| } |
| |
| round_trip_avg_plot_data = [oldRoundTrip, newRoundTrip]; |
| return testResults; |
| } |
| |
| function buildEmptyTestResults(tests) { |
| testResults = []; |
| for (let i = 0; i < tests.length / 2; i++) { |
| const index = testResults.length; |
| const test = tests[i * 2]; |
| |
| testResults.push({ |
| name: humanFileSize(test.messageSize), |
| index: index, |
| prepared: false, |
| }); |
| } |
| return testResults; |
| } |
| |
| function prepareQueuedTests(totalSamples) { |
| if (totalSamples <= 0) totalSamples = 1; |
| |
| tests.forEach((test) => { |
| test.sample = 0; |
| test.totalRoundTrip = 0; |
| test.totalSamples = totalSamples; |
| }); |
| |
| testResults = buildEmptyTestResults(tests); |
| testResults.forEach((result) => displayResult(result)); |
| |
| round_trip_avg_plot_data = []; |
| box_plot_test_data.forEach((data) => { |
| data.x = []; |
| }); |
| } |
| |
| function queueTest(name, messageSize, testFunc) { |
| const testIndex = tests.length; |
| test = { |
| name: name, |
| messageSize: messageSize, |
| index: testIndex, |
| func: testFunc, |
| }; |
| tests.push(test); |
| |
| box_plot_test_data.push({ |
| x: [], |
| type: "box", |
| boxpoints: "all", |
| name: name, |
| jitter: 0.3, |
| pointpos: -1.8, |
| }); |
| } |
| |
| function execNextTest(testIndex) { |
| testIndex++; |
| if (tests.length <= testIndex) { |
| testSuiteFinished(); |
| } else { |
| execTest(testIndex); |
| } |
| } |
| |
| function execQueuedTests(totalSamples) { |
| prepareQueuedTests(totalSamples); |
| // Let the updated table render before starting the tests |
| setTimeout(execNextTest, 200, -1); |
| } |
| |
| function setSettingsState(disabled) { |
| document.getElementById("sSamples").disabled = disabled; |
| document.getElementById("sRun").disabled = disabled; |
| } |
| |
| function testSuiteFinished() { |
| testResults = buildTestResults(tests); |
| testResults.forEach((result) => displayResult(result)); |
| |
| const round_trip_layout = { |
| title: "Average round trip, ms (Smaller Better)", |
| }; |
| Plotly.newPlot( |
| "round_trip_avg_chart", |
| round_trip_avg_plot_data, |
| round_trip_layout |
| ); |
| |
| const box_plot_layout = { |
| title: "Round Trip Time, ms", |
| }; |
| Plotly.newPlot("box_plot_chart", box_plot_test_data, box_plot_layout); |
| setSettingsState(false); |
| } |
| |
| function humanFileSize(bytes) { |
| const step = 1024; |
| |
| if (Math.abs(bytes) < step) { |
| return bytes + " B"; |
| } |
| |
| const units = [" KB", " MB", " GB"]; |
| let u = -1; |
| let count = 0; |
| |
| do { |
| bytes /= step; |
| u += 1; |
| count += 1; |
| } while (Math.abs(bytes) >= step && u < units.length - 1); |
| |
| return bytes.toString() + units[u]; |
| } |
| |
| window.runTestSuite = () => { |
| Plotly.purge("round_trip_avg_chart"); |
| Plotly.purge("box_plot_chart"); |
| setSettingsState(true); |
| const totalSamples = parseInt( |
| document.getElementById("sSamples").value |
| ); |
| execQueuedTests(totalSamples); |
| return false; |
| }; |
| |
| for (let size = 512; size <= 512 * 1024; size = size * 2) { |
| queueTest(humanFileSize(size) + " AL", size, sendRequest); |
| queueTest(humanFileSize(size) + " SM", size, sendSMRRequest); |
| } |
| |
| const totalSamples = parseInt(document.getElementById("sSamples").value); |
| prepareQueuedTests(totalSamples); |
| </script> |
| </body> |
| </html> |