From dd0c6d279fe88a0df364362b6bcc8725d88b4ead Mon Sep 17 00:00:00 2001 From: Georgios Konstantopoulos Date: Fri, 23 Jan 2026 10:09:19 -0800 Subject: [PATCH] revert: perf(trie): parallelize merge_ancestors_into_overlay (#21202) (#21370) --- crates/chain-state/src/deferred_trie.rs | 49 +------------------------ crates/trie/common/src/hashed_state.rs | 32 +--------------- crates/trie/common/src/updates.rs | 31 ---------------- 3 files changed, 3 insertions(+), 109 deletions(-) diff --git a/crates/chain-state/src/deferred_trie.rs b/crates/chain-state/src/deferred_trie.rs index 9755b54b99..1b4a3d43a3 100644 --- a/crates/chain-state/src/deferred_trie.rs +++ b/crates/chain-state/src/deferred_trie.rs @@ -243,53 +243,8 @@ impl DeferredTrieData { /// In normal operation, the parent always has a cached overlay and this /// function is never called. /// - /// When the `rayon` feature is enabled, uses parallel collection and merge: - /// 1. Collects ancestor data in parallel (each `wait_cloned()` may compute) - /// 2. Merges hashed state and trie updates in parallel with each other - /// 3. Uses tree reduction within each merge for O(log n) depth - #[cfg(feature = "rayon")] - fn merge_ancestors_into_overlay( - ancestors: &[Self], - sorted_hashed_state: &HashedPostStateSorted, - sorted_trie_updates: &TrieUpdatesSorted, - ) -> TrieInputSorted { - // Early exit: no ancestors means just wrap current block's data - if ancestors.is_empty() { - return TrieInputSorted::new( - Arc::new(sorted_trie_updates.clone()), - Arc::new(sorted_hashed_state.clone()), - Default::default(), - ); - } - - // Collect ancestor data, unzipping states and updates into Arc slices - let (states, updates): (Vec<_>, Vec<_>) = ancestors - .iter() - .map(|a| { - let data = a.wait_cloned(); - (data.hashed_state, data.trie_updates) - }) - .unzip(); - - // Merge state and nodes in parallel with each other using tree reduction - let (state, nodes) = rayon::join( - || { - let mut merged = HashedPostStateSorted::merge_parallel(&states); - merged.extend_ref_and_sort(sorted_hashed_state); - merged - }, - || { - let mut merged = TrieUpdatesSorted::merge_parallel(&updates); - merged.extend_ref_and_sort(sorted_trie_updates); - merged - }, - ); - - TrieInputSorted::new(Arc::new(nodes), Arc::new(state), Default::default()) - } - - /// Merge all ancestors and current block's data into a single overlay (sequential fallback). - #[cfg(not(feature = "rayon"))] + /// Iterates ancestors oldest -> newest, then extends with current block's data, + /// so later state takes precedence. fn merge_ancestors_into_overlay( ancestors: &[Self], sorted_hashed_state: &HashedPostStateSorted, diff --git a/crates/trie/common/src/hashed_state.rs b/crates/trie/common/src/hashed_state.rs index 3273e65829..315bda49a4 100644 --- a/crates/trie/common/src/hashed_state.rs +++ b/crates/trie/common/src/hashed_state.rs @@ -6,7 +6,7 @@ use crate::{ utils::{extend_sorted_vec, kway_merge_sorted}, KeyHasher, MultiProofTargets, Nibbles, }; -use alloc::{borrow::Cow, sync::Arc, vec::Vec}; +use alloc::{borrow::Cow, vec::Vec}; use alloy_primitives::{ keccak256, map::{hash_map, B256Map, HashMap, HashSet}, @@ -710,36 +710,6 @@ impl HashedPostStateSorted { self.accounts.clear(); self.storages.clear(); } - - /// Parallel batch-merge sorted hashed post states. Slice is **oldest to newest**. - /// - /// This is more efficient than sequential `extend_ref` calls when merging many states, - /// as it processes all states in parallel with tree reduction using divide-and-conquer. - #[cfg(feature = "rayon")] - pub fn merge_parallel(states: &[Arc]) -> Self { - fn parallel_merge_tree(states: &[Arc]) -> HashedPostStateSorted { - match states.len() { - 0 => HashedPostStateSorted::default(), - 1 => states[0].as_ref().clone(), - 2 => { - let mut acc = states[0].as_ref().clone(); - acc.extend_ref_and_sort(&states[1]); - acc - } - n => { - let mid = n / 2; - let (mut left, right) = rayon::join( - || parallel_merge_tree(&states[..mid]), - || parallel_merge_tree(&states[mid..]), - ); - left.extend_ref_and_sort(&right); - left - } - } - } - - parallel_merge_tree(states) - } } impl AsRef for HashedPostStateSorted { diff --git a/crates/trie/common/src/updates.rs b/crates/trie/common/src/updates.rs index 0155e0e484..2698510808 100644 --- a/crates/trie/common/src/updates.rs +++ b/crates/trie/common/src/updates.rs @@ -4,7 +4,6 @@ use crate::{ }; use alloc::{ collections::{btree_map::BTreeMap, btree_set::BTreeSet}, - sync::Arc, vec::Vec, }; use alloy_primitives::{ @@ -698,36 +697,6 @@ impl TrieUpdatesSorted { Self { account_nodes, storage_tries }.into() } - - /// Parallel batch-merge sorted trie updates. Slice is **oldest to newest**. - /// - /// This is more efficient than sequential `extend_ref` calls when merging many updates, - /// as it processes all updates in parallel with tree reduction using divide-and-conquer. - #[cfg(feature = "rayon")] - pub fn merge_parallel(updates: &[Arc]) -> Self { - fn parallel_merge_tree(updates: &[Arc]) -> TrieUpdatesSorted { - match updates.len() { - 0 => TrieUpdatesSorted::default(), - 1 => updates[0].as_ref().clone(), - 2 => { - let mut acc = updates[0].as_ref().clone(); - acc.extend_ref_and_sort(&updates[1]); - acc - } - n => { - let mid = n / 2; - let (mut left, right) = rayon::join( - || parallel_merge_tree(&updates[..mid]), - || parallel_merge_tree(&updates[mid..]), - ); - left.extend_ref_and_sort(&right); - left - } - } - } - - parallel_merge_tree(updates) - } } impl AsRef for TrieUpdatesSorted {