blob: 5d3cb2bcccd0235d1823266753df0f3ac9f82cac [file] [log] [blame]
#!/usr/bin/env python3
# Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
# for details. All rights reserved. Use of this source code is governed by a
# BSD-style license that can be found in the LICENSE file.
import argparse
import compiledump
import json
import os
import shutil
import subprocess
import sys
import utils
if utils.is_bot():
import upload_benchmark_data_to_google_storage
APPS = [
'ChromeApp', 'CraneApp', 'JetLaggedApp', 'JetNewsApp', 'JetCasterApp',
'JetChatApp', 'JetSnackApp', 'NowInAndroidApp', 'OwlApp', 'ReplyApp',
'TiviApp'
]
BUCKET = "r8-perf-results"
SAMPLE_BENCHMARK_RESULT_JSON = {
'benchmark_name': '<benchmark_name>',
'results': [{
'code_size': 0,
'runtime': 0
}]
}
# Result structure on cloud storage
# gs://bucket/benchmark_results/APP/TARGET/GIT_HASH/result.json
# meta
# where results simply contains the result lines and
# meta contains information about the execution (machine)
def ParseOptions():
result = argparse.ArgumentParser()
result.add_argument('--app',
help='Specific app(s) to measure.',
action='append')
result.add_argument('--iterations',
help='How many times run_benchmark is run.',
type=int,
default=1)
result.add_argument('--iterations-inner',
help='How many iterations to run inside run_benchmark.',
type=int,
default=10)
result.add_argument('--outdir',
help='Output directory for running locally.')
result.add_argument('--skip-if-output-exists',
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('--verbose',
help='To enable verbose logging.',
action='store_true',
default=False)
result.add_argument('--version',
'-v',
help='Use R8 hash for the run (default local build)',
default=None)
options, args = result.parse_known_args()
options.apps = options.app or APPS
options.quiet = not options.verbose
del options.app
return options, args
def Build(options):
utils.Print('Building', quiet=options.quiet)
build_cmd = GetRunCmd('N/A', options, ['--iterations', '0'])
subprocess.check_call(build_cmd)
def GetRunCmd(app, options, args):
base_cmd = [
'tools/run_benchmark.py', '--benchmark', app, '--target', options.target
]
if options.verbose:
base_cmd.append('--verbose')
if options.version:
base_cmd.extend(
['--version', options.version, '--version-jar', r8jar, '--nolib'])
return base_cmd + args
def MergeBenchmarkResultJsonFiles(benchmark_result_json_files):
merged_benchmark_result_json = None
for benchmark_result_json_file in benchmark_result_json_files:
benchmark_result_json = ParseBenchmarkResultJsonFile(
benchmark_result_json_file)
if merged_benchmark_result_json is None:
merged_benchmark_result_json = benchmark_result_json
else:
MergeBenchmarkResultJsonFile(merged_benchmark_result_json,
benchmark_result_json)
return merged_benchmark_result_json
def MergeBenchmarkResultJsonFile(merged_benchmark_result_json,
benchmark_result_json):
assert benchmark_result_json.keys() == SAMPLE_BENCHMARK_RESULT_JSON.keys()
assert merged_benchmark_result_json[
'benchmark_name'] == benchmark_result_json['benchmark_name']
merged_benchmark_result_json['results'].extend(
benchmark_result_json['results'])
def ParseBenchmarkResultJsonFile(result_json_file):
with open(result_json_file, 'r') as f:
lines = f.readlines()
return json.loads(''.join(lines))
def GetArtifactLocation(app, target, version, filename):
version_or_head = version or utils.get_HEAD_sha1()
return f'{app}/{target}/{version_or_head}/{filename}'
def GetGSLocation(filename, bucket=BUCKET):
return f'gs://{bucket}/{filename}'
def ArchiveOutputFile(file, dest, bucket=BUCKET, header=None, outdir=None):
if outdir:
dest_in_outdir = os.path.join(outdir, dest)
os.makedirs(os.path.dirname(dest_in_outdir), exist_ok=True)
shutil.copyfile(file, dest_in_outdir)
else:
utils.upload_file_to_cloud_storage(file,
GetGSLocation(dest, bucket=bucket),
header=header)
# Usage with historic_run.py:
# ./tools/historic_run.py
# --cmd "perf.py --skip-if-output-exists --version"
# --timeout -1
# --top 3373fd18453835bf49bff9f02523a507a2ebf317
# --bottom 7486f01e0622cb5935b77a92b59ddf1ca8dbd2e2
def main():
options, args = ParseOptions()
Build(options)
any_failed = False
with utils.TempDir() as temp:
if options.version:
# Download r8.jar once instead of once per run_benchmark.py invocation.
download_options = argparse.Namespace(no_build=True, nolib=True)
r8jar = compiledump.download_distribution(options.version,
download_options, temp)
for app in options.apps:
if options.skip_if_output_exists:
if options.outdir:
raise NotImplementedError
output = GetGSLocation(
GetArtifactLocation(app, options.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 {app} ({i+1}/{options.iterations})',
quiet=options.quiet)
benchhmark_result_file = os.path.join(temp, f'result_file_{i}')
iteration_cmd = GetRunCmd(app, 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(app, 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(app, options.target,
options.version, 'meta'),
outdir=options.outdir)
if utils.is_bot():
upload_benchmark_data_to_google_storage.run()
if any_failed:
return 1
if __name__ == '__main__':
sys.exit(main())