From 25a877fb987f2291aa510d27e89348027b96f772 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 15 Jul 2026 07:58:22 +0000 Subject: [PATCH] starknet_patricia: actually preallocate filled-tree output map capacity 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. --- .../patricia_merkle_tree/filled_tree/tree.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/crates/starknet_patricia/src/patricia_merkle_tree/filled_tree/tree.rs b/crates/starknet_patricia/src/patricia_merkle_tree/filled_tree/tree.rs index 31f07034621..df1f75b8b47 100644 --- a/crates/starknet_patricia/src/patricia_merkle_tree/filled_tree/tree.rs +++ b/crates/starknet_patricia/src/patricia_merkle_tree/filled_tree/tree.rs @@ -77,13 +77,16 @@ impl FilledTreeImpl { fn initialize_filled_tree_output_map_with_placeholders<'a>( updated_skeleton: &impl UpdatedSkeletonTree<'a>, ) -> HashMap>> { - updated_skeleton - .get_nodes() - .filter_map(|(index, node)| match node { - UpdatedSkeletonNode::UnmodifiedSubTree(_) => None, - _ => Some((*index, OnceLock::new())), - }) - .collect() + let nodes_iter = updated_skeleton.get_nodes(); + // `filter_map`'s `size_hint` lower bound is always 0 (the predicate can drop every + // item), so collecting through it directly would not preallocate. Reserve capacity + // from the unfiltered iterator's exact size instead, then extend into it. + let mut filled_tree_output_map = HashMap::with_capacity(nodes_iter.size_hint().0); + filled_tree_output_map.extend(nodes_iter.filter_map(|(index, node)| match node { + UpdatedSkeletonNode::UnmodifiedSubTree(_) => None, + _ => Some((*index, OnceLock::new())), + })); + filled_tree_output_map } fn initialize_leaf_output_map_with_placeholders(