starknet_patricia: actually pre-allocate filled-tree output map capacity#14822
starknet_patricia: actually pre-allocate filled-tree output map capacity#14822gkaempfer wants to merge 2 commits into
Conversation
PR #14638 intended to pre-allocate initialize_filled_tree_output_map_with_placeholders's HashMap to avoid O(log N) rehashing while filling the tree (once per block, per tree), but the filter_map().collect() it introduced doesn't achieve this: FilterMap::size_hint() always reports a lower bound of 0, and HashMap's Extend impl reserves capacity from that lower bound, so no pre-allocation happens. Reserve capacity from the raw node iterator (exact size_hint, since it comes from a HashMap::iter()) before filtering instead. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015cTjgteomFa2zu4xh6L2yJ
…adeoff Address Opus review feedback on the prior commit: document on the get_nodes trait method that callers rely on an exact size_hint (so future implementations don't silently regress the pre-allocation), note the deliberate over-allocation for filtered UnmodifiedSubTree nodes, and rename the local iterator binding for clarity. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015cTjgteomFa2zu4xh6L2yJ
PR SummaryLow Risk Overview
Reviewed by Cursor Bugbot for commit 1eab780. Bugbot is set up for automated code reviews on this repo. Configure here. |
Summary
Follow-up fix on top of #14638, which merged with the stated goal of pre-allocating
initialize_filled_tree_output_map_with_placeholders's outputHashMapto avoid O(log N) rehashing during tree fill — a hot path called once per block per Merkle tree (storage + class tries).That PR's implementation doesn't actually achieve the goal: it collects through a
.filter_map(...).collect()chain, butIterator::filter_map'ssize_hint()always reports a lower bound of0(verified empirically — aHashMap::iter()filtered throughfilter_mapdegrades from an exactsize_hintto(0, Some(n))).HashMap'sExtendimpl (which backs.collect()) reserves capacity based on exactly that lower bound, so the merged code reserves zero extra capacity up front and still rehashes while filling — the same cost the PR claimed to remove.Fix
size_hint()from the rawget_nodes()iterator (exact, since it comes from aHashMap::iter()) before applying.filter_map, and explicitlyHashMap::with_capacity(...)+.extend(...)instead of relying on.collect()'s internal reservation.get_nodestrait method that callers rely on this exactsize_hint, so a future implementation wrapping it in a filtering adapter doesn't silently regress the optimization again.UnmodifiedSubTreenodes that get filtered out, as a documented tradeoff against a separate counting pass.Verification
cargo build -p starknet_patricia— clean.SEED=0 cargo test -p starknet_patricia— 107 passed, 0 failed.🤖 Generated with a scheduled Claude Code review of merged PR #14638.
Generated by Claude Code