starknet_patricia: actually preallocate filled-tree output map capacity#14809
starknet_patricia: actually preallocate filled-tree output map capacity#14809gkaempfer wants to merge 1 commit into
Conversation
PR #14638 switched initialize_filled_tree_output_map_with_placeholders to build the map via filter_map(...).collect(), intending to preallocate capacity from the node count. But FilterMap's size_hint lower bound is always 0 (the predicate can drop every item), so collect() through it still starts the HashMap at zero capacity and rehashes as nodes are inserted -- the same O(log N) rehash cost the PR meant to eliminate. This is a hot path, invoked once per block per tree (storage + class). Reserve capacity from the raw (unfiltered) node iterator, which has an exact size_hint since it wraps a HashMap::iter(), then extend into the presized map.
PR SummaryLow Risk Overview The prior approach built the placeholder map via This change reserves capacity from the unfiltered Reviewed by Cursor Bugbot for commit 25a877f. Bugbot is set up for automated code reviews on this repo. Configure here. |
Follow-up to #14638, which intended to fix an O(log N) rehashing hot path in
initialize_filled_tree_output_map_with_placeholders(called once per block per tree — storage trie and class trie) by preallocating the outputHashMap's capacity.That PR's fix doesn't actually work: it switched to
updated_skeleton.get_nodes().filter_map(...).collect(). Rust'sFilterMapiterator adapter always reports asize_hint()lower bound of0, because the predicate can drop every item — soHashMap'sFromIterator/extendreserves0capacity up front, and the map still grows via repeated reallocation and rehashing as elements are inserted, exactly the cost the PR meant to eliminate.This PR reserves capacity from the unfiltered node iterator instead —
updated_skeleton.get_nodes()wraps aHashMap::iter(), which is anExactSizeIterator, so itssize_hint().0is the true node count. We callHashMap::with_capacityon that count, then.extend()thefilter_map'd iterator into the presized map. This reserves an upper bound (it includesUnmodifiedSubTreenodes that get filtered out), which is a good tradeoff — a bit of extra headroom is far cheaper than the rehash cascade it avoids.No behavior change: output map contents are identical, only the allocation strategy differs.
Verified:
cargo build -p starknet_patricia— successSEED=0 cargo test -p starknet_patricia— 107 passed, 0 failedcargo clippy -p starknet_patricia --all-targets— cleanscripts/rust_fmt.sh— cleanGenerated by Claude Code