Refactor (BREAKING)
- remove
object_hashfromOptions
This forces callers to explicitly choose a hash, eliminating the risk
thatSha1is implicitly chosen for them in SHA-1/SHA-256 builds
throughDefault::default()without them being aware of it or noticing.
This comes at the cost of callers having to always pass the hash, but
that seems like a reasonable trade-off.
New Features
-
expose index-less parallel pack traversal (cache Tree, Context, Options)
Re-exportcache::delta::{Tree, traverse::{Context, Options}}atcacheso
external callers can build a deltaTreedirectly from a pack header scan and
resolve all of its objects in parallel viaTree::traverse-- without a
pre-built.idx. This is the index-less companion to the already-public,
idx-verifiedindex::File::traverse_with_index.Minimal surface by design: the
deltamodule stayspub(crate), so only the
three already-documented items a caller names become public --pub mod delta
would instead expose the module's internal error types and constructors and fail
#![deny(missing_docs)].Tree::from_offsets_in_pack(already public) builds the
tree straight from the pack;with_capacity/add_root/add_childremain for
callers who assemble it by ascending pack offset themselves.Additive and non-breaking;
cargo docis clean under-D warnings, and a
doc-test anchors the index-less path (Tree::from_offsets_in_pack). Shape
approved in #2922.
Bug Fixes
-
validate delta base sizes during entry decoding
Reject delta instruction streams whose declared base size differs from the
resolved base object. The regression demonstrates that a REF_DELTA could
previously reconstruct successfully despite lying about its base size.Git reference: patch_delta() compares the decoded source size with src_size
before applying instructions, observed at cf5497b14c5a. -
resolve delta trees lazily in a lock-free pool
--- Summary of findings
The lazy, stealable resolver fixes the pathological phpstan pack without
regressing the Linux best case. At 16 threads, phpstan falls from the
98.38-second baseline resolver time to 22.70 seconds, while the Linux
best-case resolver improves slightly from 11.81 to 11.33 seconds. Charged
peak memory on phpstan falls from 13.14 GB to 2.16 GB, and the Linux best
case falls from 1.96 GB to 1.76 GB.Across the measured packs, gix resolver speedup at 16 threads ranges from
10.80x to 12.48x over its new serial path. On phpstan, gix is 1.07x faster
than Git at one thread and 2.31x faster at 16 threads. On the identical
10.9M-object Linux payload, gix's SHA-256 resolver is 2.72x to 2.99x faster
than its SHA-1 resolver, confirming that the slow SHA-1 implementation
dominates much of the remaining absolute cost.The Git SHA-256 comparison was initially unfair: the local Git build used
the portable SHA256_BLK backend while RustCrypto selected ARMv8 SHA-2
instructions. Rebuilding Git with OpenSSL reduced its eight-thread wall
time from 144.15 to 40.45 seconds and user CPU time from 471.94 to
99.81 seconds. All final Git SHA-256 comparisons use that faster backend.--- Pathological phpstan pack
The pack contains 100.7k objects but expands to 174.6 GB because one root
ends in a deep, expensive delta tree. The previous resolver takes
98.53 seconds wall clock and has a 13.14 GB peak memory footprint.The new resolver takes 283.41, 71.94, 36.42, and 22.83 seconds wall clock
at 1, 4, 8, and 16 threads. Its resolver speedups are 3.94x, 7.81x, and
12.48x relative to one thread, remaining nearly linear through eight
threads. Peak memory footprint is 0.68, 0.75, 1.17, and 2.16 GB
respectively. Thus the fastest run is 4.31x faster than the old wall time
while using 84% less charged peak memory.Git was measured at 1, 2, 4, 8, and 16 threads while keeping its aggregate
delta-base cache allowance at least as large as the default eight-thread
allowance of 8 times 96 MiB. Git takes 304.51, 168.06, 98.80, 65.41, and
52.79 seconds, for 5.77x one-to-sixteen-thread scaling. At equal thread
counts, gix is 1.07x, 1.37x, 1.80x, and 2.31x faster at 1, 4, 8, and
16 threads.At the nearest wall-time operating points, gix at four threads takes
71.94 seconds with a 0.75 GB peak footprint, while Git at eight threads
takes 65.41 seconds with a 1.90 GB peak footprint. gix therefore uses
61% less charged working memory near Git-matching throughput.--- Linux best-case pack
The 7.6M-object fixture expands to 95.6 GB and guards the already-friendly
case. The old implementation takes 13.68 seconds wall clock and
11.81 seconds in the resolver. The new implementation takes 137.48,
36.75, 19.81, and 13.16 seconds wall clock at 1, 4, 8, and 16 threads;
resolver scaling reaches 11.97x at 16 threads.The 16-thread result is slightly faster than the old implementation, with
11.33 seconds in the resolver, and lowers peak footprint from 1.96 to
1.76 GB. Peak footprint remains effectively constant from one through
16 threads. Git takes 156.65, 60.48, 45.48, and 42.97 seconds at the same
thread counts, so gix's advantage grows from 1.14x to 3.27x.--- SHA-1 and SHA-256 sibling packs
The sibling packs contain 10.9M objects, expand to 146.7 GB, and preserve
the same compressed payload and delta topology. This isolates hashing from
work distribution.For SHA-1, gix takes 212.57, 56.58, 30.60, and 20.74 seconds wall clock at
1, 4, 8, and 16 threads. Resolver scaling reaches 11.36x at 16 threads.
The default eight-thread Git run takes 125.11 seconds, while gix takes
30.60 seconds at eight threads and 20.74 seconds at 16.For SHA-256, gix takes 72.47, 21.83, 12.58, and 8.67 seconds wall clock.
Resolver scaling reaches 10.80x at 16 threads. Against OpenSSL-backed Git,
gix is 1.21x, 2.02x, 3.22x, and 5.61x faster at equal thread counts.
Git improves from 87.85 seconds at one thread to 40.45 seconds at eight,
then regresses to 48.68 seconds at 16 with 204.81 seconds of system CPU.--- Hasher and scheduler interpretation
On identical pack data, gix SHA-256 resolver time is 70.31 seconds at one
thread versus 210.35 seconds for SHA-1, and 6.51 seconds at 16 threads
versus 18.51 seconds for SHA-1. SHA-256 is therefore 2.99x faster
serially and 2.84x faster at 16 threads, with a 2.72x to 2.99x advantage
throughout the measured range.The similar 10.80x to 12.48x gix scaling across pathological, best-case,
SHA-1, and SHA-256 packs argues against lock contention being the main
high-thread limitation. The remaining flattening is consistent with
finite parallel work, scheduling overhead, memory bandwidth, and hashing
cost.--- Memory interpretation
The resolver no longer materializes the unresolved internal-node frontier.
It resolves a child only when a worker starts it, shares immutable bases
between sibling tasks, and recycles the final base reference. A linear
chain therefore needs roughly two object buffers per active worker instead
of retaining every intermediate base.macOS max RSS includes clean file-backed pages. gix maps the pack, so its
reported RSS can include most of a multi-gigabyte pack even when those
pages are reclaimable; Git reads through bounded pread buffers. Peak memory
footprint better represents charged working memory here. On phpstan, gix
max RSS grows from 7.20 to 8.90 GB across 1 to 16 threads, but charged
footprint grows from only 0.68 to 2.16 GB. On the Linux best case,
footprint stays at 1.76 GB across the same range.--- Implementation
Use lock-free local deques and a shared root injector so idle workers can
steal branches from the last expensive root without every worker hoarding
roots. Enable this resolver through the gix parallel feature.Regression tests prove that internal siblings are not all materialized
before descent and that two workers can concurrently resolve children from
one remaining root. The scheduler follows the proven Git index-pack
principles of bounded live delta bases and keeping independent delta work
available, informed by builtin/index-pack.c at cf5497b14c. -
resolve in-pack ref deltas while indexing
Valid packs may encode a base inside the same pack with REF_DELTA and may
place the delta before that base. The streaming lookup previously treated an
object-database miss as fatal, while index construction rejected every remaining
ref delta.Keep lookup misses in the stream and park ref-delta children by base object ID
until traversal resolves the matching object. This preserves external thin-pack
injection and reuses the existing delta tree for forward and chained references.Git reference: a23bace963d508bd96983cc637131392d3face18, builtin/index-pack.c and t/t5300-pack-object.sh.
Commit Statistics
- 19 commits contributed to the release over the course of 30 calendar days.
- 30 days passed between releases.
- 5 commits were understood as conventional.
- 3 unique issues were worked on: #1025, #2424, #2868
Commit Details
view details
- #1025
- Resolve in-pack ref deltas while indexing (64b9efe)
- #2424
- Resolve delta trees lazily in a lock-free pool (d7bbb8e)
- #2868
- Validate delta base sizes during entry decoding (916f5d8)
- Uncategorized
- Update manifests prior to release (ebe9095)
- Merge pull request #2926 from cruessler/remove-object-hash-from-options (b2d919a)
- Remove
object_hashfromOptions(c055803) - Merge pull request #2905 from GitoxideLabs/various-improvements (f3bbfad)
- Adapt to changes in
gix-testtools(0cbe539) - Merge pull request #2923 from rdicosmo/swh-expose-delta-tree-main (c4426f0)
- Review (dcb8ccd)
- Expose index-less parallel pack traversal (cache Tree, Context, Options) (40a4ca7)
- Merge pull request #2916 from cruessler/require-object-hash-in-store-at (dd8c759)
- Adapt to changes in
gix-odb(1dc741f) - Merge pull request #2869 from GitoxideLabs/validate-delta-base-size (9c12c2d)
- Merge pull request #2867 from GitoxideLabs/fix-url-authority-parsing (cc3ee80)
- Release gix-path v0.12.4, gix-command v0.9.2, gix-config-value v0.19.1, gix-url v0.37.1, gix-credentials v0.39.1, gix-transport v0.58.1 (ab4fcb0)
- Merge pull request #2852 from GitoxideLabs/delta-tree-parallelism (4a6cf9d)
- Merge pull request #2825 from GitoxideLabs/azure-compatibility (9b787f6)
- Merge pull request #2812 from GitoxideLabs/report-july (ae8845a)