Skip to content

Add a helper script to bisect a regression#430

Merged
mdboom merged 3 commits intofaster-cpython:mainfrom
mdboom:bisect
May 5, 2025
Merged

Add a helper script to bisect a regression#430
mdboom merged 3 commits intofaster-cpython:mainfrom
mdboom:bisect

Conversation

@mdboom
Copy link
Copy Markdown
Contributor

@mdboom mdboom commented Apr 30, 2025

A helper script to bisect a regression, usage:

Usage: __main__.py [-h] [--pgo] [--flags FLAGS] benchmark good bad

Run bisect on a benchmark to find the first regressing commit. A full checkout of CPython should be in the cpython
directory. If it doesn't exist, it will be cloned.

Positional Arguments:
  benchmark      The benchmark to run bisect on.
  good           The good commit hash for the bisect.
  bad            The bad commit hash for the bisect.

Options:
  -h, --help     show this help message and exit
  --pgo
  --flags FLAGS

Follow-on (but not part of this PR) will be to connect it to GitHub Actions so users can easily kick it off on one of the runners.

@mdboom mdboom requested review from Yhg1s and Copilot April 30, 2025 14:27
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds a helper script for bisecting regressions in CPython by automating the checkout, build, and benchmark run steps. Key changes include:

  • Introducing a new helper function (format_seconds) for human-readable time formatting.
  • Updating compile_unix to add a reconfigure flag.
  • Implementing the bisect script that automates bisect operations and logs results.

Reviewed Changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
bench_runner/util.py Added format_seconds for time formatting with a loop using magic numbers.
bench_runner/scripts/workflow.py Updated compile_unix signature with a reconfigure flag to clean builds appropriately.
bench_runner/scripts/bisect.py Implemented the bisect flow including result parsing, logging, and git bisect automation.
bench_runner/git.py Added a simple checkout helper function.
bench_runner/main.py Registered bisect as a new command in the command entry point.
Comments suppressed due to low confidence (1)

bench_runner/scripts/bisect.py:206

  • Parsing the 'pgo' argument by comparing against the string "True" can be error-prone. Consider converting the argument to a boolean explicitly during argument parsing for clarity.
timing = get_result(args.benchmark, args.pgo == "True", args.flags, cpython=cpython)

Comment thread bench_runner/util.py
Comment on lines +96 to +100
for i in range(2, -9, -1):
if value >= 10.0**i:
break
else:
i = -9
Copy link

Copilot AI Apr 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of magic numbers in the loop (range from 2 to -9) reduces readability. Consider defining named constants or adding inline comments to clarify the purpose of these bounds.

Suggested change
for i in range(2, -9, -1):
if value >= 10.0**i:
break
else:
i = -9
MAX_POWER_OF_TEN = 2 # Maximum power of ten to check (10^2 = 100 seconds)
MIN_POWER_OF_TEN = -9 # Minimum power of ten to check (10^-9 = 1 nanosecond)
for i in range(MAX_POWER_OF_TEN, MIN_POWER_OF_TEN - 1, -1):
if value >= 10.0**i:
break
else:
i = MIN_POWER_OF_TEN

Copilot uses AI. Check for mistakes.
Comment thread bench_runner/__main__.py

sys.argv = [sys.argv[0], *sys.argv[2:]]
mod = importlib.import_module(f"bench_runner.scripts.{command}")
mod = importlib.import_module(f".{command}", "bench_runner.scripts")
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just due to picking the unfortunate name bisect that conflicts with the name of a stdlib module and not being careful about how things are run elsewhere.

print("Bisect log:")

with get_log_file().open("r") as f:
for line in f.readlines():
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason to use readlines instead of just iterating over the file?

Suggested change
for line in f.readlines():
for line in f:

with contextlib.chdir(cpython):
subprocess.check_call(["./configure", *args], env=env)
if reconfigure:
subprocess.check_call(["./configure", *args], env=env)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if you don't explicitly run configure, the Makefile may decide to rerun configure when jumping through revisions... It's probably a good idea to pass -C (or --config-cache) to configure to significantly reduce configure time.

Comment thread bench_runner/__main__.py

COMMANDS = {
"backfill": "Schedule benchmarking a number of commits",
"bisect": "Run a bisect to find the commit that caused a regression",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a comment that it won't work on Windows?



if __name__ == "__main__":
# This is the entry point when we are called from `git bisect run` itself
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels a little magical and I would've probably just made this a separate script instead, but as long as it works...

@mdboom mdboom merged commit dfaf4ea into faster-cpython:main May 5, 2025
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants