Skip to content

perf(evm): reuse EVMFrame objects to avoid 32 KB zero-init per call#418

Open
starwarfan wants to merge 2 commits intoDTVMStack:mainfrom
starwarfan:perf/reuse-evmframe
Open

perf(evm): reuse EVMFrame objects to avoid 32 KB zero-init per call#418
starwarfan wants to merge 2 commits intoDTVMStack:mainfrom
starwarfan:perf/reuse-evmframe

Conversation

@starwarfan
Copy link
Copy Markdown
Contributor

EVMFrame contains a std::array<uint256, 1024> (32 KB) that was being zero-initialized on every allocTopFrame() via vector::emplace_back(). Instead of clearing and re-constructing frames, track active frame count separately and reuse previously allocated EVMFrame objects, resetting only the necessary scalar fields (Sp, Pc, Host, etc.).

This eliminates ~1us of memset overhead per EVM execution, yielding a ~16% improvement on ERC-20 transfer benchmarks measured via evmc_tool.

1. Does this PR affect any open issues?(Y/N) and add issue references (e.g. "fix #123", "re #123".):

  • N
  • Y

2. What is the scope of this PR (e.g. component or file name):

3. Provide a description of the PR(e.g. more details, effects, motivations or doc link):

  • Affects user behaviors
  • Contains CI/CD configuration changes
  • Contains documentation changes
  • Contains experimental features
  • Performance regression: Consumes more CPU
  • Performance regression: Consumes more Memory
  • Other

4. Are there any breaking changes?(Y/N) and describe the breaking changes(e.g. more details, motivations or doc link):

  • N
  • Y

5. Are there test cases for these changes?(Y/N) select and add more details, references or doc links:

  • Unit test
  • Integration test
  • Benchmark (add benchmark stats below)
  • Manual test (add detailed scripts or steps below)
  • Other

6. Release note

None

EVMFrame contains a std::array<uint256, 1024> (32 KB) that was being
zero-initialized on every allocTopFrame() via vector::emplace_back().
Instead of clearing and re-constructing frames, track active frame count
separately and reuse previously allocated EVMFrame objects, resetting
only the necessary scalar fields (Sp, Pc, Host, etc.).

This eliminates ~1us of memset overhead per EVM execution, yielding a
~16% improvement on ERC-20 transfer benchmarks measured via evmc_tool.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 19, 2026

⚡ Performance Regression Check Results

✅ Performance Check Passed (interpreter)

Performance Benchmark Results (threshold: 25%)

