blob: a992005818c1084e3c6675f18796f6f439199d89 [file] [log] [blame]
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>R8 perf</title>
<link rel="stylesheet" href="stylesheet.css">
</head>
<body>
<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="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";
const commits = await state.importCommits("./r8_benchmark_data.json");
state.initializeBenchmarks();
state.initializeLegends({
'Dex size': { default: true },
'Instruction size': { default: true },
'Composable size': { default: true },
'Oat size': { default: true },
'Nondeterminism': { default: true },
'Runtime': { default: true },
'Runtime variance': { default: false },
'Warmup': { default: false }
});
state.initializeZoom();
dom.initializeBenchmarkSelectors();
dom.initializeChartNavigation();
// Chart data provider.
function getData(filteredCommits) {
const labels = filteredCommits.map((c, i) => c.index);
const datasets = getDatasets(filteredCommits);
return {
labels: labels,
datasets: datasets
};
}
function getDatasets(filteredCommits) {
const datasets = [];
state.forEachSelectedBenchmark(
selectedBenchmark => {
const codeSizeData =
filteredCommits.map(
(c, i) => getSingleResult(selectedBenchmark, filteredCommits[i], "code_size"));
const instructionCodeSizeData =
filteredCommits.map(
(c, i) => getSingleResult(selectedBenchmark, filteredCommits[i], "ins_code_size"));
const composableInstructionCodeSizeData =
filteredCommits.map(
(c, i) => getSingleResult(selectedBenchmark, filteredCommits[i], "composable_code_size"));
const oatCodeSizeData =
filteredCommits.map(
(c, i) => getSingleResult(selectedBenchmark, filteredCommits[i], "oat_code_size"));
const codeSizeScatterData = [];
for (const commit of filteredCommits.values()) {
if (!(selectedBenchmark in commit.benchmarks)) {
continue;
}
const seen = new Set();
seen.add(getSingleResult(selectedBenchmark, commit, "code_size"));
const codeSizes = getAllResults(selectedBenchmark, commit, "code_size")
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) =>
getAllResults(
selectedBenchmark, filteredCommits[i], "runtime",
results => dom.transformRuntimeData(results).ns_to_s()));
const runtimeScatterData = [];
for (const commit of filteredCommits.values()) {
if (!(selectedBenchmark in commit.benchmarks)) {
continue;
}
const runtimes = getAllResults(selectedBenchmark, commit, "runtime")
for (const runtime of runtimes.values()) {
runtimeScatterData.push({ x: commit.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: 'Dex size',
data: codeSizeData,
datalabels: {
align: 'end',
anchor: 'end'
},
tension: 0.1,
yAxisID: 'y',
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: 'line',
label: 'Instruction size',
data: instructionCodeSizeData,
datalabels: {
align: 'end',
anchor: 'end'
},
tension: 0.1,
yAxisID: 'y_ins_code_size',
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: 'line',
label: 'Composable size',
data: composableInstructionCodeSizeData,
datalabels: {
align: 'start',
anchor: 'start'
},
tension: 0.1,
yAxisID: 'y_ins_code_size',
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: 'line',
label: 'Oat size',
data: oatCodeSizeData,
datalabels: {
align: 'start',
anchor: 'start'
},
tension: 0.1,
yAxisID: 'y_oat_code_size',
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: 'Nondeterminism',
data: codeSizeScatterData,
datalabels: {
labels: {
value: null
}
},
radius: 6,
pointBackgroundColor: 'red'
},
{
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: {
callbacks: {
title: context => {
const elementInfo = context[0];
var commit;
if (elementInfo.dataset.type == 'line') {
commit = commits[state.zoom.left + elementInfo.dataIndex];
} else {
console.assert(elementInfo.dataset.type == 'scatter');
commit = commits[elementInfo.raw.x];
}
return commit.title;
},
footer: context => {
const elementInfo = context[0];
var commit;
if (elementInfo.dataset.type == 'line') {
commit = commits[state.zoom.left + elementInfo.dataIndex];
} else {
console.assert(elementInfo.dataset.type == 'scatter');
commit = commits[elementInfo.raw.x];
}
const dataset = chart.get().data.datasets[elementInfo.datasetIndex];
return `App: ${dataset.benchmark}\n`
+ `Author: ${commit.author}\n`
+ `Submitted: ${new Date(commit.submitted * 1000).toLocaleString()}\n`
+ `Hash: ${commit.hash}\n`
+ `Index: ${commit.index}`;
}
}
}
},
responsive: true,
scales: scales.get()
};
chart.setDataProvider(getData);
chart.initializeChart(options);
</script>
</body>
</html>