- Notifications
You must be signed in to change notification settings - Fork868
perf: optimize ObjectID.ReadFrom with direct io.ReadFull#1738
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
base:main
Are you sure you want to change the base?
Uh oh!
There was an error while loading.Please reload this page.
Conversation
Replace encoding/binary.Read with io.ReadFull in ObjectID.ReadFrom forbetter performance, eliminating reflection overhead.Background:-----------The ObjectID.ReadFrom method used encoding/binary.Read, which employsreflection to copy bytes into the hash array. This adds unnecessary overheadin hot paths, particularly during Tree.Decode operations that are calledthousands of times during clone operations.Changes:--------1. Updated ObjectID.ReadFrom implementation to use io.ReadFull directly instead of encoding/binary.Read2. Added error handling to zero out the hash on read errors for cleaner state3. Updated test coverage to verify the optimized implementation: - TestReadFrom: validates existing functionality still works - TestReadFromOptimization: validates direct io.ReadFull behaviorTechnical Notes:---------------This is a backward-compatible change. The method signature remains identicaland it still implements io.ReaderFrom. For byte arrays (like hashes),binary.Read's BigEndian parameter is a no-op since no byte-order conversionoccurs. io.ReadFull produces identical results while being significantlyfaster by avoiding reflection.Performance Impact:------------------Benchmark results from 10 iterations (AMD Ryzen 7 6800U):Speed improvements (statistically significant, p<0.05): TreeDecode: -2.91% (1429ns → 1387ns) TreeDecodeSmall: -8.56% (308ns → 282ns) TreeDecodeLarge: -5.47% (16.95µs → 16.02µs) HashReadFrom: -2.47% (65.18ns → 63.57ns) Overall geometric: -4.02%Memory: Allocations: unchanged (same count) Bytes/op: +6.81% to +14.45% increase (acceptable tradeoff)The speed improvements are consistent across all tree sizes, with the mostsignificant gains on small trees (-8.56%). CPU profiling showed hash readingoverhead reduced from 13% to 7% of total Tree.Decode time.This optimization benefits all callers automatically with no code changesrequired, as the method signature and behavior remain identical.
There was a problem hiding this 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 optimizesObjectID.ReadFrom by replacingencoding/binary.Read withio.ReadFull to eliminate reflection overhead. The change is motivated by profiling data showing hash reading operations consuming significant CPU time during Tree.Decode operations in hot paths like git clone.
Key changes:
- Replaced
binary.Readwithio.ReadFullinObjectID.ReadFrommethod - Added hash zeroing on read errors for cleaner error state
- Added comprehensive test coverage for the optimization with both success and error cases
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| plumbing/objectid.go | Replacedbinary.Read withio.ReadFull in ReadFrom method, added error handling to zero hash on failure, updated documentation |
| plumbing/objectid_test.go | AddedTestReadFromOptimization with comprehensive test cases for SHA1/SHA256 valid reads, partial reads, and empty reader scenarios |
💡Add Copilot custom instructions for smarter, more guided reviews.Learn how to get started.
Uh oh!
There was an error while loading.Please reload this page.
Replace encoding/binary.Read with io.ReadFull in ObjectID.ReadFrom for better performance, eliminating reflection overhead.
Background:
The ObjectID.ReadFrom method used encoding/binary.Read, which employs reflection to copy bytes into the hash array. This adds unnecessary overhead in hot paths, particularly during Tree.Decode operations that are called thousands of times during clone operations.
Changes:
Updated ObjectID.ReadFrom implementation to use io.ReadFull directly instead of encoding/binary.Read
Added error handling to zero out the hash on read errors for cleaner state
Updated test coverage to verify the optimized implementation:
Technical Notes:
This is a backward-compatible change. The method signature remains identical and it still implements io.ReaderFrom. For byte arrays (like hashes), binary.Read's BigEndian parameter is a no-op since no byte-order conversion occurs. io.ReadFull produces identical results while being significantly faster by avoiding reflection.
Performance Impact:
Benchmark results from 10 iterations (AMD Ryzen 7 6800U):
Speed improvements (statistically significant, p<0.05):
TreeDecode: -2.91% (1429ns → 1387ns)
TreeDecodeSmall: -8.56% (308ns → 282ns)
TreeDecodeLarge: -5.47% (16.95µs → 16.02µs)
HashReadFrom: -2.47% (65.18ns → 63.57ns)
Overall geometric: -4.02%
Memory:
Allocations: unchanged (same count)
Bytes/op: +6.81% to +14.45% increase (acceptable tradeoff)
The speed improvements are consistent across all tree sizes, with the most significant gains on small trees (-8.56%). CPU profiling showed hash reading overhead reduced from 13% to 7% of total Tree.Decode time.
This optimization benefits all callers automatically with no code changes required, as the method signature and behavior remain identical.