Benchmark Baseline (us) Current (us) Change Status
total/main/blake2b_huff/8415nulls 1.34 1.42 +5.8% PASS
total/main/blake2b_huff/empty 0.02 0.02 +2.9% PASS
total/main/blake2b_shifts/8415nulls 9.32 9.84 +5.5% PASS
total/main/sha1_divs/5311 4.27 4.37 +2.3% PASS
total/main/sha1_divs/empty 0.05 0.05 +0.1% PASS
total/main/sha1_shifts/5311 2.24 2.33 +4.2% PASS
total/main/sha1_shifts/empty 0.03 0.03 +2.6% PASS
total/main/snailtracer/benchmark 46.64 49.34 +5.8% PASS
total/main/structarray_alloc/nfts_rank 0.91 0.93 +1.6% PASS
total/main/swap_math/insufficient_liquidity 0.00 0.00 -10.4% PASS
total/main/swap_math/received 0.00 0.00 -6.6% PASS
total/main/swap_math/spent 0.00 0.00 -8.4% PASS
total/main/weierstrudel/1 0.23 0.23 -0.3% PASS
total/main/weierstrudel/15 2.49 2.51 +0.5% PASS
total/micro/JUMPDEST_n0/empty 1.51 1.44 -4.7% PASS
total/micro/jump_around/empty 0.05 0.04 -13.7% PASS
total/micro/loop_with_many_jumpdests/empty 23.02 22.09 -4.0% PASS
total/micro/memory_grow_mload/by1 0.08 0.08 +3.4% PASS
total/micro/memory_grow_mload/by16 0.09 0.09 +0.6% PASS
total/micro/memory_grow_mload/by32 0.10 0.11 +2.4% PASS
total/micro/memory_grow_mload/nogrow 0.08 0.08 +2.8% PASS
total/micro/memory_grow_mstore/by1 0.09 0.09 +1.8% PASS
total/micro/memory_grow_mstore/by16 0.10 0.10 +0.1% PASS
total/micro/memory_grow_mstore/by32 0.11 0.11 +2.9% PASS
total/micro/memory_grow_mstore/nogrow 0.08 0.09 +3.6% PASS
total/micro/signextend/one 0.23 0.23 -0.6% PASS
total/micro/signextend/zero 0.23 0.23 -0.9% PASS
total/synth/ADD/b0 1.36 1.42 +4.9% PASS
total/synth/ADD/b1 1.21 1.16 -3.6% PASS
total/synth/ADDRESS/a0 5.97 5.81 -2.6% PASS
total/synth/ADDRESS/a1 6.08 6.04 -0.6% PASS
total/synth/AND/b0 1.27 1.32 +3.7% PASS
total/synth/AND/b1 1.06 1.13 +6.6% PASS
total/synth/BYTE/b0 4.58 4.60 +0.5% PASS
total/synth/BYTE/b1 3.67 3.70 +0.7% PASS
total/synth/CALLDATASIZE/a0 2.98 3.20 +7.3% PASS
total/synth/CALLDATASIZE/a1 3.03 2.94 -3.1% PASS
total/synth/CALLER/a0 5.98 5.85 -2.3% PASS
total/synth/CALLER/a1 6.10 6.04 -1.1% PASS
total/synth/CALLVALUE/a0 3.00 3.39 +13.3% PASS
total/synth/CALLVALUE/a1 2.99 3.10 +3.6% PASS
total/synth/CODESIZE/a0 3.47 3.61 +4.2% PASS
total/synth/CODESIZE/a1 3.49 3.54 +1.4% PASS
total/synth/DUP1/d0 0.61 0.64 +5.0% PASS
total/synth/DUP1/d1 0.65 0.68 +4.3% PASS
total/synth/DUP10/d0 0.61 0.63 +2.9% PASS
total/synth/DUP10/d1 0.61 0.62 +0.8% PASS
total/synth/DUP11/d0 0.61 0.61 -0.1% PASS
total/synth/DUP11/d1 0.61 0.62 +1.0% PASS
total/synth/DUP12/d0 0.61 0.61 +0.2% PASS
total/synth/DUP12/d1 0.61 0.62 +1.0% PASS
total/synth/DUP13/d0 0.61 0.62 +1.3% PASS
total/synth/DUP13/d1 0.61 0.62 +0.6% PASS
total/synth/DUP14/d0 0.61 0.68 +11.7% PASS
total/synth/DUP14/d1 0.61 0.62 +0.8% PASS
total/synth/DUP15/d0 0.61 0.62 +1.0% PASS
total/synth/DUP15/d1 0.61 0.62 +0.8% PASS
total/synth/DUP16/d0 0.61 0.63 +3.2% PASS
total/synth/DUP16/d1 0.61 0.62 +1.2% PASS
total/synth/DUP2/d0 0.61 0.61 +0.2% PASS
total/synth/DUP2/d1 0.61 0.62 +0.8% PASS
total/synth/DUP3/d0 0.61 0.64 +5.2% PASS
total/synth/DUP3/d1 0.61 0.62 +0.9% PASS
total/synth/DUP4/d0 0.61 0.61 +0.3% PASS
total/synth/DUP4/d1 0.61 0.62 +0.8% PASS
total/synth/DUP5/d0 0.61 0.61 +0.3% PASS
total/synth/DUP5/d1 0.61 0.62 +0.9% PASS
total/synth/DUP6/d0 0.61 0.61 -0.1% PASS
total/synth/DUP6/d1 0.61 0.62 +0.7% PASS
total/synth/DUP7/d0 0.61 0.64 +5.5% PASS
total/synth/DUP7/d1 0.61 0.62 +0.8% PASS
total/synth/DUP8/d0 0.61 0.64 +6.0% PASS
total/synth/DUP8/d1 0.61 0.62 +0.7% PASS
total/synth/DUP9/d0 0.61 0.61 +0.1% PASS
total/synth/DUP9/d1 0.61 0.62 +1.0% PASS
total/synth/EQ/b0 2.28 2.28 -0.1% PASS
total/synth/EQ/b1 1.25 1.28 +1.7% PASS
total/synth/GAS/a0 3.24 3.36 +3.8% PASS
total/synth/GAS/a1 3.27 3.27 -0.1% PASS
total/synth/GT/b0 2.22 2.18 -1.8% PASS
total/synth/GT/b1 0.96 0.98 +2.6% PASS
total/synth/ISZERO/u0 0.79 0.77 -3.3% PASS
total/synth/JUMPDEST/n0 1.51 1.36 -9.6% PASS
total/synth/LT/b0 2.17 2.19 +1.3% PASS
total/synth/LT/b1 0.96 0.98 +2.8% PASS
total/synth/MSIZE/a0 4.15 4.01 -3.3% PASS
total/synth/MSIZE/a1 4.31 4.20 -2.6% PASS
total/synth/MUL/b0 4.41 4.41 +0.1% PASS
total/synth/MUL/b1 4.66 4.66 +0.1% PASS
total/synth/NOT/u0 1.13 1.13 +0.0% PASS
total/synth/OR/b0 1.28 1.32 +3.6% PASS
total/synth/OR/b1 1.05 1.16 +9.8% PASS
total/synth/PC/a0 2.95 3.10 +5.3% PASS
total/synth/PC/a1 2.95 3.02 +2.3% PASS
total/synth/PUSH1/p0 0.98 0.96 -1.9% PASS
total/synth/PUSH1/p1 0.87 0.88 +0.9% PASS
total/synth/PUSH10/p0 1.00 0.98 -1.7% PASS
total/synth/PUSH10/p1 0.90 0.91 +1.6% PASS
total/synth/PUSH11/p0 0.99 0.97 -2.1% PASS
total/synth/PUSH11/p1 0.90 0.94 +4.1% PASS
total/synth/PUSH12/p0 0.99 0.98 -1.3% PASS
total/synth/PUSH12/p1 0.90 0.92 +1.7% PASS
total/synth/PUSH13/p0 0.99 0.98 -1.7% PASS
total/synth/PUSH13/p1 0.90 0.93 +3.5% PASS
total/synth/PUSH14/p0 0.98 0.97 -0.5% PASS
total/synth/PUSH14/p1 0.91 0.92 +1.5% PASS
total/synth/PUSH15/p0 1.00 0.98 -1.3% PASS
total/synth/PUSH15/p1 0.89 0.90 +1.8% PASS
total/synth/PUSH16/p0 1.00 0.98 -1.4% PASS
total/synth/PUSH16/p1 0.91 0.92 +1.2% PASS
total/synth/PUSH17/p0 1.00 0.99 -0.5% PASS
total/synth/PUSH17/p1 0.91 0.95 +4.3% PASS
total/synth/PUSH18/p0 1.00 0.99 -0.9% PASS
total/synth/PUSH18/p1 0.92 0.92 +0.4% PASS
total/synth/PUSH19/p0 1.00 1.00 -0.3% PASS
total/synth/PUSH19/p1 0.91 0.94 +2.8% PASS
total/synth/PUSH2/p0 0.97 0.97 -0.1% PASS
total/synth/PUSH2/p1 0.88 0.89 +1.3% PASS
total/synth/PUSH20/p0 1.00 0.99 -1.2% PASS
total/synth/PUSH20/p1 0.92 0.93 +1.2% PASS
total/synth/PUSH21/p0 1.00 0.99 -0.8% PASS
total/synth/PUSH21/p1 0.92 0.94 +3.1% PASS
total/synth/PUSH22/p0 0.99 0.99 -0.1% PASS
total/synth/PUSH22/p1 0.92 0.94 +2.0% PASS
total/synth/PUSH23/p0 1.00 0.99 -0.7% PASS
total/synth/PUSH23/p1 0.92 0.93 +0.6% PASS
total/synth/PUSH24/p0 1.00 0.99 -0.7% PASS
total/synth/PUSH24/p1 0.94 0.94 +0.4% PASS
total/synth/PUSH25/p0 1.00 1.00 -0.3% PASS
total/synth/PUSH25/p1 0.92 0.93 +1.1% PASS
total/synth/PUSH26/p0 1.00 0.99 -0.5% PASS
total/synth/PUSH26/p1 0.94 0.95 +0.4% PASS
total/synth/PUSH27/p0 1.00 1.00 -0.3% PASS
total/synth/PUSH27/p1 0.94 0.94 -0.2% PASS
total/synth/PUSH28/p0 1.00 0.99 -1.0% PASS
total/synth/PUSH28/p1 0.93 0.94 +1.4% PASS
total/synth/PUSH29/p0 1.00 0.99 -0.5% PASS
total/synth/PUSH29/p1 0.94 0.96 +2.2% PASS
total/synth/PUSH3/p0 0.99 0.96 -3.2% PASS
total/synth/PUSH3/p1 0.94 0.89 -5.1% PASS
total/synth/PUSH30/p0 0.95 0.98 +3.1% PASS
total/synth/PUSH30/p1 0.95 0.97 +2.2% PASS
total/synth/PUSH31/p0 1.00 1.00 -0.2% PASS
total/synth/PUSH31/p1 0.94 1.00 +5.6% PASS
total/synth/PUSH32/p0 1.00 1.00 -0.2% PASS
total/synth/PUSH32/p1 0.98 0.96 -2.2% PASS
total/synth/PUSH4/p0 0.99 0.97 -2.3% PASS
total/synth/PUSH4/p1 0.90 0.90 +0.2% PASS
total/synth/PUSH5/p0 0.99 0.97 -2.9% PASS
total/synth/PUSH5/p1 0.94 0.90 -4.4% PASS
total/synth/PUSH6/p0 0.99 0.96 -2.4% PASS
total/synth/PUSH6/p1 0.90 0.90 -0.1% PASS
total/synth/PUSH7/p0 1.00 0.97 -2.7% PASS
total/synth/PUSH7/p1 0.89 0.89 +0.7% PASS
total/synth/PUSH8/p0 0.99 0.98 -1.6% PASS
total/synth/PUSH8/p1 0.90 0.91 +0.7% PASS
total/synth/PUSH9/p0 1.00 0.99 -1.1% PASS
total/synth/PUSH9/p1 0.90 0.94 +5.0% PASS
total/synth/RETURNDATASIZE/a0 3.32 3.69 +11.2% PASS
total/synth/RETURNDATASIZE/a1 3.32 3.41 +2.6% PASS
total/synth/SAR/b0 3.76 3.69 -1.8% PASS
total/synth/SAR/b1 3.92 3.90 -0.5% PASS
total/synth/SGT/b0 2.19 2.19 -0.0% PASS
total/synth/SGT/b1 1.04 1.08 +3.8% PASS
total/synth/SHL/b0 2.82 2.72 -3.6% PASS
total/synth/SHL/b1 1.26 1.29 +2.4% PASS
total/synth/SHR/b0 2.75 2.82 +2.6% PASS
total/synth/SHR/b1 1.23 1.25 +2.2% PASS
total/synth/SIGNEXTEND/b0 2.87 2.98 +3.9% PASS
total/synth/SIGNEXTEND/b1 2.94 2.89 -1.4% PASS
total/synth/SLT/b0 2.22 2.19 -1.5% PASS
total/synth/SLT/b1 1.04 1.06 +2.0% PASS
total/synth/SUB/b0 1.36 1.42 +4.5% PASS
total/synth/SUB/b1 1.21 1.16 -4.1% PASS
total/synth/SWAP1/s0 1.08 1.10 +2.1% PASS
total/synth/SWAP10/s0 1.09 1.11 +1.6% PASS
total/synth/SWAP11/s0 1.08 1.11 +2.6% PASS
total/synth/SWAP12/s0 1.10 1.11 +0.9% PASS
total/synth/SWAP13/s0 1.09 1.11 +1.7% PASS
total/synth/SWAP14/s0 1.08 1.09 +0.3% PASS
total/synth/SWAP15/s0 1.08 1.10 +1.6% PASS
total/synth/SWAP16/s0 1.09 1.09 +0.6% PASS
total/synth/SWAP2/s0 1.08 1.10 +1.6% PASS
total/synth/SWAP3/s0 1.09 1.10 +1.1% PASS
total/synth/SWAP4/s0 1.09 1.10 +1.5% PASS
total/synth/SWAP5/s0 1.08 1.11 +2.2% PASS
total/synth/SWAP6/s0 1.08 1.10 +1.7% PASS
total/synth/SWAP7/s0 1.09 1.11 +1.5% PASS
total/synth/SWAP8/s0 1.09 1.11 +2.2% PASS
total/synth/SWAP9/s0 1.09 1.11 +1.5% PASS
total/synth/XOR/b0 1.01 1.11 +10.0% PASS
total/synth/XOR/b1 1.00 1.00 -0.1% PASS
total/synth/loop_v1 3.61 3.53 -2.1% PASS
total/synth/loop_v2 3.63 3.90 +7.3% PASS

