|  | #!/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 historic_run | 
|  | import perf | 
|  | import utils | 
|  |  | 
|  | import argparse | 
|  | import json | 
|  | import os | 
|  | import sys | 
|  |  | 
|  | TARGETS = ['r8-full'] | 
|  | NUM_COMMITS = 1000 | 
|  |  | 
|  | FILES = [ | 
|  | 'chart.js', 'd8.html', 'dom.js', 'extensions.js', 'r8.html', 'retrace.html', | 
|  | 'scales.js', 'state.js', 'stylesheet.css', 'url.js', 'utils.js' | 
|  | ] | 
|  |  | 
|  |  | 
|  | def DownloadCloudBucket(dest): | 
|  | os.makedirs(dest) | 
|  | utils.download_file_from_cloud_storage(perf.GetGSLocation('*'), | 
|  | dest, | 
|  | concurrent=True, | 
|  | flags=['-R']) | 
|  |  | 
|  |  | 
|  | def ParseJsonFromCloudStorage(filename, local_bucket): | 
|  | abs_path = os.path.join(local_bucket, filename) | 
|  | if not os.path.exists(abs_path): | 
|  | return None | 
|  | with open(abs_path, 'r') as f: | 
|  | lines = f.readlines() | 
|  | content = ''.join(lines) | 
|  | try: | 
|  | return json.loads(content) | 
|  | except: | 
|  | return None | 
|  |  | 
|  |  | 
|  | def RecordBenchmarkResult(commit, benchmark, benchmark_info, local_bucket, | 
|  | target, benchmarks): | 
|  | if not target in benchmark_info['targets']: | 
|  | return | 
|  | sub_benchmarks = benchmark_info.get('subBenchmarks', {}) | 
|  | sub_benchmarks_for_target = sub_benchmarks.get(target, []) | 
|  | if sub_benchmarks_for_target: | 
|  | for sub_benchmark in sub_benchmarks_for_target: | 
|  | RecordSingleBenchmarkResult(commit, benchmark + sub_benchmark, | 
|  | local_bucket, target, benchmarks) | 
|  | else: | 
|  | RecordSingleBenchmarkResult(commit, benchmark, local_bucket, target, | 
|  | benchmarks) | 
|  |  | 
|  |  | 
|  | def RecordSingleBenchmarkResult(commit, benchmark, local_bucket, target, | 
|  | benchmarks): | 
|  | 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, outdir, temp): | 
|  | # 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', | 
|  | outdir=outdir) | 
|  |  | 
|  |  | 
|  | def run_bucket(): | 
|  | # Get the N most recent commits sorted by newest first. | 
|  | top = utils.get_sha1_from_revision('origin/main') | 
|  | bottom = utils.get_nth_sha1_from_revision(NUM_COMMITS - 1, 'origin/main') | 
|  | commits = historic_run.enumerate_git_commits(top, bottom) | 
|  | assert len(commits) == NUM_COMMITS | 
|  |  | 
|  | # Download all benchmark data from the cloud bucket to a temp folder. | 
|  | with utils.TempDir() as temp: | 
|  | local_bucket = os.path.join(temp, perf.BUCKET) | 
|  | DownloadCloudBucket(local_bucket) | 
|  | run(commits, local_bucket, temp) | 
|  |  | 
|  |  | 
|  | def run_local(local_bucket): | 
|  | commit_hashes = set() | 
|  | for benchmark in os.listdir(local_bucket): | 
|  | benchmark_dir = os.path.join(local_bucket, benchmark) | 
|  | if not os.path.isdir(benchmark_dir): | 
|  | continue | 
|  | for target in os.listdir(benchmark_dir): | 
|  | target_dir = os.path.join(local_bucket, benchmark, target) | 
|  | if not os.path.isdir(target_dir): | 
|  | continue | 
|  | for commit_hash in os.listdir(target_dir): | 
|  | commit_hash_dir = os.path.join(local_bucket, benchmark, target, | 
|  | commit_hash) | 
|  | if not os.path.isdir(commit_hash_dir): | 
|  | continue | 
|  | commit_hashes.add(commit_hash) | 
|  | commits = [] | 
|  | for commit_hash in commit_hashes: | 
|  | commits.append(historic_run.git_commit_from_hash(commit_hash)) | 
|  | commits.sort(key=lambda c: c.committer_timestamp(), reverse=True) | 
|  | with utils.TempDir() as temp: | 
|  | outdir = os.path.join(utils.TOOLS_DIR, 'perf') | 
|  | run(commits, local_bucket, temp, outdir=outdir) | 
|  |  | 
|  |  | 
|  | def run(commits, local_bucket, temp, outdir=None): | 
|  | # Aggregate all the result.json files into a single file that has the | 
|  | # same format as tools/perf/benchmark_data.json. | 
|  | d8_benchmark_data = [] | 
|  | r8_benchmark_data = [] | 
|  | retrace_benchmark_data = [] | 
|  | for commit in commits: | 
|  | d8_benchmarks = {} | 
|  | r8_benchmarks = {} | 
|  | retrace_benchmarks = {} | 
|  | for benchmark, benchmark_info in perf.ALL_BENCHMARKS.items(): | 
|  | RecordBenchmarkResult(commit, benchmark, benchmark_info, | 
|  | local_bucket, 'd8', d8_benchmarks) | 
|  | RecordBenchmarkResult(commit, benchmark, benchmark_info, | 
|  | local_bucket, 'r8-full', r8_benchmarks) | 
|  | RecordBenchmarkResult(commit, benchmark, benchmark_info, | 
|  | local_bucket, 'retrace', retrace_benchmarks) | 
|  | RecordBenchmarkResults(commit, d8_benchmarks, d8_benchmark_data) | 
|  | RecordBenchmarkResults(commit, r8_benchmarks, r8_benchmark_data) | 
|  | RecordBenchmarkResults(commit, retrace_benchmarks, | 
|  | retrace_benchmark_data) | 
|  |  | 
|  | # Trim data. | 
|  | d8_benchmark_data = TrimBenchmarkResults(d8_benchmark_data) | 
|  | r8_benchmark_data = TrimBenchmarkResults(r8_benchmark_data) | 
|  | retrace_benchmark_data = TrimBenchmarkResults(retrace_benchmark_data) | 
|  |  | 
|  | # Write output JSON files to public bucket, or to tools/perf/ if running | 
|  | # with --local-bucket. | 
|  | ArchiveBenchmarkResults(d8_benchmark_data, 'd8_benchmark_data.json', outdir, | 
|  | temp) | 
|  | ArchiveBenchmarkResults(r8_benchmark_data, 'r8_benchmark_data.json', outdir, | 
|  | temp) | 
|  | ArchiveBenchmarkResults(retrace_benchmark_data, | 
|  | 'retrace_benchmark_data.json', outdir, temp) | 
|  |  | 
|  | # Write remaining files to public bucket. | 
|  | if outdir is None: | 
|  | for file in FILES: | 
|  | dest = os.path.join(utils.TOOLS_DIR, 'perf', file) | 
|  | perf.ArchiveOutputFile(dest, file) | 
|  |  | 
|  |  | 
|  | def ParseOptions(): | 
|  | result = argparse.ArgumentParser() | 
|  | result.add_argument('--local-bucket', help='Local results dir.') | 
|  | return result.parse_known_args() | 
|  |  | 
|  |  | 
|  | def main(): | 
|  | options, args = ParseOptions() | 
|  | if options.local_bucket: | 
|  | run_local(options.local_bucket) | 
|  | else: | 
|  | run_bucket() | 
|  |  | 
|  |  | 
|  | if __name__ == '__main__': | 
|  | sys.exit(main()) |