Repository navigation
perf(gazelle): parse Python files on all CPUs ahead of rule generation - #4482
Open
Shamsidinkhon wants to merge 2 commits into
Open
Shamsidinkhon wants to merge 2 commits into
Shamsidinkhon wants to merge 2 commits into
Conversation
Shamsidinkhon
requested review from
aignas,
dougthor42 and
rickeylev
as code owners
November 12, 2026 13:27
aignas
reviewed
Nov 12, 2026
aignas
left a comment
Collaborator
There was a problem hiding this comment.
Please add unit tests for the new code.
Author
Thanks, I added unit tests |
dougthor42
reviewed
Nov 13, 2026
dougthor42
left a comment
Collaborator
There was a problem hiding this comment.
I ran this on our relatively large repo (~7500 targets, ~7700 python files, ~1940 dirs).
Overall speedup was ~76s --> 8.6s, an 88.7% reduction in overall time, hot damn!
Granted, this was on a 64-core cloud VM...
Before
On rules_python 2.5.0
$ hyperfine --warmup=1 --runs=3 'bazel run --bes_backend= //:gazelle'
Benchmark 1: bazel run --bes_backend= //:gazelle
Time (mean ± σ): 76.605 s ± 0.107 s [User: 76.476 s, System: 4.481 s]
Range (min … max): 76.485 s … 76.691 s 3 runs
After
On commit db95bd53
$ hyperfine --warmup=1 --runs=3 'bazel run --bes_backend= //:gazelle'
Benchmark 1: bazel run --bes_backend= //:gazelle
Time (mean ± σ): 8.611 s ± 0.550 s [User: 125.623 s, System: 6.973 s]
Range (min … max): 8.224 s … 9.241 s 3 runs
| @@ -0,0 +1,179 @@ | |||
| // Copyright 2026 The Bazel Authors. All rights reserved. | |||
Collaborator
There was a problem hiding this comment.
IIRC we no longer need these notices.
| // f is the build file for the current directory or nil if there is no | ||
| // existing build file. | ||
| func (py *Configurer) Configure(c *config.Config, rel string, f *rule.File) { | ||
| activePrefetcher.Load().enqueueDir(rel) |
Collaborator
There was a problem hiding this comment.
Thoughts on guarding things behind an env var or CLI flag?
My main concern is edge cases, where it would be nice to have an easy way to go back to the prior behavior to double-check things.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Gazelle calls
GenerateRulesfor one directory at a time, and the plugin parses only the files of that directory, at most 6 at a time. Most directories have only a few Python files, so on a large repository about 2 CPUs are busy however many the machine has, and parsing takes most of the run time.This adds a prefetcher that parses files ahead of the walk. When
Configurereaches a directory passed on the command line, the prefetcher lists the Python files under it from the walk's cached directory listings (walk.GetDirInfo), honoring-r=false, and parses them in the background in the order rules will be generated, at mostGOMAXPROCSat a time. WhenGenerateRulesparses a file, it takes the prefetched result, waiting for it if needed; a file that wasn't prefetched is parsed as before. Only the timing of parsing changes, so the generated BUILD files are the same. The prefetcher is only set up for commands with a-modeflag (updateandfix). It is a package-level variable becauseCheckFlags,Configureand the parser share no other state; it could move into theConfigurerif preferred.I wrote this change with an AI coding assistant (Claude Code) and reviewed it myself. I'll read and answer review comments personally; no agent posts on this PR.