Line graphs for new metrics
Change-Id: I2178c016d0b5b4e2e44f9075583112755a87a0e8
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsSingle.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsSingle.java
index 93b6efb..7553ea8 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsSingle.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsSingle.java
@@ -44,10 +44,22 @@
return codeSizeResults;
}
+ public LongList getInstructionCodeSizeResults() {
+ return instructionCodeSizeResults;
+ }
+
public LongList getComposableInstructionCodeSizeResults() {
return composableInstructionCodeSizeResults;
}
+ public List<Int2ReferenceMap<SegmentInfo>> getDexSegmentsSizeResults() {
+ return dexSegmentsSizeResults;
+ }
+
+ public LongList getDex2OatSizeResult() {
+ return dex2OatSizeResult;
+ }
+
public LongList getRuntimeResults() {
return runtimeResults;
}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsSingleAdapter.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsSingleAdapter.java
index 416594b..86cfe62 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsSingleAdapter.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsSingleAdapter.java
@@ -3,13 +3,16 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.benchmarks;
-import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.dex.DexSection;
+import com.google.common.base.CaseFormat;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import java.lang.reflect.Type;
+import java.util.Collection;
+import java.util.function.IntToLongFunction;
public class BenchmarkResultsSingleAdapter implements JsonSerializer<BenchmarkResultsSingle> {
@@ -17,23 +20,74 @@
public JsonElement serialize(
BenchmarkResultsSingle result, Type type, JsonSerializationContext jsonSerializationContext) {
JsonArray resultsArray = new JsonArray();
- ListUtils.forEachWithIndex(
- result.getCodeSizeResults(),
- (codeSizeResult, iteration) -> {
- JsonObject resultObject = new JsonObject();
- resultObject.addProperty("code_size", codeSizeResult);
- if (!result.getComposableInstructionCodeSizeResults().isEmpty()) {
- resultObject.addProperty(
- "composable_code_size",
- result.getComposableInstructionCodeSizeResults().getLong(iteration));
- }
- resultObject.addProperty("runtime", result.getRuntimeResults().getLong(iteration));
+ for (int iteration = 0; iteration < result.getCodeSizeResults().size(); iteration++) {
+ JsonObject resultObject = new JsonObject();
+ addPropertyIfValueDifferentFromRepresentative(
+ resultObject,
+ "code_size",
+ iteration,
+ result.getCodeSizeResults(),
+ i -> result.getCodeSizeResults().getLong(i));
+ addPropertyIfValueDifferentFromRepresentative(
+ resultObject,
+ "ins_code_size",
+ iteration,
+ result.getInstructionCodeSizeResults(),
+ i -> result.getInstructionCodeSizeResults().getLong(i));
+ addPropertyIfValueDifferentFromRepresentative(
+ resultObject,
+ "composable_code_size",
+ iteration,
+ result.getComposableInstructionCodeSizeResults(),
+ i -> result.getComposableInstructionCodeSizeResults().getLong(i));
+ for (int section : DexSection.getConstants()) {
+ String sectionName = DexSection.typeName(section);
+ String sectionNameUnderscore =
+ CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, sectionName);
+ addPropertyIfValueDifferentFromRepresentative(
+ resultObject,
+ "dex_" + sectionNameUnderscore + "_size",
+ iteration,
+ result.getDexSegmentsSizeResults(),
+ i -> result.getDexSegmentsSizeResults().get(i).get(section).getSegmentSize());
+ }
+ addPropertyIfValueDifferentFromRepresentative(
+ resultObject,
+ "oat_code_size",
+ iteration,
+ result.getDex2OatSizeResult(),
+ i -> result.getDex2OatSizeResult().getLong(i));
+ addPropertyIfValueDifferentFromRepresentative(
+ resultObject,
+ "runtime",
+ iteration,
+ result.getRuntimeResults(),
+ i -> result.getRuntimeResults().getLong(i));
resultsArray.add(resultObject);
- });
+ }
JsonObject benchmarkObject = new JsonObject();
benchmarkObject.addProperty("benchmark_name", result.getName());
benchmarkObject.add("results", resultsArray);
return benchmarkObject;
}
+
+ private void addPropertyIfValueDifferentFromRepresentative(
+ JsonObject resultObject,
+ String propertyName,
+ int iteration,
+ Collection<?> results,
+ IntToLongFunction getter) {
+ if (results.isEmpty()) {
+ return;
+ }
+ long result = getter.applyAsLong(iteration);
+ if (iteration != 0) {
+ long representativeResult = getter.applyAsLong(0);
+ if (result == representativeResult) {
+ return;
+ }
+ }
+ resultObject.addProperty(propertyName, result);
+ }
}
diff --git a/tools/perf/benchmark_data.json b/tools/perf/benchmark_data.json
index 9d18466..e54df9e 100644
--- a/tools/perf/benchmark_data.json
+++ b/tools/perf/benchmark_data.json
@@ -7,17 +7,48 @@
"NowInAndroidApp": {
"benchmark_name": "NowInAndroidApp",
"results": [
- { "code_size": 42, "runtime": 1 },
- { "code_size": 42, "runtime": 2 },
- { "code_size": 42, "runtime": 3 }
+ {
+ "code_size": 1,
+ "instruction_code_size": 2,
+ "composable_code_size": 3,
+ "oat_code_size": 4,
+ "runtime": 1
+ },
+ {
+ "code_size": 1,
+ "instruction_code_size": 2,
+ "composable_code_size": 3,
+ "oat_code_size": 4,
+ "runtime": 1
+ },
+ {
+ "code_size": 1,
+ "instruction_code_size": 2,
+ "composable_code_size": 3,
+ "oat_code_size": 4,
+ "runtime": 1
+ }
]
},
"TiviApp": {
"benchmark_name": "TiviApp",
"results": [
- { "code_size": 84, "runtime": 4 },
- { "code_size": 84, "runtime": 5 },
- { "code_size": 84, "runtime": 6 }
+ {
+ "code_size": 1,
+ "instruction_code_size": 2,
+ "composable_code_size": 3,
+ "oat_code_size": 4,
+ "runtime": 1
+ },
+ {
+ },
+ {
+ "code_size": 1,
+ "instruction_code_size": 2,
+ "composable_code_size": 3,
+ "oat_code_size": 4,
+ "runtime": 1
+ }
]
}
}
diff --git a/tools/perf/index.html b/tools/perf/index.html
index 73c2931..43e72b8 100644
--- a/tools/perf/index.html
+++ b/tools/perf/index.html
@@ -121,6 +121,29 @@
benchmarkSelectors.appendChild(label);
}
+ function getSingleResult(benchmark, commit, resultName, resultIteration = 0) {
+ if (!(benchmark in commit.benchmarks)) {
+ return NaN;
+ }
+ const allResults = commit.benchmarks[benchmark].results;
+ const resultsForIteration = allResults[resultIteration];
+ // If a given iteration does not declare a result, then the result
+ // was the same as the first run.
+ if (resultIteration > 0 && !(resultName in resultsForIteration)) {
+ return allResults.first()[resultName];
+ }
+ return resultsForIteration[resultName];
+ }
+
+ function getAllResults(benchmark, commit, resultName) {
+ const result = [];
+ const allResults = commit.benchmarks[benchmark].results;
+ for (var iteration = 0; iteration < allResults.length; iteration++) {
+ result.push(getSingleResult(benchmark, commit, resultName, iteration));
+ }
+ return result;
+ }
+
// Chart data provider.
function getData() {
const filteredCommits = commits.slice(zoom.left, zoom.right);
@@ -129,40 +152,28 @@
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 composableCodeSizeData =
+ (c, i) => getSingleResult(selectedBenchmark, filteredCommits[i], "code_size"));
+ const instructionCodeSizeData =
filteredCommits.map(
- (c, i) =>
- selectedBenchmark in filteredCommits[i].benchmarks
- ? filteredCommits[i]
- .benchmarks[selectedBenchmark]
- .results
- .first()
- .composable_code_size
- : NaN);
+ (c, i) => getSingleResult(selectedBenchmark, filteredCommits[i], "instruction_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 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 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);
}
}
}
@@ -170,10 +181,7 @@
filteredCommits.map(
(c, i) =>
selectedBenchmark in filteredCommits[i].benchmarks
- ? filteredCommits[i]
- .benchmarks[selectedBenchmark]
- .results
- .map(result => result.runtime)
+ ? getAllResults(selectedBenchmark, filteredCommits[i], "runtime")
.min()
.ns_to_s()
: NaN);
@@ -182,8 +190,7 @@
if (!(selectedBenchmark in commit.benchmarks)) {
continue;
}
- const runtimes =
- commit.benchmarks[selectedBenchmark].results.map(result => result.runtime)
+ const runtimes = getAllResults(selectedBenchmark, commit, "runtime")
for (const runtime of runtimes.values()) {
runtimeScatterData.push({ x: commit.index, y: runtime.ns_to_s() });
}
@@ -194,7 +201,7 @@
{
benchmark: selectedBenchmark,
type: 'line',
- label: 'Code size',
+ label: 'Dex size',
data: codeSizeData,
datalabels: {
align: 'end',
@@ -216,14 +223,58 @@
{
benchmark: selectedBenchmark,
type: 'line',
+ label: 'Instruction size',
+ data: instructionCodeSizeData,
+ datalabels: {
+ align: 'end',
+ anchor: 'end'
+ },
+ tension: 0.1,
+ yAxisID: 'y_instruction_code_size',
+ segment: {
+ borderColor: ctx =>
+ skipped(
+ ctx,
+ myChart
+ ? myChart.data.datasets[ctx.datasetIndex].backgroundColor
+ : undefined),
+ borderDash: ctx => skipped(ctx, [6, 6]),
+ },
+ spanGaps: true
+ },
+ {
+ benchmark: selectedBenchmark,
+ type: 'line',
label: 'Composable size',
- data: composableCodeSizeData,
+ data: composableInstructionCodeSizeData,
datalabels: {
align: 'start',
anchor: 'start'
},
tension: 0.1,
- yAxisID: 'y_composable_code_size',
+ yAxisID: 'y_instruction_code_size',
+ segment: {
+ borderColor: ctx =>
+ skipped(
+ ctx,
+ myChart
+ ? myChart.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(
@@ -294,7 +345,7 @@
// Legend tracking.
const legends =
- new Set(['Code size', 'Composable size', 'Nondeterminism', 'Runtime', 'Runtime variance']);
+ new Set(['Dex size', 'Instruction size', 'Composable size', 'Oat size', 'Nondeterminism', 'Runtime', 'Runtime variance']);
const selectedLegends =
new Set(
unescape(window.location.hash.substring(1))
@@ -408,7 +459,7 @@
position: 'left',
title: {
display: true,
- text: 'Code size (bytes)'
+ text: 'Dex size (bytes)'
}
},
y_runtime: {
@@ -418,13 +469,20 @@
text: 'Runtime (seconds)'
}
},
- y_composable_code_size: {
+ y_instruction_code_size: {
position: 'left',
title: {
display: true,
- text: 'Composable code size (bytes)'
+ text: 'Instruction size (bytes)'
}
},
+ y_oat_code_size: {
+ position: 'left',
+ title: {
+ display: true,
+ text: 'Oat size (bytes)'
+ }
+ }
}
};
@@ -522,8 +580,10 @@
}
// Update scales.
- options.scales.y.display = selectedLegends.has('Code size');
- options.scales.y_composable_code_size.display = selectedLegends.has('Composable size');
+ options.scales.y.display = selectedLegends.has('Dex size');
+ options.scales.y_instruction_code_size.display =
+ selectedLegends.has('Instruction size') || selectedLegends.has('Composable size');
+ options.scales.y_oat_code_size.display = selectedLegends.has('Oat size');
options.scales.y_runtime.display =
selectedLegends.has('Runtime') || selectedLegends.has('Runtime variance');