Summary: 194 benchmarks, 0 regressions


✅ Performance Check Passed (multipass)

Performance Benchmark Results (threshold: 25%)

Benchmark Baseline (us) Current (us) Change Status
total/main/blake2b_huff/8415nulls 1.38 1.37 -1.0% PASS
total/main/blake2b_huff/empty 0.06 0.07 +4.6% PASS
total/main/blake2b_shifts/8415nulls 6.01 5.98 -0.6% PASS
total/main/sha1_divs/5311 2.40 2.40 +0.1% PASS
total/main/sha1_divs/empty 0.04 0.04 +0.4% PASS
total/main/sha1_shifts/5311 3.46 3.46 +0.1% PASS
total/main/sha1_shifts/empty 0.05 0.05 +0.4% PASS
total/main/snailtracer/benchmark 47.81 49.20 +2.9% PASS
total/main/structarray_alloc/nfts_rank 0.32 0.33 +3.2% PASS
total/main/swap_math/insufficient_liquidity 0.02 0.02 +2.0% PASS
total/main/swap_math/received 0.02 0.02 +2.6% PASS
total/main/swap_math/spent 0.02 0.02 +1.7% PASS
total/main/weierstrudel/1 0.30 0.30 -0.9% PASS
total/main/weierstrudel/15 2.58 2.58 -0.0% PASS
total/micro/JUMPDEST_n0/empty 0.13 0.12 -2.7% PASS
total/micro/jump_around/empty 0.55 0.54 -0.8% PASS
total/micro/loop_with_many_jumpdests/empty 1.97 1.96 -0.7% PASS
total/micro/memory_grow_mload/by1 0.18 0.18 +0.0% PASS
total/micro/memory_grow_mload/by16 0.20 0.20 -1.2% PASS
total/micro/memory_grow_mload/by32 0.21 0.20 -2.4% PASS
total/micro/memory_grow_mload/nogrow 0.18 0.18 +4.0% PASS
total/micro/memory_grow_mstore/by1 0.18 0.19 +1.3% PASS
total/micro/memory_grow_mstore/by16 0.19 0.20 +1.2% PASS
total/micro/memory_grow_mstore/by32 0.20 0.20 +0.7% PASS
total/micro/memory_grow_mstore/nogrow 0.18 0.18 +1.9% PASS
total/micro/signextend/one 0.35 0.35 -0.3% PASS
total/micro/signextend/zero 0.35 0.34 -2.4% PASS
total/synth/ADD/b0 0.01 0.01 +5.9% PASS
total/synth/ADD/b1 0.01 0.01 +3.2% PASS
total/synth/ADDRESS/a0 0.16 0.16 +0.2% PASS
total/synth/ADDRESS/a1 0.16 0.16 +0.3% PASS
total/synth/AND/b0 0.01 0.01 +6.1% PASS
total/synth/AND/b1 0.01 0.01 +3.2% PASS
total/synth/BYTE/b0 2.72 2.72 +0.0% PASS
total/synth/BYTE/b1 2.72 2.72 +0.0% PASS
total/synth/CALLDATASIZE/a0 0.09 0.09 +0.3% PASS
total/synth/CALLDATASIZE/a1 0.09 0.09 +0.5% PASS
total/synth/CALLER/a0 0.16 0.16 +0.2% PASS
total/synth/CALLER/a1 0.16 0.16 +0.2% PASS
total/synth/CALLVALUE/a0 0.26 0.26 +0.2% PASS
total/synth/CALLVALUE/a1 0.31 0.31 +0.1% PASS
total/synth/CODESIZE/a0 0.09 0.09 +0.2% PASS
total/synth/CODESIZE/a1 0.09 0.09 +0.5% PASS
total/synth/DUP1/d0 0.01 0.01 +5.9% PASS
total/synth/DUP1/d1 0.01 0.01 +3.1% PASS
total/synth/DUP10/d0 0.01 0.01 +3.2% PASS
total/synth/DUP10/d1 0.01 0.01 +3.4% PASS
total/synth/DUP11/d0 0.01 0.01 +5.8% PASS
total/synth/DUP11/d1 0.01 0.01 +3.2% PASS
total/synth/DUP12/d0 0.01 0.01 +5.0% PASS
total/synth/DUP12/d1 0.01 0.01 +3.1% PASS
total/synth/DUP13/d0 0.01 0.01 +8.0% PASS
total/synth/DUP13/d1 0.01 0.01 +3.1% PASS
total/synth/DUP14/d0 0.01 0.01 +3.1% PASS
total/synth/DUP14/d1 0.01 0.01 +3.1% PASS
total/synth/DUP15/d0 0.01 0.01 +6.9% PASS
total/synth/DUP15/d1 0.01 0.01 +3.2% PASS
total/synth/DUP16/d0 0.01 0.01 +7.5% PASS
total/synth/DUP16/d1 0.01 0.01 +3.2% PASS
total/synth/DUP2/d0 0.01 0.01 +6.5% PASS
total/synth/DUP2/d1 0.01 0.01 +3.2% PASS
total/synth/DUP3/d0 0.01 0.01 +6.3% PASS
total/synth/DUP3/d1 0.01 0.01 +3.1% PASS
total/synth/DUP4/d0 0.01 0.01 +5.4% PASS
total/synth/DUP4/d1 0.01 0.01 +3.1% PASS
total/synth/DUP5/d0 0.01 0.01 +11.9% PASS
total/synth/DUP5/d1 0.01 0.01 +3.1% PASS
total/synth/DUP6/d0 0.01 0.01 +5.9% PASS
total/synth/DUP6/d1 0.01 0.01 +3.1% PASS
total/synth/DUP7/d0 0.01 0.01 +9.2% PASS
total/synth/DUP7/d1 0.01 0.01 +3.4% PASS
total/synth/DUP8/d0 0.01 0.01 +5.0% PASS
total/synth/DUP8/d1 0.01 0.01 +3.2% PASS
total/synth/DUP9/d0 0.01 0.01 +3.7% PASS
total/synth/DUP9/d1 0.01 0.01 +3.1% PASS
total/synth/EQ/b0 0.01 0.01 +5.8% PASS
total/synth/EQ/b1 0.01 0.01 +3.2% PASS
total/synth/GAS/a0 0.53 0.53 +0.0% PASS
total/synth/GAS/a1 0.53 0.53 +0.1% PASS
total/synth/GT/b0 0.01 0.01 +6.0% PASS
total/synth/GT/b1 0.01 0.01 +3.2% PASS
total/synth/ISZERO/u0 0.01 0.01 +3.0% PASS
total/synth/JUMPDEST/n0 0.13 0.13 -0.1% PASS
total/synth/LT/b0 0.01 0.01 +6.6% PASS
total/synth/LT/b1 0.01 0.01 +3.2% PASS
total/synth/MSIZE/a0 0.01 0.01 +5.0% PASS
total/synth/MSIZE/a1 0.01 0.01 +3.6% PASS
total/synth/MUL/b0 0.01 0.01 +6.0% PASS
total/synth/MUL/b1 0.01 0.01 +3.0% PASS
total/synth/NOT/u0 0.01 0.01 +3.2% PASS
total/synth/OR/b0 0.01 0.01 +5.5% PASS
total/synth/OR/b1 0.01 0.01 +3.2% PASS
total/synth/PC/a0 0.01 0.01 +5.0% PASS
total/synth/PC/a1 0.01 0.01 +3.6% PASS
total/synth/PUSH1/p0 0.01 0.01 +8.3% PASS
total/synth/PUSH1/p1 0.01 0.01 +3.4% PASS
total/synth/PUSH10/p0 0.01 0.01 +8.7% PASS
total/synth/PUSH10/p1 0.01 0.01 +3.4% PASS
total/synth/PUSH11/p0 0.01 0.01 +8.5% PASS
total/synth/PUSH11/p1 0.01 0.01 +3.2% PASS
total/synth/PUSH12/p0 0.01 0.01 +8.3% PASS
total/synth/PUSH12/p1 0.01 0.01 +3.2% PASS
total/synth/PUSH13/p0 0.01 0.01 +8.2% PASS
total/synth/PUSH13/p1 0.01 0.01 +3.1% PASS
total/synth/PUSH14/p0 0.01 0.01 +8.3% PASS
total/synth/PUSH14/p1 0.01 0.01 +3.4% PASS
total/synth/PUSH15/p0 0.01 0.01 +8.5% PASS
total/synth/PUSH15/p1 0.01 0.01 +5.2% PASS
total/synth/PUSH16/p0 0.01 0.01 +8.1% PASS
total/synth/PUSH16/p1 0.01 0.01 +3.4% PASS
total/synth/PUSH17/p0 0.01 0.01 +8.4% PASS
total/synth/PUSH17/p1 0.01 0.01 +3.2% PASS
total/synth/PUSH18/p0 0.01 0.01 +8.1% PASS
total/synth/PUSH18/p1 0.01 0.01 +3.3% PASS
total/synth/PUSH19/p0 0.01 0.01 +8.4% PASS
total/synth/PUSH19/p1 0.01 0.01 +3.8% PASS
total/synth/PUSH2/p0 0.01 0.01 +8.6% PASS
total/synth/PUSH2/p1 0.01 0.01 +3.0% PASS
total/synth/PUSH20/p0 0.01 0.01 +8.4% PASS
total/synth/PUSH20/p1 0.01 0.01 +3.8% PASS
total/synth/PUSH21/p0 0.01 0.01 +8.5% PASS
total/synth/PUSH21/p1 0.01 0.01 +3.4% PASS
total/synth/PUSH22/p0 0.94 0.93 -1.8% PASS
total/synth/PUSH22/p1 0.93 0.93 -0.1% PASS
total/synth/PUSH23/p0 0.96 0.93 -2.8% PASS
total/synth/PUSH23/p1 0.93 0.93 +0.4% PASS
total/synth/PUSH24/p0 0.96 0.95 -0.9% PASS
total/synth/PUSH24/p1 0.92 0.95 +2.7% PASS
total/synth/PUSH25/p0 0.96 0.92 -4.0% PASS
total/synth/PUSH25/p1 0.96 0.95 -1.3% PASS
total/synth/PUSH26/p0 0.95 0.94 -1.7% PASS
total/synth/PUSH26/p1 0.94 0.96 +2.6% PASS
total/synth/PUSH27/p0 0.96 0.95 -1.2% PASS
total/synth/PUSH27/p1 0.97 0.96 -0.3% PASS
total/synth/PUSH28/p0 0.96 0.95 -1.1% PASS
total/synth/PUSH28/p1 0.94 0.98 +4.3% PASS
total/synth/PUSH29/p0 0.96 0.95 -1.3% PASS
total/synth/PUSH29/p1 0.96 0.96 -0.3% PASS
total/synth/PUSH3/p0 0.01 0.01 +8.4% PASS
total/synth/PUSH3/p1 0.01 0.01 +3.5% PASS
total/synth/PUSH30/p0 0.92 0.96 +4.0% PASS
total/synth/PUSH30/p1 0.96 0.96 -0.5% PASS
total/synth/PUSH31/p0 0.96 0.94 -2.3% PASS
total/synth/PUSH31/p1 1.00 0.95 -4.8% PASS
total/synth/PUSH32/p0 0.97 0.96 -1.2% PASS
total/synth/PUSH32/p1 0.95 0.97 +1.6% PASS
total/synth/PUSH4/p0 0.01 0.01 +7.8% PASS
total/synth/PUSH4/p1 0.01 0.01 +3.0% PASS
total/synth/PUSH5/p0 0.01 0.01 +8.4% PASS
total/synth/PUSH5/p1 0.01 0.01 +4.5% PASS
total/synth/PUSH6/p0 0.01 0.01 +8.6% PASS
total/synth/PUSH6/p1 0.01 0.01 +3.2% PASS
total/synth/PUSH7/p0 0.01 0.01 +8.4% PASS
total/synth/PUSH7/p1 0.01 0.01 +3.5% PASS
total/synth/PUSH8/p0 0.01 0.01 +11.4% PASS
total/synth/PUSH8/p1 0.01 0.01 +3.2% PASS
total/synth/PUSH9/p0 0.01 0.01 +8.3% PASS
total/synth/PUSH9/p1 0.01 0.01 +3.3% PASS
total/synth/RETURNDATASIZE/a0 1.34 1.33 -0.8% PASS
total/synth/RETURNDATASIZE/a1 1.32 1.33 +0.5% PASS
total/synth/SAR/b0 3.85 3.72 -3.2% PASS
total/synth/SAR/b1 4.13 3.96 -4.0% PASS
total/synth/SGT/b0 0.01 0.01 +6.8% PASS
total/synth/SGT/b1 0.01 0.01 +3.0% PASS
total/synth/SHL/b0 2.84 2.73 -3.6% PASS
total/synth/SHL/b1 1.29 1.30 +0.8% PASS
total/synth/SHR/b0 2.76 2.85 +3.2% PASS
total/synth/SHR/b1 1.24 1.26 +1.9% PASS
total/synth/SIGNEXTEND/b0 2.84 2.96 +4.4% PASS
total/synth/SIGNEXTEND/b1 2.95 2.98 +0.9% PASS
total/synth/SLT/b0 0.01 0.01 +6.2% PASS
total/synth/SLT/b1 0.01 0.01 +3.2% PASS
total/synth/SUB/b0 0.01 0.01 +5.8% PASS
total/synth/SUB/b1 0.01 0.01 +3.1% PASS
total/synth/SWAP1/s0 0.01 0.01 +9.2% PASS
total/synth/SWAP10/s0 0.01 0.01 +12.1% PASS
total/synth/SWAP11/s0 0.01 0.01 +12.1% PASS
total/synth/SWAP12/s0 0.01 0.01 +12.2% PASS
total/synth/SWAP13/s0 0.01 0.01 +12.0% PASS
total/synth/SWAP14/s0 0.01 0.01 +12.0% PASS
total/synth/SWAP15/s0 0.01 0.01 +12.0% PASS
total/synth/SWAP16/s0 0.01 0.01 +12.0% PASS
total/synth/SWAP2/s0 0.01 0.01 +8.9% PASS
total/synth/SWAP3/s0 0.01 0.01 +10.2% PASS
total/synth/SWAP4/s0 0.01 0.01 +11.1% PASS
total/synth/SWAP5/s0 0.01 0.01 +11.7% PASS
total/synth/SWAP6/s0 0.01 0.01 +11.8% PASS
total/synth/SWAP7/s0 0.01 0.01 +11.8% PASS
total/synth/SWAP8/s0 0.01 0.01 +12.3% PASS
total/synth/SWAP9/s0 0.01 0.01 +12.0% PASS
total/synth/XOR/b0 0.01 0.01 +5.9% PASS
total/synth/XOR/b1 0.01 0.01 +3.2% PASS
total/synth/loop_v1 1.37 1.35 -1.0% PASS
total/synth/loop_v2 1.28 1.29 +0.1% PASS

