feat: cacheless hamt iteration - #2216
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #2216 +/- ##
==========================================
+ Coverage 77.58% 77.59% +0.01%
==========================================
Files 147 147
Lines 15789 15851 +62
==========================================
+ Hits 12250 12300 +50
- Misses 3539 3551 +12
🚀 New features to boost your workflow:
|
| fn hash<X>(key: &X) -> HashedKey | ||
| where | ||
| X: Hash, | ||
| X: Hash + ?Sized, |
There was a problem hiding this comment.
To fix a clippy warning
| Ok(()) | ||
| } | ||
|
|
||
| pub fn for_each_cacheless<F>(&self, mut f: F) -> anyhow::Result<()> |
|
All in all, LGTM, but I'll let @rvagg have a final say here, my HAMT-fu is not that strong. |
There was a problem hiding this comment.
Pull Request Overview
This PR adds cacheless iteration functionality to HAMT (Hash Array Mapped Trie) to reduce memory usage when iterating over large HAMTs. The primary addition is for_each_cacheless methods that avoid caching nodes during iteration, which can be more memory-efficient for one-time traversals.
- Implements
for_each_cachelessmethods for bothHamtImplandStateTree - Adds comprehensive tests for the new cacheless iteration functionality
- Includes benchmarks comparing performance between cached and cacheless iteration
Reviewed Changes
Copilot reviewed 9 out of 10 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| ipld/hamt/tests/hamt_tests.rs | Adds comprehensive test suite for for_each_cacheless functionality and refactors existing test infrastructure |
| ipld/hamt/src/pointer.rs | Implements Clone trait for Pointer enum to support cacheless iteration |
| ipld/hamt/src/node.rs | Implements Clone trait for Node and adds core for_each_cacheless iteration logic |
| ipld/hamt/src/lib.rs | Adds Clone trait to KeyValuePair struct to support cloning operations |
| ipld/hamt/src/hash_algorithm.rs | Minor refactor to parameter order for Identity::hash method |
| ipld/hamt/src/hamt.rs | Adds public for_each_cacheless method to the main HAMT API |
| ipld/hamt/benches/hamt_benchmark.rs | Adds benchmark for for_each_cacheless and refactors existing benchmarks |
| ipld/hamt/Cargo.toml | Adds itertools dependency for test utilities |
| fvm/src/state_tree.rs | Adds for_each_cacheless method to StateTree API |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| } | ||
| IterItem::Borrowed(Pointer::Dirty(node)) => stack.push(node.pointers.iter().into()), | ||
| IterItem::Owned(Pointer::Dirty(node)) => { | ||
| stack.push(node.pointers.clone().into_iter().into()) |
There was a problem hiding this comment.
The clone() call here creates an unnecessary copy of the entire pointers vector. Consider restructuring to avoid this clone operation, as it defeats the memory efficiency purpose of the cacheless iteration.
| stack.push(node.pointers.clone().into_iter().into()) | |
| stack.push(node.pointers.into_iter().into()) |
| /// map.set(4, 2).unwrap(); | ||
| /// | ||
| /// let mut total = 0; | ||
| /// map.for_each_cacheless(|_, v: &u64| { |
There was a problem hiding this comment.
The type annotation in the example should be &usize to match the generic type used in the example setup, not &u64.
| /// map.for_each_cacheless(|_, v: &u64| { | |
| /// map.for_each_cacheless(|_, v: &usize| { |
|
Hey @LesnyRumcajs @rvagg I have addressed all outstanding comments and all CI checks are green, please take another look. |
There was a problem hiding this comment.
LGTM, but I'll let @rvagg make a final call. As mentioned earlier, my HAMT-fu is weak. :)
ZenGround0
left a comment
There was a problem hiding this comment.
This LGTM. It's nice you are getting the same ordering but that's probably more than you need anyway since HAMTs are inherently unordered.
| /// map.set(4, 2).unwrap(); | ||
| /// | ||
| /// let mut total = 0; | ||
| /// map.for_each(|_, v: &u64| { |
There was a problem hiding this comment.
why remove this? Unless I'm missing something it clarifies the example.
| where | ||
| F: FnMut(&K, &V) -> anyhow::Result<()>, | ||
| S: Blockstore, | ||
| K: Clone, |
There was a problem hiding this comment.
Do we actually need to implement clone? I tried compiling with the trait removed from requirements here and Node and things seem to work.
There was a problem hiding this comment.
You're right, Clone is no longer needed. Removed.
(Simliar to #2189)
This PR adds
HamtImpl::for_each_cachelessandStateTree::for_each_cachelessto reduce memory usage for iterating over a large hamt, to address #2215 (comment)cargo benchshows no significant difference betweenfor_eachandfor_each_cacheless