blob: 98a66612a0a9c29d172f345c6b67a710662c0220 [file] [log] [blame]
Christoffer Adamsen3146d502024-09-03 15:01:49 +02001<!DOCTYPE html>
2<html>
3<head>
4 <meta charset="utf-8">
5 <title>Retrace perf</title>
6 <link rel="stylesheet" href="stylesheet.css">
7</head>
8<body>
9 <div id="benchmark-selectors"></div>
10 <div>
11 <canvas id="myChart"></canvas>
12 </div>
13 <div>
14 <div style="float: left; width: 50%">
15 <button type="button" id="show-more-left" disabled></button>
16 <button type="button" id="show-less-left"></button>
17 </div>
18 <div style="float: left; text-align: right; width: 50%">
19 <button type="button" id="show-less-right"></button>
20 <button type="button" id="show-more-right" disabled></button>
21 </div>
22 </div>
23 <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.3/dist/chart.umd.min.js"></script>
24 <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.2.0"></script>
25 <script src="extensions.js"></script>
26 <script src="utils.js"></script>
27 <script type="module">
28 import chart from "./chart.js";
29 import dom from "./dom.js";
30 import scales from "./scales.js";
31 import state from "./state.js";
32
Christoffer Adamsend15c48b2024-09-04 10:38:59 +020033 const commits = await state.importCommits("./retrace_benchmark_data.json");
34 state.initializeBenchmarks();
35 state.initializeLegends({
36 'Runtime': { default: true },
37 'Runtime variance': { default: true }
38 });
39 state.initializeZoom();
40 dom.initializeBenchmarkSelectors();
41 dom.initializeChartNavigation();
42
Christoffer Adamsen3146d502024-09-03 15:01:49 +020043 // Chart data provider.
44 function getData(filteredCommits) {
45 const labels = filteredCommits.map((c, i) => c.index);
46 const datasets = getDatasets(filteredCommits);
47 return {
48 labels: labels,
49 datasets: datasets
50 };
51 }
52
53 function getDatasets(filteredCommits) {
54 const datasets = [];
55 state.forEachSelectedBenchmark(
56 selectedBenchmark => {
57 const runtimeData =
58 filteredCommits.map(
59 (c, i) =>
60 selectedBenchmark in filteredCommits[i].benchmarks
61 ? getAllResults(selectedBenchmark, filteredCommits[i], "runtime")
62 .min()
63 .ns_to_s()
64 : NaN);
65 const runtimeScatterData = [];
66 for (const commit of filteredCommits.values()) {
67 if (!(selectedBenchmark in commit.benchmarks)) {
68 continue;
69 }
70 const runtimes = getAllResults(selectedBenchmark, commit, "runtime")
71 for (const runtime of runtimes.values()) {
72 runtimeScatterData.push({ x: commit.index, y: runtime.ns_to_s() });
73 }
74 }
75
76 const skipped = (ctx, value) => ctx.p0.skip || ctx.p1.skip ? value : undefined;
77 datasets.push(...[
78 {
79 benchmark: selectedBenchmark,
80 type: 'line',
81 label: 'Runtime',
82 data: runtimeData,
83 datalabels: {
84 labels: {
85 value: null
86 }
87 },
88 tension: 0.1,
89 yAxisID: 'y_runtime',
90 segment: {
91 borderColor: ctx =>
92 skipped(
93 ctx,
94 chart.get()
95 ? chart.get().data.datasets[ctx.datasetIndex].backgroundColor
96 : undefined),
97 borderDash: ctx => skipped(ctx, [6, 6]),
98 },
99 spanGaps: true
100 },
101 {
102 benchmark: selectedBenchmark,
103 type: 'scatter',
104 label: 'Runtime variance',
105 data: runtimeScatterData,
106 datalabels: {
107 labels: {
108 value: null
109 }
110 },
111 yAxisID: 'y_runtime'
112 }
113 ]);
114 });
115 return datasets;
116 }
117
118 // Chart options.
119 const options = {
120 onHover: (event, chartElement) =>
121 event.native.target.style.cursor =
122 chartElement[0] ? 'pointer' : 'default',
123 plugins: {
124 datalabels: {
125 backgroundColor: 'rgba(255, 255, 255, 0.7)',
126 borderColor: 'rgba(128, 128, 128, 0.7)',
127 borderRadius: 4,
128 borderWidth: 1,
129 color: context => chart.getDataPercentageChange(context) < 0 ? 'green' : 'red',
130 display: context => {
131 var percentageChange = chart.getDataPercentageChange(context);
132 return percentageChange !== null && Math.abs(percentageChange) >= 0.1;
133 },
134 font: {
135 size: 20,
136 weight: 'bold'
137 },
138 offset: 8,
139 formatter: chart.getDataLabelFormatter,
140 padding: 6
141 },
142 legend: {
143 labels: {
144 filter: (legendItem, data) => {
145 // Only retain the legends for the first selected benchmark. If
146 // multiple benchmarks are selected, then use the legends of the
147 // first selected benchmark to control all selected benchmarks.
148 const numUniqueLegends =
149 data.datasets.length / state.selectedBenchmarks.size;
150 return legendItem.datasetIndex < numUniqueLegends;
151 },
152 },
153 onClick: (e, legendItem, legend) => {
154 const clickedLegend = legendItem.text;
155 if (state.selectedLegends.has(clickedLegend)) {
156 state.selectedLegends.delete(clickedLegend);
157 } else {
158 state.selectedLegends.add(clickedLegend);
159 }
160 chart.update(false, true);
161 },
162 },
163 tooltip: {
164 callbacks: {
165 title: context => {
166 const elementInfo = context[0];
167 var commit;
168 if (elementInfo.dataset.type == 'line') {
169 commit = commits[state.zoom.left + elementInfo.dataIndex];
170 } else {
171 console.assert(elementInfo.dataset.type == 'scatter');
172 commit = commits[elementInfo.raw.x];
173 }
174 return commit.title;
175 },
176 footer: context => {
177 const elementInfo = context[0];
178 var commit;
179 if (elementInfo.dataset.type == 'line') {
180 commit = commits[state.zoom.left + elementInfo.dataIndex];
181 } else {
182 console.assert(elementInfo.dataset.type == 'scatter');
183 commit = commits[elementInfo.raw.x];
184 }
185 const dataset = chart.get().data.datasets[elementInfo.datasetIndex];
186 return `App: ${dataset.benchmark}\n`
187 + `Author: ${commit.author}\n`
188 + `Submitted: ${new Date(commit.submitted * 1000).toLocaleString()}\n`
189 + `Hash: ${commit.hash}\n`
190 + `Index: ${commit.index}`;
191 }
192 }
193 }
194 },
195 responsive: true,
196 scales: scales.get()
197 };
198
Christoffer Adamsen3146d502024-09-03 15:01:49 +0200199 chart.setDataProvider(getData);
200 chart.initializeChart(options);
201 </script>
202</body>
203</html>