diff --git a/crates/engine/tree/src/tree/payload_processor/mod.rs b/crates/engine/tree/src/tree/payload_processor/mod.rs index 3c7be7b5bd..84eee2a0a1 100644 --- a/crates/engine/tree/src/tree/payload_processor/mod.rs +++ b/crates/engine/tree/src/tree/payload_processor/mod.rs @@ -94,6 +94,17 @@ pub const SPARSE_TRIE_MAX_NODES_SHRINK_CAPACITY: usize = 1_000_000; /// 144MB. pub const SPARSE_TRIE_MAX_VALUES_SHRINK_CAPACITY: usize = 1_000_000; +/// Gas used threshold below which prewarming is skipped for small blocks. +/// +/// Based on analysis of 100 mainnet blocks (starting at block 24,425,210), reth's `new_payload` +/// winrate drops from 88% on 40-50M gas blocks to 49% on 0-10M gas blocks. The root cause is the +/// fixed overhead of spawning prewarm workers (building state providers, creating EVM instances, +/// wrapping precompiles) which exceeds execution time on small blocks. +/// +/// 20M gas is used as the threshold because ~23% of recent mainnet blocks fall under this level +/// where prewarming overhead dominates. +pub const SMALL_BLOCK_GAS_THRESHOLD: u64 = 20_000_000; + /// Type alias for [`PayloadHandle`] returned by payload processor spawn methods. type IteratorPayloadHandle = PayloadHandle< WithTxEnv, >::Recovered>, @@ -431,7 +442,8 @@ where where P: BlockReader + StateProviderFactory + StateReader + Clone + 'static, { - let skip_prewarm = self.disable_transaction_prewarming; + let skip_prewarm = self.disable_transaction_prewarming || + (env.gas_used > 0 && env.gas_used < SMALL_BLOCK_GAS_THRESHOLD); let saved_cache = self.disable_state_cache.not().then(|| self.cache_for(env.parent_hash)); @@ -972,6 +984,10 @@ pub struct ExecutionEnv { /// Used to determine parallel worker count for prewarming. /// A value of 0 indicates the count is unknown. pub transaction_count: usize, + /// Total gas used in the block. + /// Used to skip prewarming for small blocks (see [`SMALL_BLOCK_GAS_THRESHOLD`]). + /// A value of 0 indicates the gas used is unknown. + pub gas_used: u64, /// Withdrawals included in the block. /// Used to generate prefetch targets for withdrawal addresses. pub withdrawals: Option>, @@ -988,6 +1004,7 @@ where parent_hash: Default::default(), parent_state_root: Default::default(), transaction_count: 0, + gas_used: 0, withdrawals: None, } } diff --git a/crates/engine/tree/src/tree/payload_validator.rs b/crates/engine/tree/src/tree/payload_validator.rs index 096547a4a9..11b2d20c56 100644 --- a/crates/engine/tree/src/tree/payload_validator.rs +++ b/crates/engine/tree/src/tree/payload_validator.rs @@ -412,6 +412,7 @@ where parent_hash: input.parent_hash(), parent_state_root: parent_block.state_root(), transaction_count: input.transaction_count(), + gas_used: input.gas_used(), withdrawals: input.withdrawals().map(|w| w.to_vec()), }; @@ -1563,6 +1564,17 @@ impl BlockOrPayload { } } + /// Returns the total gas used by all transactions in the payload or block. + pub fn gas_used(&self) -> u64 + where + T::ExecutionData: ExecutionPayload, + { + match self { + Self::Payload(payload) => payload.gas_used(), + Self::Block(block) => block.header().gas_used(), + } + } + /// Returns the withdrawals from the payload or block. pub fn withdrawals(&self) -> Option<&[Withdrawal]> where