Skip to content

Commit fa890b9

Browse files
committed
Automate bisecting benchmarks
1 parent 2769f60 commit fa890b9

5 files changed

Lines changed: 698 additions & 7 deletions

File tree

.agents/skills/rota-bench-regression-analysis/SKILL.md

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,14 @@ scripts/compare_bench_regressions.py --rota --json-out /tmp/compare_bench_regres
1818
3. Focus on `plausible` regressions. Ignore `flaky` and `inconclusive` items unless they help explain a plausible shift.
1919
4. Split the summary into:
2020
- `Attributed`
21-
- `Unattributed`
21+
- `To bisect`
22+
- `To watch`
23+
5. Show the current summary
24+
6. Execute the bisect script for each "to bisect" entry in parallel, then wait for all of them to finish.
25+
The builds can take many hours without the script showing any output, make sure you wait for them with a long timeout.
26+
If running in codex: round-robin poll the processes with `write_stdin` and 1 hour timeout (the configuration might cap this at a lower timeout in practice)
27+
7. Collect the bisect results and move any benchmarks that were attributed by the bisections.
28+
8. Show the final summary. Note any failed bisects.
2229

2330
## Useful JSON Queries
2431
```bash
@@ -67,10 +74,16 @@ git diff --stat GOOD..BAD
6774

6875
## Output Contract
6976
- List findings first, not process notes.
70-
- Keep two top-level sections: `Attributed` and `Unattributed`.
77+
- Keep three top-level sections (if not empty): `Attributed` and `To bisect` and `To watch`
7178
- In the attributed section, use this header format: `abcd1234efgh | author@oracle.com | Full subject`
72-
- In the unattributed section, say whether the item looks real, flaky, or likely the same cause as another attributed item.
79+
- Unattributed changes that look plausible go to "to bisect", flaky ones go to "to watch"
80+
- In the "to bisect" section, add an invocation (don't execute yet) of `scripts/bisect_benchmark_regression.py` that can bisect it (use unabbreviated commits in this case)
81+
- In the "to watch" section, say whether the item looks flaky, or likely the same cause as another attributed item.
7382
- Do not abbreviate commit subjects.
7483
- Keep author emails.
7584
- Abbreviate commit IDs to 12 characters.
76-
- Do not list every benchmark; only the worst examples from each affected suite.
85+
- Do not list every benchmark if there are many; only the worst examples from each affected suite. If you didn't list all, say "and X others".
86+
87+
## Guardrails
88+
- If the script or you can't find `bench-cli`, ask the user to provide it from the `bench-server` repo.
89+
- Don't submit more than 5 bisect jobs. If there are more in the "to bisect" list, pick 5 that look the most serious and leave the rest as "to bisect".

bisect-benchmark.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# Usage:
44
# - Create a temporary branch based on the main branch (or the bad commit)
55
# - Fill in this configuration file, preferably using the automated script
6-
# graal-enterprise/graalpython-enterprise/scripts/create-bisect-config
6+
# scripts/create_bisect_config.py
77
# - Commit and push the file
88
# - The push command output should give you a link to create a PR. Open it, but
99
# don't create a PR. Instead, you should execute the job on your commit using

ci/python-bench.libsonnet

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,9 @@
240240
downloads: downloads(self.os, self.arch),
241241
name: "bisect-benchmark",
242242
targets: ['bench'],
243-
logs +: logs(self.os, self.arch),
243+
logs +: logs(self.os, self.arch) + [
244+
"bisect-benchmark-result.json",
245+
],
244246
deploysArtifacts: true,
245247
packages +: packages(self.os, self.arch) + {
246248
"apache/ant": ">=1.9.4",

mx.graalpython/mx_graalpython_bisect.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
22
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
33
#
44
# The Universal Permissive License (UPL), Version 1.0
@@ -71,6 +71,8 @@ def print_line(l):
7171
GRAAL_DIR: GRAAL_ENTERPRISE_DIR,
7272
}
7373

74+
RESULTS_JSON_PATH = 'bisect-benchmark-result.json'
75+
7476

7577
def get_commit(repo_path, ref='HEAD'):
7678
if repo_path:
@@ -178,6 +180,22 @@ def summarize(self):
178180
.format(self.repo_name, self.bad_commit, get_message(self.repo_path, self.bad_commit)))
179181
return ''
180182

183+
def to_dict(self):
184+
return {
185+
'repo_name': self.repo_name,
186+
'repo_path': str(self.repo_path),
187+
'commits': self.commits,
188+
'results': [result.to_dict() if result is not None else None for result in self.results],
189+
'good_index': self.good_index,
190+
'bad_index': self.bad_index,
191+
'good_commit': self.good_commit,
192+
'bad_commit': self.bad_commit,
193+
'dependency_results': {
194+
str(index): dependency_result.to_dict()
195+
for index, dependency_result in self.dependency_results.items()
196+
},
197+
}
198+
181199

182200
class BenchmarkResult(abc.ABC):
183201
def __init__(self, value, unit=None):
@@ -201,6 +219,14 @@ def bound_is_significant(self, bad_result, epsilon):
201219
def is_good(self, good_result, bad_result):
202220
pass
203221

222+
def to_dict(self):
223+
return {
224+
'kind': type(self).__name__,
225+
'value': self.value,
226+
'unit': self.unit,
227+
'display': str(self),
228+
}
229+
204230

205231
class LowerIsBetterResult(BenchmarkResult):
206232
def is_good(self, good_result, bad_result):
@@ -358,6 +384,16 @@ def benchmark_callback(repo_path: Path, commit, bench_command=args.benchmark_com
358384
print()
359385
print(summary)
360386

387+
with open(RESULTS_JSON_PATH, 'w', encoding='utf-8') as result_file:
388+
json.dump({
389+
'bisect_id': bisect_id,
390+
'summary': summary,
391+
'visualization': visualization,
392+
'result': result.to_dict(),
393+
'build_url': os.environ.get('BUILD_URL'),
394+
}, result_file, indent=2, sort_keys=True)
395+
result_file.write('\n')
396+
361397
if args.rerun_with_commands:
362398
print('\n\nRerunning the good and bad commits with extra benchmark commands:')
363399
repo_path = DIR

0 commit comments

Comments
 (0)