| #!/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 json |
| import os |
| import shutil |
| import subprocess |
| import sys |
| |
| import utils |
| |
| 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/results |
| # 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 ['NowInAndroidApp', 'TiviApp'] |
| options.quiet = not options.verbose |
| del options.app |
| return options, 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): |
| return f'gs://{BUCKET}/{filename}' |
| |
| |
| def ArchiveOutputFile(file, dest, 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)) |
| |
| |
| # 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() |
| with utils.TempDir() as 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 |
| |
| 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, '--nolib']) |
| |
| # Build |
| utils.Print(f'Preparing {app}', quiet=options.quiet) |
| build_cmd = base_cmd + ['--iterations', '0'] |
| subprocess.check_output(build_cmd) |
| |
| # Run benchmark. |
| benchmark_result_json_files = [] |
| 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 = base_cmd + [ |
| '--iterations', |
| str(options.iterations_inner), '--output', |
| benchhmark_result_file, '--no-build' |
| ] |
| subprocess.check_output(iteration_cmd) |
| benchmark_result_json_files.append(benchhmark_result_file) |
| |
| # 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'), options.outdir) |
| |
| # Write metadata. |
| if os.environ.get('SWARMING_BOT_ID'): |
| 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'), options.outdir) |
| |
| |
| if __name__ == '__main__': |
| sys.exit(main()) |