perf(precompiles): shrink per-call zone precompile lookup state - #1386
Closed
mattsse wants to merge 1 commit into
Closed
perf(precompiles): shrink per-call zone precompile lookup state#1386mattsse wants to merge 1 commit into
mattsse wants to merge 1 commit into
Conversation
alloy-evm invokes the dynamic precompile lookup on every call frame whose bytecode address misses the static map, so the zone builds a fresh DynPrecompile for each CALL into a zone precompile. Registering those addresses in the map instead is not an option: revm warms PrecompileProvider::warm_addresses at the start of every transaction, so that would turn cold CALLs into warm ones and change consensus gas. A unit test now pins that the lookup leaves the warm address set untouched. ZonePrecompileEnv carried a full CfgEnv clone even though the wrappers only read spec, enable_amsterdam_eip8037 and gas_params, and L1State spread its transaction-local state over three separately reference-counted fields. Both now sit behind a single Rc, so a lookup clones one refcount instead of copying a CfgEnv and bumping four counters. Building the env before taking the precompiles out of the EVM also drops the second CfgEnv clone per create_evm. Measured with a throwaway interleaved A/B microbenchmark (release, opt-level 3, old and new types running in one process, minimum of 2000 rounds of 2000 iterations): cloning L1State 7.2 -> 4.2 ns, cloning ZonePrecompileEnv 5.7 -> 4.4 ns. The remaining ~36 ns of PrecompilesMap::get is the Box<dyn Precompile> allocation the lookup API requires; caching the constructed precompile per address measured 6 ns slower, because the lookup must still hand back an owned DynPrecompile.
mattsse
requested review from
0xKitsune,
0xrusowsky,
klkvr and
legion2002
as code owners
September 3, 2026 20:50
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
alloy-evm runs the dynamic precompile lookup on every call frame that misses the static map, so each CALL into a zone precompile builds a fresh
DynPrecompile. Registering the addresses in the map is not an option, since revm warmswarm_addressesat the start of every transaction and that would change CALL gas; a new test pins that the lookup leaves the warm set untouched.ZonePrecompileEnvcarried a fullCfgEnvclone although the wrappers only readspec,enable_amsterdam_eip8037andgas_params, andL1Statespread its state over three refcounted fields plus a provider clone. Both now sit behind a singleRc, and building the env before taking the precompiles out of the EVM removes a secondCfgEnvclone percreate_evm.Measured with a throwaway interleaved A/B microbenchmark (release, minimum over 2000 rounds, A/A control at 0.00 ns): cloning
L1State7.2 → 4.2 ns, cloningZonePrecompileEnv5.7 → 4.4 ns. The remaining ~36 ns ofPrecompilesMap::getis the boxed precompile the lookup API requires; caching per address was tried and measured 6 ns slower. This is a few nanoseconds out of roughly 750 per call, so the value is a minimal per-call path and the pinned warm-address invariant, not a visible speedup.