Skip to content

Commit 4a3497a

Browse files
committed
ci: Refactor benchmark regression checks
iai-callgrind now correctly exits with error if regressions were found [1], so we no longer need to check for regressions manually. Remove this check and instead exit based on the exit status of the benchmark run. [1] iai-callgrind/iai-callgrind#337
1 parent 4f943d4 commit 4a3497a

File tree

2 files changed

+29
-74
lines changed

2 files changed

+29
-74
lines changed

ci/bench-icount.sh

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,17 +46,18 @@ function run_icount_benchmarks() {
4646
shift
4747
done
4848

49-
# Run iai-callgrind benchmarks
50-
cargo bench "${cargo_args[@]}" -- "${iai_args[@]}"
49+
# Run iai-callgrind benchmarks. Do this in a subshell with `&& true` to
50+
# capture rather than exit on error.
51+
(cargo bench "${cargo_args[@]}" -- "${iai_args[@]}") && true
52+
exit_code="$?"
5153

52-
# NB: iai-callgrind should exit on error but does not, so we inspect the sumary
53-
# for errors. See https://github.com/iai-callgrind/iai-callgrind/issues/337
54-
if [ -n "${PR_NUMBER:-}" ]; then
55-
# If this is for a pull request, ignore regressions if specified.
56-
./ci/ci-util.py check-regressions --home "$iai_home" --allow-pr-override "$PR_NUMBER"
57-
else
54+
if [ "$exit_code" -eq 0 ]; then
55+
echo "Benchmarks completed with no regressions"
56+
else if [ -z "${PR_NUMBER:-}" ]; then
5857
# Disregard regressions after merge
59-
./ci/ci-util.py check-regressions --home "$iai_home" || true
58+
echo "Benchmarks completed with regressions; ignoring (not in a PR)"
59+
else
60+
./ci/ci-util.py handle-banch-regressions "$PR_NUMBER"
6061
fi
6162
}
6263

ci/ci-util.py

Lines changed: 19 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import subprocess as sp
1212
import sys
1313
from dataclasses import dataclass
14-
from glob import glob, iglob
14+
from glob import glob
1515
from inspect import cleandoc
1616
from os import getenv
1717
from pathlib import Path
@@ -38,14 +38,10 @@
3838
3939
Note that `--extract` will overwrite files in `iai-home`.
4040
41-
check-regressions [--home iai-home] [--allow-pr-override pr_number]
42-
Check `iai-home` (or `iai-home` if unspecified) for `summary.json`
43-
files and see if there are any regressions. This is used as a workaround
44-
for `iai-callgrind` not exiting with error status; see
45-
<https://github.com/iai-callgrind/iai-callgrind/issues/337>.
46-
47-
If `--allow-pr-override` is specified, the regression check will not exit
48-
with failure if any line in the PR starts with `allow-regressions`.
41+
handle-bench-regressions PR_NUMBER
42+
Exit with success if the pull request contains a line starting with
43+
`ci: allow-regressions`, indicating that regressions in benchmarks should
44+
be accepted. Otherwise, exit 1.
4945
"""
5046
)
5147

@@ -365,64 +361,22 @@ def locate_baseline(flags: list[str]) -> None:
365361
eprint("baseline extracted successfully")
366362

367363

368-
def check_iai_regressions(args: list[str]):
369-
"""Find regressions in iai summary.json files, exit with failure if any are
370-
found.
371-
"""
372-
373-
iai_home_str = "iai-home"
374-
pr_number = None
375-
376-
while len(args) > 0:
377-
match args:
378-
case ["--home", home, *rest]:
379-
iai_home_str = home
380-
args = rest
381-
case ["--allow-pr-override", pr_num, *rest]:
382-
pr_number = pr_num
383-
args = rest
384-
case _:
385-
eprint(USAGE)
386-
exit(1)
387-
388-
iai_home = Path(iai_home_str)
389-
390-
found_summaries = False
391-
regressions: list[dict] = []
392-
for summary_path in iglob("**/summary.json", root_dir=iai_home, recursive=True):
393-
found_summaries = True
394-
with open(iai_home / summary_path, "r") as f:
395-
summary = json.load(f)
396-
397-
summary_regs = []
398-
run = summary["callgrind_summary"]["callgrind_run"]
399-
fname = summary["function_name"]
400-
id = summary["id"]
401-
name_entry = {"name": f"{fname}.{id}"}
402-
403-
for segment in run["segments"]:
404-
summary_regs.extend(segment["regressions"])
364+
def handle_bench_regressions(args: list[str]):
365+
"""Exit with error unless the PR message contains an ignore directive."""
405366

406-
summary_regs.extend(run["total"]["regressions"])
407-
408-
regressions.extend(name_entry | reg for reg in summary_regs)
409-
410-
if not found_summaries:
411-
eprint(f"did not find any summary.json files within {iai_home}")
412-
exit(1)
367+
match args:
368+
case [pr_number]:
369+
pr_number = pr_number
370+
case _:
371+
eprint(USAGE)
372+
exit(1)
413373

414-
if len(regressions) == 0:
415-
eprint("No regressions found")
374+
pr = PrInfo.load(pr_number)
375+
if pr.contains_directive(REGRESSION_DIRECTIVE):
376+
eprint("PR allows regressions")
416377
return
417378

418-
eprint("Found regressions:", json.dumps(regressions, indent=4))
419-
420-
if pr_number is not None:
421-
pr = PrInfo.load(pr_number)
422-
if pr.contains_directive(REGRESSION_DIRECTIVE):
423-
eprint("PR allows regressions, returning")
424-
return
425-
379+
eprint("Regressions were found; benchmark failed")
426380
exit(1)
427381

428382

@@ -433,8 +387,8 @@ def main():
433387
ctx.emit_workflow_output()
434388
case ["locate-baseline", *flags]:
435389
locate_baseline(flags)
436-
case ["check-regressions", *args]:
437-
check_iai_regressions(args)
390+
case ["handle-bench-regressions", *args]:
391+
handle_bench_regressions(args)
438392
case ["--help" | "-h"]:
439393
print(USAGE)
440394
exit()

0 commit comments

Comments
 (0)