Add retrace benchmark
Change-Id: Ie1c7f2cb1df5621486fd8b59fcccc5ad12053941
diff --git a/tools/perf.py b/tools/perf.py
index 409b61f..95f8af6 100755
--- a/tools/perf.py
+++ b/tools/perf.py
@@ -15,11 +15,44 @@
if utils.is_bot():
import upload_benchmark_data_to_google_storage
-BENCHMARKS = [
- 'ChromeApp', 'CraneApp', 'JetLaggedApp', 'JetNewsApp', 'JetCasterApp',
- 'JetChatApp', 'JetSnackApp', 'NowInAndroidApp', 'OwlApp', 'ReplyApp',
- 'TiviApp'
-]
+BENCHMARKS = {
+ 'ChromeApp': {
+ 'targets': ['r8-full']
+ },
+ 'CraneApp': {
+ 'targets': ['r8-full']
+ },
+ 'JetLaggedApp': {
+ 'targets': ['r8-full']
+ },
+ 'JetNewsApp': {
+ 'targets': ['r8-full']
+ },
+ 'JetCasterApp': {
+ 'targets': ['r8-full']
+ },
+ 'JetChatApp': {
+ 'targets': ['r8-full']
+ },
+ 'JetSnackApp': {
+ 'targets': ['r8-full']
+ },
+ 'NowInAndroidApp': {
+ 'targets': ['r8-full']
+ },
+ 'OwlApp': {
+ 'targets': ['r8-full']
+ },
+ 'R8': {
+ 'targets': ['retrace']
+ },
+ 'ReplyApp': {
+ 'targets': ['r8-full']
+ },
+ 'TiviApp': {
+ 'targets': ['r8-full']
+ },
+}
BUCKET = "r8-perf-results"
SAMPLE_BENCHMARK_RESULT_JSON = {
'benchmark_name': '<benchmark_name>',
@@ -54,10 +87,10 @@
help='Skip if output exists.',
action='store_true',
default=False)
- result.add_argument('--target',
- help='Specific target to run on.',
- default='r8-full',
- choices=['d8', 'r8-full', 'r8-force', 'r8-compat'])
+ result.add_argument(
+ '--target',
+ help='Specific target to run on.',
+ choices=['d8', 'r8-full', 'r8-force', 'r8-compat', 'retrace'])
result.add_argument('--verbose',
help='To enable verbose logging.',
action='store_true',
@@ -67,7 +100,7 @@
help='Use R8 hash for the run (default local build)',
default=None)
options, args = result.parse_known_args()
- options.benchmarks = options.benchmark or BENCHMARKS
+ options.benchmarks = options.benchmark or BENCHMARKS.keys()
options.quiet = not options.verbose
del options.benchmark
return options, args
@@ -75,13 +108,14 @@
def Build(options):
utils.Print('Building', quiet=options.quiet)
- build_cmd = GetRunCmd('N/A', options, ['--iterations', '0'])
+ target = options.target or 'r8-full'
+ build_cmd = GetRunCmd('N/A', target, options, ['--iterations', '0'])
subprocess.check_call(build_cmd)
-def GetRunCmd(benchmark, options, args):
+def GetRunCmd(benchmark, target, options, args):
base_cmd = [
- 'tools/run_benchmark.py', '--benchmark', benchmark, '--target', options.target
+ 'tools/run_benchmark.py', '--benchmark', benchmark, '--target', target
]
if options.verbose:
base_cmd.append('--verbose')
@@ -156,61 +190,69 @@
r8jar = compiledump.download_distribution(options.version,
download_options, temp)
for benchmark in options.benchmarks:
- if options.skip_if_output_exists:
- if options.outdir:
- raise NotImplementedError
- output = GetGSLocation(
- GetArtifactLocation(benchmark, options.target, options.version,
- 'result.json'))
- if utils.cloud_storage_exists(output):
- print(f'Skipping run, {output} already exists.')
+ targets = [options.target
+ ] if options.target else BENCHMARKS[benchmark].targets
+ for target in targets:
+ if options.skip_if_output_exists:
+ if options.outdir:
+ raise NotImplementedError
+ output = GetGSLocation(
+ GetArtifactLocation(benchmark, target, options.version,
+ 'result.json'))
+ if utils.cloud_storage_exists(output):
+ print(f'Skipping run, {output} already exists.')
+ continue
+
+ # Run benchmark.
+ benchmark_result_json_files = []
+ failed = False
+ for i in range(options.iterations):
+ utils.Print(
+ f'Benchmarking {benchmark} ({i+1}/{options.iterations})',
+ quiet=options.quiet)
+ benchhmark_result_file = os.path.join(
+ temp, f'result_file_{i}')
+ iteration_cmd = GetRunCmd(benchmark, target, options, [
+ '--iterations',
+ str(options.iterations_inner), '--output',
+ benchhmark_result_file, '--no-build'
+ ])
+ try:
+ subprocess.check_call(iteration_cmd)
+ benchmark_result_json_files.append(
+ benchhmark_result_file)
+ except subprocess.CalledProcessError as e:
+ failed = True
+ any_failed = True
+ break
+
+ if failed:
continue
- # Run benchmark.
- benchmark_result_json_files = []
- failed = False
- for i in range(options.iterations):
- utils.Print(f'Benchmarking {benchmark} ({i+1}/{options.iterations})',
- quiet=options.quiet)
- benchhmark_result_file = os.path.join(temp, f'result_file_{i}')
- iteration_cmd = GetRunCmd(benchmark, options, [
- '--iterations',
- str(options.iterations_inner), '--output',
- benchhmark_result_file, '--no-build'
- ])
- try:
- subprocess.check_call(iteration_cmd)
- benchmark_result_json_files.append(benchhmark_result_file)
- except subprocess.CalledProcessError as e:
- failed = True
- any_failed = True
- break
-
- if failed:
- continue
-
- # Merge results and write output.
- result_file = os.path.join(temp, 'result_file')
- with open(result_file, 'w') as f:
- json.dump(
- MergeBenchmarkResultJsonFiles(benchmark_result_json_files),
- f)
- ArchiveOutputFile(result_file,
- GetArtifactLocation(benchmark, options.target,
- options.version,
- 'result.json'),
- outdir=options.outdir)
-
- # Write metadata.
- if utils.is_bot():
- meta_file = os.path.join(temp, "meta")
- with open(meta_file, 'w') as f:
- f.write("Produced by: " + os.environ.get('SWARMING_BOT_ID'))
- ArchiveOutputFile(meta_file,
- GetArtifactLocation(benchmark, options.target,
- options.version, 'meta'),
+ # Merge results and write output.
+ result_file = os.path.join(temp, 'result_file')
+ with open(result_file, 'w') as f:
+ json.dump(
+ MergeBenchmarkResultJsonFiles(
+ benchmark_result_json_files), f)
+ ArchiveOutputFile(result_file,
+ GetArtifactLocation(benchmark, target,
+ options.version,
+ 'result.json'),
outdir=options.outdir)
+ # Write metadata.
+ if utils.is_bot():
+ meta_file = os.path.join(temp, "meta")
+ with open(meta_file, 'w') as f:
+ f.write("Produced by: " +
+ os.environ.get('SWARMING_BOT_ID'))
+ ArchiveOutputFile(meta_file,
+ GetArtifactLocation(
+ benchmark, target, options.version,
+ 'meta'),
+ outdir=options.outdir)
+
if utils.is_bot():
upload_benchmark_data_to_google_storage.run()
diff --git a/tools/perf/index.html b/tools/perf/r8.html
similarity index 99%
rename from tools/perf/index.html
rename to tools/perf/r8.html
index aa14c1b..857e607 100644
--- a/tools/perf/index.html
+++ b/tools/perf/r8.html
@@ -314,7 +314,7 @@
scales: scales.get()
};
- const commits = await state.importCommits("./benchmark_data.json");
+ const commits = await state.importCommits("./r8_benchmark_data.json");
state.initializeBenchmarks();
state.initializeLegends({
'Dex size': { default: true },
diff --git a/tools/perf/retrace.html b/tools/perf/retrace.html
new file mode 100644
index 0000000..00d5f48
--- /dev/null
+++ b/tools/perf/retrace.html
@@ -0,0 +1,202 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Retrace perf</title>
+ <link rel="stylesheet" href="stylesheet.css">
+</head>
+<body>
+ <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";
+
+ // 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 runtimeData =
+ filteredCommits.map(
+ (c, i) =>
+ selectedBenchmark in filteredCommits[i].benchmarks
+ ? getAllResults(selectedBenchmark, filteredCommits[i], "runtime")
+ .min()
+ .ns_to_s()
+ : NaN);
+ 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 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'
+ }
+ ]);
+ });
+ 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()
+ };
+
+ const commits = await state.importCommits("./retrace_benchmark_data.json");
+ state.initializeBenchmarks();
+ state.initializeLegends({
+ 'Runtime': { default: true },
+ 'Runtime variance': { default: true }
+ });
+ state.initializeZoom();
+ dom.initializeBenchmarkSelectors();
+ dom.initializeChartNavigation();
+ chart.setDataProvider(getData);
+ chart.initializeChart(options);
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/tools/run_benchmark.py b/tools/run_benchmark.py
index 783e695..436c046 100755
--- a/tools/run_benchmark.py
+++ b/tools/run_benchmark.py
@@ -47,7 +47,7 @@
help='The test target to run',
required=True,
# These should 1:1 with benchmarks/BenchmarkTarget.java
- choices=['d8', 'r8-full', 'r8-force', 'r8-compat'])
+ choices=['d8', 'r8-full', 'r8-force', 'r8-compat', 'retrace'])
result.add_argument(
'--debug-agent',
'--debug_agent',
@@ -165,8 +165,11 @@
def run(options, r8jar, testjars):
jdkhome = get_jdk_home(options, options.benchmark)
cmd = [
- jdk.GetJavaExecutable(jdkhome), '-Xms8g', '-Xmx8g',
- '-XX:+TieredCompilation', '-XX:TieredStopAtLevel=4',
+ jdk.GetJavaExecutable(jdkhome),
+ '-Xms8g',
+ '-Xmx8g',
+ '-XX:+TieredCompilation',
+ '-XX:TieredStopAtLevel=4',
'-DBENCHMARK_IGNORE_CODE_SIZE_DIFFERENCES',
f'-DBUILD_PROP_KEEPANNO_RUNTIME_PATH={utils.REPO_ROOT}/d8_r8/keepanno/build/classes/java/main',
# Since we change the working directory to a temp folder.
diff --git a/tools/upload_benchmark_data_to_google_storage.py b/tools/upload_benchmark_data_to_google_storage.py
index f201638..fdb1feb 100755
--- a/tools/upload_benchmark_data_to_google_storage.py
+++ b/tools/upload_benchmark_data_to_google_storage.py
@@ -20,7 +20,8 @@
'chart.js',
'dom.js',
'extensions.js',
- 'index.html',
+ 'r8.html',
+ 'retrace.html',
'scales.js',
'state.js',
'stylesheet.css',
@@ -50,6 +51,53 @@
return None
+def RecordBenchmarkResult(
+ commit, benchmark, benchmark_info, target, benchmarks):
+ if not target in benchmark_info.targets:
+ return
+ filename = perf.GetArtifactLocation(benchmark, target,
+ commit.hash(),
+ 'result.json')
+ benchmark_data = ParseJsonFromCloudStorage(
+ filename, local_bucket)
+ if benchmark_data:
+ benchmarks[benchmark] = benchmark_data
+
+
+def RecordBenchmarkResults(commit, benchmarks, benchmark_data):
+ if benchmarks or benchmark_data:
+ benchmark_data.append({
+ 'author': commit.author_name(),
+ 'hash': commit.hash(),
+ 'submitted': commit.committer_timestamp(),
+ 'title': commit.title(),
+ 'benchmarks': benchmarks
+ })
+
+
+def TrimBenchmarkResults(benchmark_data):
+ new_benchmark_data_len = len(benchmark_data)
+ while new_benchmark_data_len > 0:
+ candidate_len = new_benchmark_data_len - 1
+ if not benchmark_data[candidate_len]['benchmarks']:
+ new_benchmark_data_len = candidate_len
+ else:
+ break
+ return benchmark_data[0:new_benchmark_data_len]
+
+
+def ArchiveBenchmarkResults(benchmark_data, dest):
+ # Serialize JSON to temp file.
+ benchmark_data_file = os.path.join(temp, dest)
+ with open(benchmark_data_file, 'w') as f:
+ json.dump(benchmark_data, f)
+
+ # Write output files to public bucket.
+ perf.ArchiveOutputFile(benchmark_data_file,
+ dest,
+ header='Cache-Control:no-store')
+
+
def run():
# Get the N most recent commits sorted by newest first.
top = utils.get_sha1_from_revision('origin/main')
@@ -62,54 +110,38 @@
local_bucket = os.path.join(temp, perf.BUCKET)
DownloadCloudBucket(local_bucket)
- # Aggregate all the result.json files into a single benchmark_data.json file
- # that has the same format as tools/perf/benchmark_data.json.
- benchmark_data = []
+ # Aggregate all the result.json files into a single file that has the
+ # same format as tools/perf/benchmark_data.json.
+ r8_benchmark_data = []
+ retrace_benchmark_data = []
for commit in commits:
- benchmarks = {}
- for benchmark in BENCHMARKS:
- for target in TARGETS:
- filename = perf.GetArtifactLocation(benchmark, target,
- commit.hash(),
- 'result.json')
- single_benchmark_data = ParseJsonFromCloudStorage(
- filename, local_bucket)
- if single_benchmark_data:
- benchmarks[benchmark] = single_benchmark_data
- if benchmarks or benchmark_data:
- benchmark_data.append({
- 'author': commit.author_name(),
- 'hash': commit.hash(),
- 'submitted': commit.committer_timestamp(),
- 'title': commit.title(),
- 'benchmarks': benchmarks
- })
+ r8_benchmarks = {}
+ retrace_benchmarks = {}
+ for benchmark, benchmark_info in BENCHMARKS.items():
+ RecordBenchmarkResult(
+ commit, benchmark, benchmark_info, 'r8-full', r8_benchmarks)
+ RecordBenchmarkResult(
+ commit, benchmark, benchmark_info, 'retrace',
+ retrace_benchmarks)
+ RecordBenchmarkResults(commmit, r8_benchmarks, r8_benchmark_data)
+ RecordBenchmarkResults(
+ commmit, retrace_benchmarks, retrace_benchmark_data)
# Trim data.
- new_benchmark_data_len = len(benchmark_data)
- while new_benchmark_data_len > 0:
- candidate_len = new_benchmark_data_len - 1
- if not benchmark_data[candidate_len]['benchmarks']:
- new_benchmark_data_len = candidate_len
- else:
- break
- benchmark_data = benchmark_data[0:new_benchmark_data_len]
-
- # Serialize JSON to temp file.
- benchmark_data_file = os.path.join(temp, 'benchmark_data.json')
- with open(benchmark_data_file, 'w') as f:
- json.dump(benchmark_data, f)
+ r8_benchmark_data = TrimBenchmarkResults(r8_benchmark_data)
+ retrace_benchmark_data = TrimBenchmarkResults(retrace_benchmark_data)
# Write output files to public bucket.
- perf.ArchiveOutputFile(benchmark_data_file,
- 'benchmark_data.json',
- header='Cache-Control:no-store')
+ ArchiveBenchmarkResults(r8_benchmark_data, 'r8_benchmark_data.json')
+ ArchiveBenchmarkResults(
+ retrace_benchmark_data, 'retrace_benchmark_data.json'
+
+ # Write remaining files to public bucket.
for file in FILES:
dest = os.path.join(utils.TOOLS_DIR, 'perf', file)
perf.ArchiveOutputFile(dest, file)
-
def main():
run()