Repository navigation
perf(gix-status): cap automatic macOS modification checks at three threads (#3185) - #3187
Merged
Sebastian Thiel (Byron) merged 2 commits intoNov 10, 2026
Conversation
…reads (GitoxideLabs#3185) On macOS, `gix status` limits the tracked-file modification check to three threads, but the cap lived in the CLI (`src/plumbing/main.rs`). Library callers, including `Repository::status()`, `Repository::index_worktree_status()` and `Repository::is_dirty()`, used one thread per logical core. On a 14-core Mac with clean stat data, the extra threads added CPU time without finishing sooner. This came up while evaluating `gix` for the status call of [residue](https://github.com/whiteheap/residue-cli), which runs one status per Git worktree it checks. Resolve `None` and `Some(0)` to at most three available logical cores in `gix_status::index_as_worktree::Options`, so every caller inherits the cap, as checkout and worktree removal already do with theirs. Positive limits remain overrides, and other platforms keep their defaults. The CLI drops its special case and still uses three threads; only `--threads 0` changes, from all cores to the automatic cap. Update the option docs and the `--threads` help. ## Measurements Apple M4 Max, 14 logical cores, 36 GB, macOS 26.3.1, APFS, warm cache. A probe built in release mode with Rust 1.94.0 calls `Repository::status()` with its defaults and collects every item. It is built against `main` at `e2743fe28` (before) and against this commit (after), runs under `/usr/bin/time -l`, and alternates before and after each round. The worktree is a `git clone --shared` of a repository with 60,038 index entries and 9 status items. Other work shared the machine, with a 1-minute load average of 33 to 43, so wall times are noisy and CPU times are steadier. Medians of two batches each: | Worktree | Wall s | CPU s (user + sys) | Peak footprint MB | |---|---|---|---| | Clean stat data, 21 + 21 rounds | 0.19 → 0.18, 0.13 → 0.10 | 0.59 → 0.39, 0.57 → 0.30 | 29.5 → 28.8, 29.5 → 28.5 | | All 60,033 files re-read (1.21 GB), 9 + 11 rounds | 1.71 → 1.90, 1.78 → 2.08 | 11.38 → 4.64, 12.66 → 4.36 | 36.7 → 33.6, 37.1 → 35.1 | The cap used less CPU in 39 of 42 clean pairs and in all 20 re-read pairs. The status output was byte-identical in every run. The second row is the case the removed CLI comment noted: "MacOS also scales if reading a lot of files for refresh index". Touching every tracked file makes each status hash all files again, as long as nothing writes the index back. The cap costs wall time there. A sweep with explicit limits on that worktree, 9 rotated rounds under a load of 17 to 60, shows the direction: | Threads | 1 | 2 | 3 | 4 | 6 | 8 | 10 | 14 | |---|---:|---:|---:|---:|---:|---:|---:|---:| | Wall s | 10.26 | 6.02 | 4.05 | 3.19 | 2.18 | 1.80 | 1.84 | 1.75 | | CPU s | 4.10 | 3.53 | 3.67 | 4.14 | 5.78 | 7.56 | 6.85 | 9.71 | The sweep's 3-thread wall time is about twice the batches' figure: with 18 variants per round, the load changed more within a round. Callers that expect many reads can pass a higher `thread_limit`. ## Limits One Mac, warm cache, heavy background load. Not measured: an idle machine, a cold cache, Intel Macs or other core counts, and `is_dirty()` on its own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Codex Review Summary: ✅ CompletedThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
🟢 Approval recommended
No active review issues remain for the change centralizing macOS status thread limits in gix-status.
No open issues 🎉
What changed in this PR
Moves the macOS status thread cap from the CLI into gix-status, giving library and CLI callers consistent automatic limits and addressing #3185.
Changes:
- Caps automatic tracked-file modification checks at three threads on macOS, while preserving positive overrides and other platforms’ behavior.
- Removes the CLI-specific cap and updates API documentation and
--threadshelp. - Adds a unit test covering automatic limits and explicit overrides.
| File | Description |
|---|---|
src/plumbing/options/mod.rs |
Clarifies automatic thread selection in CLI help. |
src/plumbing/main.rs |
Removes the CLI-specific macOS status cap. |
gix/src/status/index_worktree.rs |
Documents the shared cap and explicit overrides. |
gix-status/src/index_as_worktree/types.rs |
Implements and tests platform-specific thread-limit resolution. |
gix-status/src/index_as_worktree/function.rs |
Uses the resolved limit when sizing modification-check workers. |
Review effort: Balanced
Sebastian Thiel (Byron)
merged commit Nov 10, 2026
8118e5e
into
GitoxideLabs:main
24 of 33 checks passed
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.
Fixes #3185
Motivation
We were evaluating
gixas the status backend of residue, which runs one status per Git worktree to decide whether it can be removed. On macOS,Repository::status()started one modification-check thread per logical core (14 here), whilegix statusused 3. The extra threads cost CPU without finishing sooner on a clean worktree.What changed
gix_status::index_as_worktree::Optionsresolvesthread_limitNoneandSome(0)to at most 3 threads on macOS. This follows the macOS caps ingix-worktree-statecheckout andgix-worktreeremoval.Repository::status(),index_worktree_status(),is_dirty()and directgix-statuscallers all get the cap.cfg!(target_os = "macos").then_some(3).gix statusstill uses 3 threads. Only--threads 0changes, from all cores to the cap.thread_limitdocs ingix-statusandgix, and the--threadshelp.index_as_worktree::types::tests::thread_limit_resolution.Before and after
Apple M4 Max, 14 cores, macOS 26.3.1, APFS, warm cache. A release-built probe calls
Repository::status()with defaults and collects every item. Before ismainate2743fe28, after is this branch./usr/bin/time -l, before and after alternate each round. The worktree is agit clone --sharedof a repository with 60,038 index entries and 9 status items. Other work shared the machine: load average 33 to 43, so wall times are noisy. Medians of two batches:ps -Mduring a re-read run.The second row is the case the old CLI comment named: "MacOS also scales if reading a lot of files for refresh index". Every tracked file was touched, so each status hashes all files again until something writes the index back. The cap costs wall time there. A sweep with explicit
thread_limiton that worktree (9 rotated rounds, load 17 to 60) shows the direction:Its 3-thread wall time is about twice the batch figure. With 18 variants per round, load changed more within a round. If the re-read case matters more than idle CPU, 8 threads came within 3% of 14 on wall time with 22% less CPU in this sweep. The cap value is one constant.
Checks run
All with Rust 1.99.1 unless noted.
cargo fmt --all -- --check: clean (also on 1.94.0). Nightly rustfmt withrustfmt-nightly.tomladds no new diffs in the touched files.cargo clippy --locked --all-targets -p gix-status -p gix -p gitoxide -- -D warnings -A unknown-lints --no-deps: clean.--target x86_64-unknown-linux-gnu -p gix-status -p gix: clean.cargo clippy -p gix-status --no-default-features --features sha1(noparallel): clean.cargo checkforgix-status,gix,gitoxide: clean. Linux target:gix-status,gix, andgitoxidewith--no-default-features --features small.GIX_TEST_IGNORE_ARCHIVES=1 cargo test -p gix-status: 1 unit and 42 integration tests pass.left: None, right: Some(3)when the cap is removed.GIX_TEST_IGNORE_ARCHIVES=1 cargo test -p gix: all pass (597 intests/gix, 46 doctests, the rest 44).cargo test -p gitoxide: 7 pass.tests/journey.shwith themax,http-client-curl-rustlsbuild: exit 0, including "keeps status worker spans under the command's root span".cargo doc --no-deps -p gix-status -p gix: no new warnings.Not verified
is_dirty()timing on its own.cfg(target_os = "macos")only.just test(justis not installed here).GIX_TEST_IGNORE_ARCHIVES=1regenerated were reverted, not committed.🤖 Generated with Claude Code