Skip to content

core/vm, eth/tracers: simplify interpreter loop #32160

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

omerfirmak
Copy link
Contributor

@omerfirmak omerfirmak commented Jul 7, 2025

Closes #32082 #32083

This PR mainly does 2 things,

  1. moves all the dynamic gas and memory handling to opcode handlers to simplify the interpreter loop and reduce the number of unpredictable branches on the hot path.
  2. refactors stack to get rid of redundant implicit bound checks.

To do (1), I also had to refactor some of the tracing code as well. Mainly, I had to move dynamic gas tracing from OnOpcode callback to OnGasChange and add some stitching code to preserve the existing output.
Compared to master, this is how it performs.

omer@omer-ThinkPad-E14-Gen-3:~/Documents/go-ethereum$ benchstat /tmp/master.txt /tmp/jt.txt
goos: linux
goarch: amd64
pkg: github.com/ethereum/go-ethereum/core/vm/runtime
cpu: AMD Ryzen 7 5700U with Radeon Graphics         
                                       │ /tmp/master.txt │             /tmp/jt.txt             │
                                       │     sec/op      │   sec/op     vs base                │
SimpleLoop/staticcall-identity-100M-16       277.1m ± 0%   233.8m ± 0%  -15.62% (p=0.000 n=10)
SimpleLoop/call-identity-100M-16             331.7m ± 1%   278.5m ± 0%  -16.02% (p=0.000 n=10)
SimpleLoop/loop-100M-16                      291.8m ± 1%   195.7m ± 1%  -32.93% (p=0.000 n=10)
SimpleLoop/loop2-100M-16                     285.6m ± 0%   230.9m ± 0%  -19.14% (p=0.000 n=10)
SimpleLoop/call-nonexist-100M-16             657.9m ± 0%   577.6m ± 0%  -12.21% (p=0.000 n=10)
SimpleLoop/call-EOA-100M-16                  270.6m ± 1%   216.6m ± 0%  -19.96% (p=0.000 n=10)
SimpleLoop/call-reverting-100M-16            522.9m ± 1%   424.2m ± 1%  -18.89% (p=0.000 n=10)
geomean                                      355.2m        285.9m       -19.50%

                                       │ /tmp/master.txt  │             /tmp/jt.txt              │
                                       │       B/op       │     B/op      vs base                │
SimpleLoop/staticcall-identity-100M-16       67.95Mi ± 0%   47.04Mi ± 0%  -30.77% (p=0.000 n=10)
SimpleLoop/call-identity-100M-16             66.58Mi ± 0%   46.10Mi ± 0%  -30.76% (p=0.000 n=10)
SimpleLoop/loop-100M-16                      5.273Ki ± 0%   4.641Ki ± 3%  -12.00% (p=0.000 n=10)
SimpleLoop/loop2-100M-16                     5.273Ki ± 4%   4.894Ki ± 3%   -7.20% (p=0.000 n=10)
SimpleLoop/call-nonexist-100M-16            119.59Mi ± 0%   96.82Mi ± 0%  -19.05% (p=0.000 n=10)
SimpleLoop/call-EOA-100M-16              23332.066Ki ± 0%   4.917Ki ± 3%  -99.98% (p=0.000 n=10)
SimpleLoop/call-reverting-100M-16            163.8Mi ± 0%   141.9Mi ± 0%  -13.35% (p=0.000 n=10)
geomean                                      4.737Mi        1.175Mi       -75.19%

                                       │ /tmp/master.txt │             /tmp/jt.txt             │
                                       │    allocs/op    │  allocs/op   vs base                │
SimpleLoop/staticcall-identity-100M-16       2.740M ± 0%   2.055M ± 0%  -25.00% (p=0.000 n=10)
SimpleLoop/call-identity-100M-16             2.685M ± 0%   2.013M ± 0%  -25.00% (p=0.000 n=10)
SimpleLoop/loop-100M-16                       41.00 ± 0%    40.00 ± 0%   -2.44% (p=0.000 n=10)
SimpleLoop/loop2-100M-16                      41.00 ± 2%    41.00 ± 2%        ~ (p=1.000 n=10)
SimpleLoop/call-nonexist-100M-16             2.985M ± 0%   2.239M ± 0%  -25.00% (p=0.000 n=10)
SimpleLoop/call-EOA-100M-16               746301.00 ± 0%    42.00 ± 2%  -99.99% (p=0.000 n=10)
SimpleLoop/call-reverting-100M-16            2.858M ± 0%   2.143M ± 0%  -25.00% (p=0.000 n=10)
geomean                                      96.64k        20.19k       -79.11%

About some of the ideas that was discussed in Discord;

  1. Panics instead of errors ended up slowing down the VM noticeably (up to %30 in some cases)
  2. Moving the call to tracer to opcode handlers slowed down VM quite a lot.

@omerfirmak omerfirmak changed the title core/vm, et/tracers: move dynamic gas and memory handling to opcode handlers core/vm, eth/tracers: move dynamic gas and memory handling to opcode handlers Jul 8, 2025
@omerfirmak omerfirmak force-pushed the no-dyn-gasmem-nil-check branch from 11f25db to 3a52c0f Compare July 10, 2025 11:32
@omerfirmak omerfirmak marked this pull request as draft July 10, 2025 11:35
@omerfirmak omerfirmak changed the title core/vm, eth/tracers: move dynamic gas and memory handling to opcode handlers core/vm, eth/tracers: simplify interpreter loop Jul 10, 2025
@omerfirmak omerfirmak marked this pull request as ready for review July 10, 2025 13:16
@omerfirmak omerfirmak force-pushed the no-dyn-gasmem-nil-check branch from 3a52c0f to 8555272 Compare July 10, 2025 13:19
@omerfirmak omerfirmak force-pushed the no-dyn-gasmem-nil-check branch from 8555272 to e5b7430 Compare July 11, 2025 09:35
@omerfirmak omerfirmak requested a review from fjl as a code owner July 11, 2025 09:35
@omerfirmak omerfirmak marked this pull request as draft July 11, 2025 09:36
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.

Refactor EVM interpreter loop for better branch predictability
1 participant