Summary: 194 benchmarks, 0 regressions


Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR optimizes the EVM interpreter’s call-frame management by reusing previously allocated EVMFrame objects instead of reconstructing them per call, avoiding repeated 32KB stack-array zero-initialization and reducing per-execution overhead.

Changes:

  • Add FrameCount to track the logically active portion of FrameStack while keeping old EVMFrame objects alive for reuse.
  • Update allocTopFrame()/freeBackFrame() and getCurFrame() to use FrameCount and selectively reset scalar fields on reuse.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
src/evm/interpreter.h Adds FrameCount, updates reset/getCurFrame semantics and documentation to support frame reuse.
src/evm/interpreter.cpp Reworks frame allocation/freeing to reuse EVMFrame objects and reset only key fields.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +271 to +279
EVMFrame &Frame = FrameStack[FrameCount];
Frame.Sp = 0;
Frame.Pc = 0;
Frame.Host = nullptr;
Frame.Memory.clear(); // keeps capacity
Frame.CallData.clear(); // keeps capacity
Frame.MTx = {};
Frame.Value = 0;
} else {
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reusing EVMFrame objects now retains per-frame Memory and CallData capacities across calls (clear() keeps capacity). Previously FrameStack.clear() destroyed frames and released these buffers. This can significantly increase steady-state RSS after a single call that expands memory a lot or has large calldata, and also keeps prior calldata contents resident longer than before. Consider adding a cap (e.g., if capacity exceeds a threshold, release/shrink) or explicitly releasing these vectors during resetForNewCall() while still reusing the fixed-size Stack array.

Copilot uses AI. Check for mistakes.
Comment on lines +268 to +283
if (FrameCount < FrameStack.size()) {
// Reuse an existing EVMFrame object – avoids zero-initializing the
// 32 KB uint256 stack array. Only reset the fields that matter.
EVMFrame &Frame = FrameStack[FrameCount];
Frame.Sp = 0;
Frame.Pc = 0;
Frame.Host = nullptr;
Frame.Memory.clear(); // keeps capacity
Frame.CallData.clear(); // keeps capacity
Frame.MTx = {};
Frame.Value = 0;
} else {
FrameStack.emplace_back();
}
EVMFrame &Frame = FrameStack[FrameCount];
++FrameCount;
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

allocTopFrame() declares EVMFrame &Frame inside the reuse branch and then immediately declares another EVMFrame &Frame after the if/else. This shadowing makes it easier to accidentally reset fields on one reference and use the other later. Consider using a single reference/pointer initialized once (and then conditionally resetting) to avoid shadowing.

Copilot uses AI. Check for mistakes.
Comment on lines 89 to 96
/// Reset state for reuse across calls. Keeps allocated EVMFrame objects
/// (and their 32 KB stack arrays) alive so that the next allocTopFrame()
/// only needs to reset a few scalar fields instead of zero-initializing
/// the entire array.
void resetForNewCall(runtime::EVMInstance *NewInst) {
Inst = NewInst;
FrameStack.clear(); // keeps vector capacity
FrameCount = 0; // logically empty, but frames stay allocated
Status = EVMC_SUCCESS;
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The updated resetForNewCall() doc comment mentions keeping EVMFrame objects/stack arrays alive, but with the new reuse logic the per-frame Memory and CallData vector capacities are also kept across calls. It would help to mention this explicitly (or document that only sizes are cleared on reuse) so callers understand the memory-retention tradeoff.

Copilot uses AI. Check for mistakes.
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.

2 participants