gix-pack: add a benchmark for pack generation from loose objects#2675
gix-pack: add a benchmark for pack generation from loose objects#2675Amey Pawar (ameyypawar) wants to merge 4 commits into
Conversation
Adds the first gix-pack benchmark (criterion), for GitoxideLabs#2611. It isolates the counting phase (collecting the `Vec<output::Count>`) from the writing phase (streaming entries out as a pack), and reports the peak heap allocation of each phase plus the resulting pack size via a tracking global allocator, so the memory and delta-compression characteristics raised in the issue are grounded in concrete numbers.
`iter_from_counts` requires a `Send` object database handle (it resolves counts across threads); enable `gix-features/parallel` for the benchmark build so the handle is `Arc`-backed.
Add a `discover` phase that walks the database (`odb.iter()`) to enumerate the loose object ids, which `count` then turns into counts - so the benchmark covers walking as well as collecting. Also make the framing honest: the counts are sorted and resolved inside the write phase (not count); the reported figure is peak heap allocation (a lower bound on RSS); the discarding sink imposes no back-pressure; and `AsIs` input never attempts delta compression, so the pack size is an un-deltified baseline rather than a measured delta gap.
Adds the criterion and tempfile edges for the new gix-pack benchmark. Both crates are already in the lock from elsewhere in the workspace, so no new packages are introduced.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0be0f4ef45
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "Codex (@codex) review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "Codex (@codex) address that feedback".
| static LIVE: AtomicUsize = AtomicUsize::new(0); | ||
| static PEAK: AtomicUsize = AtomicUsize::new(0); | ||
|
|
||
| unsafe impl GlobalAlloc for PeakTracking { |
There was a problem hiding this comment.
Delegate realloc in the tracking allocator
For benchmark phases dominated by Vec growth (discover/count collect object lists and write builds chunks), this GlobalAlloc impl changes the allocator behavior because the trait's default realloc allocates a new block, copies, and frees the old block instead of using System.realloc. That inflates both timings and the recorded peak live bytes the benchmark is meant to measure, so the allocator should delegate realloc (and adjust the counters for the size delta) rather than relying on the default.
Useful? React with 👍 / 👎.
What
For #2611. Adds the first
gix-packbenchmark — a harness for pack generation from many loose objects — so the performance and memory questions raised there can be grounded in numbers, as Axel Ibarrondo (@cachel2) and I discussed in the thread. This is the measurement step before any code changes, not a fix for the underlying performance itself.The harness
gix-pack/benches/pack_generation.rsseparates the three phases of agix pack create-style generation and reports, per phase, both timing (criterion) and the peak heap allocation, plus the resulting pack size:odb.iter()) to enumerate loose object ids.count::objectsturns those ids into aVec<output::Count>(loads each object's header).iter_from_counts→FromEntriesIterresolves locations, sorts, and encodes the counts into a pack byte stream.Note on phase boundaries: the counts sort and pack-location resolve happen inside
iter_from_counts— i.e. in the write phase, not in count.Wiring: a
[[bench]]target,criterion/tempfiledev-deps, and theparallelfeature on the dev-dep (iter_from_countsrequires aSendobject-database handle). No new crates enterCargo.lock— both deps are already used elsewhere in the workspace.What it shows (example run; flat, unique blobs)
Discovery and count memory are O(N) but modest (~64 B and ~225 B per object); write peak grows sub-linearly with object count for this fixture; the pack is the un-deltified baseline.
Honest limits (also in the bench's doc comment)
writeis multi-threaded (wall-clock varies with cores);AsIsinput with no pre-existing pack never attempts delta compression — so the pack size is an un-deltified baseline; quantifying the delta gap againstgitwould need agit-packed baseline of the same objects.Verification
cargo clippy -p gix-pack --benchesandcargo fmt --checkare clean.cargo clippy --workspace --all-targets.gix-pack, so the object counts are kept modest to stay runnable; raiseOBJECT_COUNTSlocally to probe larger, more degenerate repositories.