Skip to content

evmonly: run dependent blocks under Block-STM when their chains leave parallelism - #4393

Draft
suryuhh wants to merge 2 commits into
sei-protocol:devin/1789943071-stack-4-parallel-occfrom
suryuhh:executor/block-stm
Draft

suryuhh wants to merge 2 commits into
sei-protocol:devin/1789943071-stack-4-parallel-occfrom
suryuhh:executor/block-stm

Conversation

@suryuhh

@suryuhh suryuhh commented Sep 30, 2026

Copy link
Copy Markdown

The dependency fallback in #4392 runs dependent blocks sequentially. That helps when transactions form one long chain, but blocks with many short, independent chains still have parallel work available. This change uses the same sample to estimate chain length and runs Block-STM when those chains can execute concurrently. Blocks dominated by one chain stay sequential; low-dependency blocks stay on OCC. Until the first change lands upstream, this draft's comparison includes both changes.

Against #4273 at 11431679 on a 16-vCPU AMD EPYC host, eight recorded Ethereum windows improved from 3,303 to 9,397 tx/s, with 19% more CPU per transaction. A mixed 2,000-transaction block improved from 13,595 to 27,834 tx/s, with 28% more CPU per transaction. Pooled Sei traffic gained little beyond the simpler fallback, and conflict-free ERC-20 transfers were 2% slower. The inputs, commands, and full results are in suryuhh/sei-chain#2.

Differential tests compare this scheduler with sequential execution on 160 seeded blocks at 2, 4, and 16 workers, including nonce chains, cross-contract dependencies, reverts, and gas changes; the EVM-only package passes under -race. This is a larger consensus-sensitive change than the fallback and trades CPU for throughput, so it needs separate correctness and resource review.

executeBlockOCC speculates every transaction and then hands every result that read a lower write to the serial frontier in validateBlockSTM, which reruns them one at a time with a parallel validation pass between reruns. On a block where many transactions chain through shared state that is slower than executing the block in order.

The executor now speculates 256 of the block's transactions, in 16 runs of 16 consecutive transactions spaced evenly from its first transaction to its last (the whole block when it holds at most 256), so that a block whose dependent transactions sit in one part of it is measured as a whole. It measures from their read and write sets, in block order, how many read a key a lower one wrote and how long the longest such chain is, and runs the block on the sequential path when at least one in six does. Otherwise it speculates the transactions between the runs and continues on the frontier, keeping the results it already has. Fee credits to the coinbase are commutative deltas outside the write sets: they chain nothing between transactions that only pay fees, and a later read of the coinbase balance or account depends on every earlier non-zero credit, as it does in the frontier's validation. The decision uses only the executed state accesses, not calldata, so many calls to one function that each write the caller's own slot stay parallel. Blocks routed this way are counted as fallbacks with reason dependent.
…arallelism

A block whose sampled transactions read what lower ones wrote leaves the frontier. Most such blocks still hold many independent chains, and the sequential path gives that parallelism away. They now run under a Block-STM phase (occ_bstm.go): a multi-version memory and scheduler in which a transaction that reads an estimate waits for its writer instead of rerunning against a prefix it cannot use, with its final results handed to the frontier for validation against the exact prefix.

The route uses the dependencies measured on the speculative results, not calldata: Block-STM when the longest chain leaves at least 4 transactions per link (48 when at least three in eight transactions are chained, where each link costs an abort or a wait), the sequential path otherwise, and the frontier when fewer than one in 64 transactions read a lower write. The thresholds are set for the sample spread through the block, which sees fewer reads of lower writes and shorter chains than the block holds. A differential test runs 160 adverse and randomized blocks under Block-STM and on the chosen path at 2, 4 and 16 workers and requires results identical to the sequential path.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant