Create benchmark overview
Change-Id: Ib48bb238c9bd951b288e64730aa2841f23d8e28b
diff --git a/tools/perf/index.html b/tools/perf/index.html
index 8d3089c..b63053c 100644
--- a/tools/perf/index.html
+++ b/tools/perf/index.html
@@ -5,7 +5,7 @@
<title>R8 perf</title>
</head>
<body>
- <select id="benchmark-selector"></select>
+ <div id="benchmark-selectors"></div>
<div>
<canvas id="myChart"></canvas>
</div>
@@ -62,106 +62,113 @@
}
// DOM references.
- const benchmarkSelector = document.getElementById('benchmark-selector')
+ const benchmarkSelectors = document.getElementById('benchmark-selectors')
const canvas = document.getElementById('myChart');
const showMoreLeft = document.getElementById('show-more-left');
const showLessLeft = document.getElementById('show-less-left');
const showLessRight = document.getElementById('show-less-right');
const showMoreRight = document.getElementById('show-more-right');
- // Initialize benchmark selector.
+ // Initialize benchmark selectors.
const benchmarks = new Set();
for (const commit of commits.values()) {
for (const benchmark in commit.benchmarks) {
benchmarks.add(benchmark)
}
}
- var selectedBenchmark = window.location.hash.substring(1)
- if (!benchmarks.has(selectedBenchmark)) {
- selectedBenchmark = benchmarks.values().next().value;
+ const selectedBenchmarks =
+ new Set(
+ unescape(window.location.hash.substring(1))
+ .split(',')
+ .filter(b => benchmarks.has(b)));
+ if (selectedBenchmarks.size == 0) {
+ selectedBenchmarks.add(benchmarks.values().next().value);
}
for (const benchmark of benchmarks.values()) {
- const opt = document.createElement('option');
- opt.value = benchmark;
- opt.innerHTML = benchmark;
- benchmarkSelector.appendChild(opt);
- if (benchmark == selectedBenchmark) {
- benchmarkSelector.selectedIndex = benchmarkSelector.options.length - 1
- }
+ const input = document.createElement('input');
+ input.type = 'checkbox';
+ input.name = 'benchmark';
+ input.id = benchmark;
+ input.value = benchmark;
+ input.checked = selectedBenchmarks.has(benchmark);
+ input.onchange = function (e) {
+ if (e.target.checked) {
+ selectedBenchmarks.add(e.target.value);
+ } else {
+ selectedBenchmarks.delete(e.target.value);
+ }
+ updateChart();
+ };
+
+ const label = document.createElement('label');
+ label.htmlFor = benchmark;
+ label.innerHTML = benchmark;
+
+ benchmarkSelectors.appendChild(input);
+ benchmarkSelectors.appendChild(label);
}
- benchmarkSelector.onchange = function(event) {
- selectedBenchmark =
- benchmarkSelector.options[benchmarkSelector.selectedIndex].value;
- updateChart();
- window.location.hash = selectedBenchmark;
- };
// Chart data provider.
function getData(start = 0, end = commits.length) {
const filteredCommits =
commits
.slice(start, end);
- //.filter(
- // commit =>
- // selectedBenchmark in commit.benchmarks
- // && commit.benchmarks[selectedBenchmark].results.length > 0);
const labels = filteredCommits.map((c, i) => i);
- const codeSizeData =
- filteredCommits.map(
- (c, i) =>
- selectedBenchmark in filteredCommits[i].benchmarks
- ? filteredCommits[i]
- .benchmarks[selectedBenchmark]
- .results
- .first()
- .code_size
- : NaN);
- const codeSizeScatterData = [];
- for (const commit of filteredCommits.values()) {
- if (!(selectedBenchmark in commit.benchmarks)) {
- continue;
- }
- const codeSizes =
- commit.benchmarks[selectedBenchmark].results.map(result => result.code_size)
- const expectedCodeSize = codeSizes.first();
- if (codeSizes.any(codeSize => codeSize != expectedCodeSize)) {
- const seen = new Set();
- seen.add(expectedCodeSize);
- for (const codeSize of codeSizes.values()) {
- if (!seen.has(codeSize)) {
- codeSizeScatterData.push({ x: commit.index, y: codeSize });
- seen.add(codeSize);
+ const datasets = []
+ for (const selectedBenchmark of selectedBenchmarks.values()) {
+ const codeSizeData =
+ filteredCommits.map(
+ (c, i) =>
+ selectedBenchmark in filteredCommits[i].benchmarks
+ ? filteredCommits[i]
+ .benchmarks[selectedBenchmark]
+ .results
+ .first()
+ .code_size
+ : NaN);
+ const codeSizeScatterData = [];
+ for (const commit of filteredCommits.values()) {
+ if (!(selectedBenchmark in commit.benchmarks)) {
+ continue;
+ }
+ const codeSizes =
+ commit.benchmarks[selectedBenchmark].results.map(result => result.code_size)
+ const expectedCodeSize = codeSizes.first();
+ if (codeSizes.any(codeSize => codeSize != expectedCodeSize)) {
+ const seen = new Set();
+ seen.add(expectedCodeSize);
+ for (const codeSize of codeSizes.values()) {
+ if (!seen.has(codeSize)) {
+ codeSizeScatterData.push({ x: commit.index, y: codeSize });
+ seen.add(codeSize);
+ }
}
}
}
- }
- const runtimeData =
- filteredCommits.map(
- (c, i) =>
- selectedBenchmark in filteredCommits[i].benchmarks
- ? filteredCommits[i]
- .benchmarks[selectedBenchmark]
- .results
- .map(result => result.runtime)
- .min()
- .ns_to_s()
- : NaN);
- const runtimeScatterData = [];
- for (const commit of filteredCommits.values()) {
- if (!(selectedBenchmark in commit.benchmarks)) {
- continue;
+ const runtimeData =
+ filteredCommits.map(
+ (c, i) =>
+ selectedBenchmark in filteredCommits[i].benchmarks
+ ? filteredCommits[i]
+ .benchmarks[selectedBenchmark]
+ .results
+ .map(result => result.runtime)
+ .min()
+ .ns_to_s()
+ : NaN);
+ const runtimeScatterData = [];
+ for (const commit of filteredCommits.values()) {
+ if (!(selectedBenchmark in commit.benchmarks)) {
+ continue;
+ }
+ const runtimes =
+ commit.benchmarks[selectedBenchmark].results.map(result => result.runtime)
+ for (const runtime of runtimes.values()) {
+ runtimeScatterData.push({ x: commit.index, y: runtime.ns_to_s() });
+ }
}
- const runtimes =
- commit.benchmarks[selectedBenchmark].results.map(result => result.runtime)
- for (const runtime of runtimes.values()) {
- runtimeScatterData.push({ x: commit.index, y: runtime.ns_to_s() });
- }
- }
- const skipped = (ctx, value) => ctx.p0.skip || ctx.p1.skip ? value : undefined;
- return {
- labels: labels,
- datasets: [
+ datasets.push(...[
{
type: 'line',
label: 'Code size',
@@ -178,6 +185,7 @@
type: 'scatter',
label: 'Nondeterminism',
data: codeSizeScatterData,
+ radius: 6,
pointBackgroundColor: 'red'
},
{
@@ -199,16 +207,55 @@
data: runtimeScatterData,
yAxisID: 'y2'
}
- ],
+ ]);
+ }
+
+ const skipped = (ctx, value) => ctx.p0.skip || ctx.p1.skip ? value : undefined;
+ return {
+ labels: labels,
+ datasets: datasets,
};
}
+ // Legend tracking.
+ const legends =
+ new Set(['Code size', 'Nondeterminism', 'Runtime', 'Runtime variance']);
+ const selectedLegends =
+ new Set(
+ unescape(window.location.hash.substring(1))
+ .split(',')
+ .filter(l => legends.has(l)));
+ if (selectedLegends.size == 0) {
+ legends.forEach(l => selectedLegends.add(l));
+ }
+
// Chart options.
const options = {
onHover: (event, chartElement) =>
event.native.target.style.cursor =
chartElement[0] ? 'pointer' : 'default',
plugins: {
+ legend: {
+ labels: {
+ filter: function(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 / selectedBenchmarks.size;
+ return legendItem.datasetIndex < numUniqueLegends;
+ },
+ },
+ onClick: function legendClickHandler(e, legendItem, legend) {
+ const clickedLegend = legendItem.text;
+ if (selectedLegends.has(clickedLegend)) {
+ selectedLegends.delete(clickedLegend);
+ } else {
+ selectedLegends.add(clickedLegend);
+ }
+ updateChart();
+ },
+ },
tooltip: {
callbacks: {
title: (context) => {
@@ -258,12 +305,6 @@
}
};
- // Create chart.
- const myChart = new Chart(canvas, {
- data: getData(),
- options: options
- });
-
// Setup click handler.
canvas.onclick = function (event) {
const points =
@@ -324,14 +365,41 @@
function updateChart() {
console.assert(left <= right);
+ // Update datasets.
const newData = getData(left, right);
Object.assign(myChart.data, newData);
+ // Update chart.
myChart.update();
+ // Update legends.
+ for (var datasetIndex = 0;
+ datasetIndex < myChart.data.datasets.length;
+ datasetIndex++) {
+ const datasetMeta = myChart.getDatasetMeta(datasetIndex);
+ datasetMeta.hidden = !selectedLegends.has(datasetMeta.label);
+ }
+ // Update chart.
+ myChart.update();
+ // Update navigation.
showMoreLeft.disabled = left == 0;
showLessLeft.disabled = left == right - 1;
showLessRight.disabled = left == right - 1;
showMoreRight.disabled = right == commits.length;
+ // Update hash.
+ window.location.hash =
+ Array.from(selectedBenchmarks)
+ .concat(
+ selectedLegends.size == legends.size
+ ? []
+ : Array.from(selectedLegends))
+ .join(',');
}
+
+ // Create chart.
+ const myChart = new Chart(canvas, {
+ data: getData(),
+ options: options
+ });
+ updateChart();
</script>
</body>
</html>
\ No newline at end of file