| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <meta charset="utf-8"> | |
| <title>Build perf</title> | |
| <link rel="stylesheet" href="stylesheet.css"> | |
| </head> | |
| <body> | |
| <div id="try-selector"> | |
| <input type="checkbox" id="try-checkbox" name="try-selector" checked> | |
| <label for="try-checkbox">Try</label> | |
| </div> | |
| <div id="runtime-fn"> | |
| <input type="radio" id="runtime-min" name="runtime-fn" checked> | |
| <label for="runtime-min">Min</label> | |
| <input type="radio" id="runtime-p50" name="runtime-fn"> | |
| <label for="runtime-p50">P50</label> | |
| <input type="radio" id="runtime-p90" name="runtime-fn"> | |
| <label for="runtime-p90">P90</label> | |
| <input type="radio" id="runtime-p95" name="runtime-fn"> | |
| <label for="runtime-p95">P95</label> | |
| <input type="radio" id="runtime-max" name="runtime-fn"> | |
| <label for="runtime-max">Max</label> | |
| </div> | |
| <div id="branch-selector"> | |
| <input type="radio" id="branch-main" name="branch-selector" checked> | |
| <label for="branch-main">Main</label> | |
| <input type="radio" id="branch-release" name="branch-selector"> | |
| <label for="branch-release">Release</label> | |
| </div> | |
| <div id="benchmark-selectors"></div> | |
| <div> | |
| <canvas id="myChart"></canvas> | |
| </div> | |
| <div> | |
| <div style="float: left; width: 50%"> | |
| <button type="button" id="show-more-left" disabled>⇐</button> | |
| <button type="button" id="show-less-left">⇒</button> | |
| </div> | |
| <div style="float: left; text-align: right; width: 50%"> | |
| <button type="button" id="show-less-right">⇐</button> | |
| <button type="button" id="show-more-right" disabled>⇒</button> | |
| </div> | |
| </div> | |
| <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.3/dist/chart.umd.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.2.0"></script> | |
| <script src="extensions.js"></script> | |
| <script src="utils.js"></script> | |
| <script type="module"> | |
| import chart from "./chart.js"; | |
| import dom from "./dom.js"; | |
| import scales from "./scales.js"; | |
| import state from "./state.js"; | |
| import tooltip from "./tooltip.js"; | |
| const commits = await state.importCommits("./build_benchmark_data.json"); | |
| state.initializeBenchmarks(); | |
| state.initializeLegends({ | |
| 'Runtime': { default: true }, | |
| 'Runtime variance': { default: true }, | |
| 'Warmup': { default: false } | |
| }); | |
| dom.initialize(); | |
| state.initializeZoom(); | |
| // Chart data provider. | |
| function getData(filteredCommits) { | |
| const labels = filteredCommits.map((c, i) => i); | |
| const datasets = getDatasets(filteredCommits); | |
| return { | |
| labels: labels, | |
| datasets: datasets | |
| }; | |
| } | |
| function getDatasets(filteredCommits) { | |
| const datasets = []; | |
| state.forEachSelectedBenchmark( | |
| selectedBenchmark => { | |
| const runtimeData = | |
| filteredCommits.map( | |
| (c, i) => | |
| getAllResults( | |
| selectedBenchmark, filteredCommits[i], "runtime", | |
| results => dom.transformRuntimeData(results).ns_to_s())); | |
| const runtimeScatterData = []; | |
| filteredCommits.forEach((commit, index) => { | |
| if (!(selectedBenchmark in commit.benchmarks)) { | |
| return; | |
| } | |
| const runtimes = getAllResults(selectedBenchmark, commit, "runtime") | |
| for (const runtime of runtimes.values()) { | |
| runtimeScatterData.push({ x: index, y: runtime.ns_to_s() }); | |
| } | |
| }); | |
| const warmupData = | |
| filteredCommits.map( | |
| (c, i) => | |
| getAllWarmupResults( | |
| selectedBenchmark, filteredCommits[i], "runtime", | |
| results => dom.transformRuntimeData(results).ns_to_s())); | |
| const skipped = (ctx, value) => ctx.p0.skip || ctx.p1.skip ? value : undefined; | |
| datasets.push(...[ | |
| { | |
| benchmark: selectedBenchmark, | |
| type: 'line', | |
| label: 'Runtime', | |
| data: runtimeData, | |
| datalabels: { | |
| labels: { | |
| value: null | |
| } | |
| }, | |
| tension: 0.1, | |
| yAxisID: 'y_runtime', | |
| segment: { | |
| borderColor: ctx => | |
| skipped( | |
| ctx, | |
| chart.get() | |
| ? chart.get().data.datasets[ctx.datasetIndex].backgroundColor | |
| : undefined), | |
| borderDash: ctx => skipped(ctx, [6, 6]), | |
| }, | |
| spanGaps: true | |
| }, | |
| { | |
| benchmark: selectedBenchmark, | |
| type: 'scatter', | |
| label: 'Runtime variance', | |
| data: runtimeScatterData, | |
| datalabels: { | |
| labels: { | |
| value: null | |
| } | |
| }, | |
| yAxisID: 'y_runtime' | |
| }, | |
| { | |
| benchmark: selectedBenchmark, | |
| type: 'line', | |
| label: 'Warmup', | |
| data: warmupData, | |
| datalabels: { | |
| labels: { | |
| value: null | |
| } | |
| }, | |
| tension: 0.1, | |
| yAxisID: 'y_runtime', | |
| segment: { | |
| borderColor: ctx => | |
| skipped( | |
| ctx, | |
| chart.get() | |
| ? chart.get().data.datasets[ctx.datasetIndex].backgroundColor | |
| : undefined), | |
| borderDash: ctx => skipped(ctx, [6, 6]), | |
| }, | |
| spanGaps: true | |
| } | |
| ]); | |
| }); | |
| return datasets; | |
| } | |
| // Chart options. | |
| const options = { | |
| onHover: (event, chartElement) => | |
| event.native.target.style.cursor = | |
| chartElement[0] ? 'pointer' : 'default', | |
| plugins: { | |
| datalabels: { | |
| backgroundColor: 'rgba(255, 255, 255, 0.7)', | |
| borderColor: 'rgba(128, 128, 128, 0.7)', | |
| borderRadius: 4, | |
| borderWidth: 1, | |
| color: context => chart.getDataPercentageChange(context) < 0 ? 'green' : 'red', | |
| display: context => { | |
| var percentageChange = chart.getDataPercentageChange(context); | |
| return percentageChange !== null && Math.abs(percentageChange) >= 0.1; | |
| }, | |
| font: { | |
| size: 20, | |
| weight: 'bold' | |
| }, | |
| offset: 8, | |
| formatter: chart.getDataLabelFormatter, | |
| padding: 6 | |
| }, | |
| legend: { | |
| labels: { | |
| filter: (legendItem, data) => { | |
| // Only retain the legends for the first selected benchmark. If | |
| // multiple benchmarks are selected, then use the legends of the | |
| // first selected benchmark to control all selected benchmarks. | |
| const numUniqueLegends = | |
| data.datasets.length / state.selectedBenchmarks.size; | |
| return legendItem.datasetIndex < numUniqueLegends; | |
| }, | |
| }, | |
| onClick: (e, legendItem, legend) => { | |
| const clickedLegend = legendItem.text; | |
| if (state.selectedLegends.has(clickedLegend)) { | |
| state.selectedLegends.delete(clickedLegend); | |
| } else { | |
| state.selectedLegends.add(clickedLegend); | |
| } | |
| chart.update(false, true); | |
| }, | |
| }, | |
| tooltip: tooltip | |
| }, | |
| responsive: true, | |
| scales: scales.get() | |
| }; | |
| chart.setDataProvider(getData); | |
| chart.initializeChart(options); | |
| </script> | |
| </body> | |
| </html> |