blob: 79e594ea6aa7b54884ae3cefd49d19623f7c66fc [file] [log] [blame]
Christoffer Adamsen30283712024-06-12 13:08:14 +02001#!/usr/bin/env python3
2# Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
3# for details. All rights reserved. Use of this source code is governed by a
4# BSD-style license that can be found in the LICENSE file.
5
6import historic_run
Christoffer Adamsen30283712024-06-12 13:08:14 +02007import perf
8import utils
9
Christoffer Adamsen71711a32024-09-16 14:24:45 +020010import argparse
11import json
12import os
Christoffer Adamsen96e6e7e2024-12-18 12:30:10 +010013import re
14import subprocess
Christoffer Adamsen30283712024-06-12 13:08:14 +020015import sys
16
Christoffer Adamsen30283712024-06-12 13:08:14 +020017TARGETS = ['r8-full']
Christoffer Adamsen987b6372024-06-17 09:34:11 +020018NUM_COMMITS = 1000
Christoffer Adamsen30283712024-06-12 13:08:14 +020019
Christoffer Adamsenf05505b2024-09-03 13:48:14 +020020FILES = [
Christoffer Adamsen58f03d32024-09-04 09:08:14 +020021 'chart.js', 'd8.html', 'dom.js', 'extensions.js', 'r8.html', 'retrace.html',
Christoffer Adamsenf19a9902024-12-18 14:21:59 +010022 'scales.js', 'state.js', 'stylesheet.css', 'tooltip.js', 'url.js',
23 'utils.js'
Christoffer Adamsenf05505b2024-09-03 13:48:14 +020024]
Christoffer Adamsenb43ff592024-06-12 18:49:01 +020025
Christoffer Adamsen30283712024-06-12 13:08:14 +020026
Christoffer Adamsenc09ba6f2024-06-12 18:48:36 +020027def DownloadCloudBucket(dest):
28 os.makedirs(dest)
29 utils.download_file_from_cloud_storage(perf.GetGSLocation('*'),
30 dest,
31 concurrent=True,
32 flags=['-R'])
33
34
Christoffer Adamsen96e6e7e2024-12-18 12:30:10 +010035def GetMainCommits():
36 top = utils.get_sha1_from_revision('origin/main')
37 bottom = utils.get_nth_sha1_from_revision(NUM_COMMITS - 1, 'origin/main')
38 commits = historic_run.enumerate_git_commits(top, bottom)
39 assert len(commits) == NUM_COMMITS
40 return commits
41
42
43def GetReleaseBranches():
44 remote_branches = subprocess.check_output(
45 ['git', 'branch', '-r']).decode('utf-8').strip().splitlines()
46 result = []
47 for remote_branch in remote_branches:
48 remote_branch = remote_branch.strip()
49
50 # Strip 'origin/'.
51 try:
52 remote_name_end_index = remote_branch.index('/')
53 remote_branch = remote_branch[remote_name_end_index + 1:]
54 except ValueError:
55 pass
56
57 # Filter out branches that are not on the form X.Y
58 if not re.search('^(0|[1-9]\d*)\.(0|[1-9]\d*)$', remote_branch):
59 continue
60
61 # Filter out branches prior to 8.9.
62 dot_index = remote_branch.index('.')
63 [major, minor] = remote_branch.split('.')
64 if int(major) < 8 or (major == '8' and int(minor) < 9):
65 continue
66
67 result.append(remote_branch)
68 return result
69
70
71def GetReleaseCommits():
72 release_commits = []
73 for branch in GetReleaseBranches():
74 (major, minor) = branch.split('.')
75 candidate_commits = subprocess.check_output([
76 'git', 'log', '--grep=-dev', '--max-count=100',
77 '--pretty=format:%H %s', 'origin/' + branch, '--',
78 'src/main/java/com/android/tools/r8/Version.java'
79 ]).decode('utf-8').strip().splitlines()
80 for candidate_commit in candidate_commits:
81 separator_index = candidate_commit.index(' ')
82 git_hash = candidate_commit[:separator_index]
83 git_title = candidate_commit[separator_index + 1:]
84 if not re.search(
85 '^Version %s\.%s\.(0|[1-9]\d*)-dev$' %
86 (major, minor), git_title):
87 continue
88 release_commits.append(historic_run.git_commit_from_hash(git_hash))
89 return release_commits
90
91
Christoffer Adamseneeb8d322024-12-18 18:45:06 +010092def ParseJsonFromCloudStorage(filename, local_bucket_dict):
93 if not filename in local_bucket_dict:
Christoffer Adamsen30283712024-06-12 13:08:14 +020094 return None
Christoffer Adamseneeb8d322024-12-18 18:45:06 +010095 return json.loads(local_bucket_dict[filename])
Christoffer Adamsen30283712024-06-12 13:08:14 +020096
97
Christoffer Adamseneeb8d322024-12-18 18:45:06 +010098def RecordBenchmarkResult(commit, benchmark, benchmark_info, local_bucket_dict,
Christoffer Adamsenf0d74a12024-09-03 18:41:36 +020099 target, benchmarks):
100 if not target in benchmark_info['targets']:
Christoffer Adamsen3146d502024-09-03 15:01:49 +0200101 return
Christoffer Adamsenc12e43b2024-09-06 10:15:51 +0200102 sub_benchmarks = benchmark_info.get('subBenchmarks', {})
103 sub_benchmarks_for_target = sub_benchmarks.get(target, [])
104 if sub_benchmarks_for_target:
105 for sub_benchmark in sub_benchmarks_for_target:
106 RecordSingleBenchmarkResult(commit, benchmark + sub_benchmark,
Christoffer Adamseneeb8d322024-12-18 18:45:06 +0100107 local_bucket_dict, target, benchmarks)
Christoffer Adamsenc12e43b2024-09-06 10:15:51 +0200108 else:
Christoffer Adamseneeb8d322024-12-18 18:45:06 +0100109 RecordSingleBenchmarkResult(commit, benchmark, local_bucket_dict,
110 target, benchmarks)
Christoffer Adamsenc12e43b2024-09-06 10:15:51 +0200111
112
Christoffer Adamseneeb8d322024-12-18 18:45:06 +0100113def RecordSingleBenchmarkResult(commit, benchmark, local_bucket_dict, target,
Christoffer Adamsenc12e43b2024-09-06 10:15:51 +0200114 benchmarks):
Christoffer Adamsen96e6e7e2024-12-18 12:30:10 +0100115 filename = perf.GetArtifactLocation(benchmark,
116 target,
117 commit.hash(),
118 'result.json',
119 branch=commit.branch())
Christoffer Adamseneeb8d322024-12-18 18:45:06 +0100120 benchmark_data = ParseJsonFromCloudStorage(filename, local_bucket_dict)
Christoffer Adamsen3146d502024-09-03 15:01:49 +0200121 if benchmark_data:
122 benchmarks[benchmark] = benchmark_data
123
124
125def RecordBenchmarkResults(commit, benchmarks, benchmark_data):
126 if benchmarks or benchmark_data:
Christoffer Adamsen3396d812024-12-18 14:50:15 +0100127 data = {
Christoffer Adamsen3146d502024-09-03 15:01:49 +0200128 'author': commit.author_name(),
129 'hash': commit.hash(),
130 'submitted': commit.committer_timestamp(),
131 'title': commit.title(),
132 'benchmarks': benchmarks
Christoffer Adamsen3396d812024-12-18 14:50:15 +0100133 }
134 version = commit.version()
135 if version:
136 data['version'] = version
137 benchmark_data.append(data)
Christoffer Adamsen3146d502024-09-03 15:01:49 +0200138
139
140def TrimBenchmarkResults(benchmark_data):
141 new_benchmark_data_len = len(benchmark_data)
142 while new_benchmark_data_len > 0:
143 candidate_len = new_benchmark_data_len - 1
144 if not benchmark_data[candidate_len]['benchmarks']:
145 new_benchmark_data_len = candidate_len
146 else:
147 break
148 return benchmark_data[0:new_benchmark_data_len]
149
150
Christoffer Adamsen71711a32024-09-16 14:24:45 +0200151def ArchiveBenchmarkResults(benchmark_data, dest, outdir, temp):
Christoffer Adamsen3146d502024-09-03 15:01:49 +0200152 # Serialize JSON to temp file.
153 benchmark_data_file = os.path.join(temp, dest)
154 with open(benchmark_data_file, 'w') as f:
155 json.dump(benchmark_data, f)
156
157 # Write output files to public bucket.
158 perf.ArchiveOutputFile(benchmark_data_file,
159 dest,
Christoffer Adamsen71711a32024-09-16 14:24:45 +0200160 header='Cache-Control:no-store',
161 outdir=outdir)
Christoffer Adamsen3146d502024-09-03 15:01:49 +0200162
163
Christoffer Adamsen71711a32024-09-16 14:24:45 +0200164def run_bucket():
Christoffer Adamsen30283712024-06-12 13:08:14 +0200165 # Get the N most recent commits sorted by newest first.
Christoffer Adamsen96e6e7e2024-12-18 12:30:10 +0100166 main_commits = GetMainCommits()
167
168 # Get all release commits from 8.9 and onwards.
169 release_commits = GetReleaseCommits()
Christoffer Adamsen30283712024-06-12 13:08:14 +0200170
Christoffer Adamsenc09ba6f2024-06-12 18:48:36 +0200171 # Download all benchmark data from the cloud bucket to a temp folder.
Christoffer Adamsen30283712024-06-12 13:08:14 +0200172 with utils.TempDir() as temp:
Christoffer Adamsenc09ba6f2024-06-12 18:48:36 +0200173 local_bucket = os.path.join(temp, perf.BUCKET)
174 DownloadCloudBucket(local_bucket)
Christoffer Adamsen96e6e7e2024-12-18 12:30:10 +0100175 run(main_commits + release_commits, local_bucket, temp)
Christoffer Adamsenc09ba6f2024-06-12 18:48:36 +0200176
Christoffer Adamsenc09ba6f2024-06-12 18:48:36 +0200177
Christoffer Adamsen71711a32024-09-16 14:24:45 +0200178def run_local(local_bucket):
179 commit_hashes = set()
180 for benchmark in os.listdir(local_bucket):
181 benchmark_dir = os.path.join(local_bucket, benchmark)
182 if not os.path.isdir(benchmark_dir):
183 continue
184 for target in os.listdir(benchmark_dir):
185 target_dir = os.path.join(local_bucket, benchmark, target)
186 if not os.path.isdir(target_dir):
187 continue
188 for commit_hash in os.listdir(target_dir):
189 commit_hash_dir = os.path.join(local_bucket, benchmark, target,
190 commit_hash)
191 if not os.path.isdir(commit_hash_dir):
192 continue
193 commit_hashes.add(commit_hash)
194 commits = []
195 for commit_hash in commit_hashes:
196 commits.append(historic_run.git_commit_from_hash(commit_hash))
197 commits.sort(key=lambda c: c.committer_timestamp(), reverse=True)
198 with utils.TempDir() as temp:
199 outdir = os.path.join(utils.TOOLS_DIR, 'perf')
200 run(commits, local_bucket, temp, outdir=outdir)
Christoffer Adamsenb43ff592024-06-12 18:49:01 +0200201
Christoffer Adamsen3146d502024-09-03 15:01:49 +0200202
Christoffer Adamsen71711a32024-09-16 14:24:45 +0200203def run(commits, local_bucket, temp, outdir=None):
Christoffer Adamseneeb8d322024-12-18 18:45:06 +0100204 print('Loading bucket into memory')
205 local_bucket_dict = {}
206 for (root, dirs, files) in os.walk(local_bucket):
207 for file in files:
208 if file != 'result.json':
209 continue
210 abs_path = os.path.join(root, file)
211 rel_path = os.path.relpath(abs_path, local_bucket)
212 with open(abs_path, 'r') as f:
213 local_bucket_dict[rel_path] = f.read()
214
Christoffer Adamsen71711a32024-09-16 14:24:45 +0200215 # Aggregate all the result.json files into a single file that has the
216 # same format as tools/perf/benchmark_data.json.
Christoffer Adamseneeb8d322024-12-18 18:45:06 +0100217 print('Processing commits')
Christoffer Adamsen71711a32024-09-16 14:24:45 +0200218 d8_benchmark_data = []
219 r8_benchmark_data = []
220 retrace_benchmark_data = []
221 for commit in commits:
222 d8_benchmarks = {}
223 r8_benchmarks = {}
224 retrace_benchmarks = {}
225 for benchmark, benchmark_info in perf.ALL_BENCHMARKS.items():
226 RecordBenchmarkResult(commit, benchmark, benchmark_info,
Christoffer Adamseneeb8d322024-12-18 18:45:06 +0100227 local_bucket_dict, 'd8', d8_benchmarks)
Christoffer Adamsen71711a32024-09-16 14:24:45 +0200228 RecordBenchmarkResult(commit, benchmark, benchmark_info,
Christoffer Adamseneeb8d322024-12-18 18:45:06 +0100229 local_bucket_dict, 'r8-full', r8_benchmarks)
Christoffer Adamsen71711a32024-09-16 14:24:45 +0200230 RecordBenchmarkResult(commit, benchmark, benchmark_info,
Christoffer Adamseneeb8d322024-12-18 18:45:06 +0100231 local_bucket_dict, 'retrace',
232 retrace_benchmarks)
Christoffer Adamsen71711a32024-09-16 14:24:45 +0200233 RecordBenchmarkResults(commit, d8_benchmarks, d8_benchmark_data)
234 RecordBenchmarkResults(commit, r8_benchmarks, r8_benchmark_data)
235 RecordBenchmarkResults(commit, retrace_benchmarks,
236 retrace_benchmark_data)
237
238 # Trim data.
Christoffer Adamseneeb8d322024-12-18 18:45:06 +0100239 print('Trimming data')
Christoffer Adamsen71711a32024-09-16 14:24:45 +0200240 d8_benchmark_data = TrimBenchmarkResults(d8_benchmark_data)
241 r8_benchmark_data = TrimBenchmarkResults(r8_benchmark_data)
242 retrace_benchmark_data = TrimBenchmarkResults(retrace_benchmark_data)
243
244 # Write output JSON files to public bucket, or to tools/perf/ if running
245 # with --local-bucket.
Christoffer Adamseneeb8d322024-12-18 18:45:06 +0100246 print('Writing JSON')
Christoffer Adamsen71711a32024-09-16 14:24:45 +0200247 ArchiveBenchmarkResults(d8_benchmark_data, 'd8_benchmark_data.json', outdir,
248 temp)
249 ArchiveBenchmarkResults(r8_benchmark_data, 'r8_benchmark_data.json', outdir,
250 temp)
251 ArchiveBenchmarkResults(retrace_benchmark_data,
252 'retrace_benchmark_data.json', outdir, temp)
253
254 # Write remaining files to public bucket.
Christoffer Adamseneeb8d322024-12-18 18:45:06 +0100255 print('Writing static files')
Christoffer Adamsen71711a32024-09-16 14:24:45 +0200256 if outdir is None:
Christoffer Adamsenf05505b2024-09-03 13:48:14 +0200257 for file in FILES:
258 dest = os.path.join(utils.TOOLS_DIR, 'perf', file)
259 perf.ArchiveOutputFile(dest, file)
260
Christoffer Adamsen30283712024-06-12 13:08:14 +0200261
Christoffer Adamsen71711a32024-09-16 14:24:45 +0200262def ParseOptions():
263 result = argparse.ArgumentParser()
264 result.add_argument('--local-bucket', help='Local results dir.')
265 return result.parse_known_args()
266
267
Christoffer Adamsen19d093d2024-06-13 14:20:52 +0200268def main():
Christoffer Adamsen71711a32024-09-16 14:24:45 +0200269 options, args = ParseOptions()
270 if options.local_bucket:
271 run_local(options.local_bucket)
272 else:
273 run_bucket()
Christoffer Adamsen19d093d2024-06-13 14:20:52 +0200274
275
Christoffer Adamsen30283712024-06-12 13:08:14 +0200276if __name__ == '__main__':
277 sys.exit(main())