Skip to content

multigas: use unchecked add to improve performance#644

Open
gligneul wants to merge 7 commits intomasterfrom
gligneul/nit-4668
Open

multigas: use unchecked add to improve performance#644
gligneul wants to merge 7 commits intomasterfrom
gligneul/nit-4668

Conversation

@gligneul
Copy link
Copy Markdown
Contributor

@gligneul gligneul commented Mar 31, 2026

Replacing safe/saturating adds with unchecked ones can improve performance by up to ~19% in synthetic benchmarks. We can safely replace checked adds when we know that there isn't a chance of overflow. For instance, in the EVM.run() function, a uint64 gas value won't overflow because the program would run out of gas first.

Here are some benchmark results of programs that

  • Without optmization:
$ go test -bench=BenchmarkSimpleLoop -benchtime=300x ./core/vm/runtime/
goos: linux
goarch: amd64
pkg: github.com/ethereum/go-ethereum/core/vm/runtime
cpu: AMD Ryzen 9 7900 12-Core Processor
BenchmarkSimpleLoop/staticcall-identity-100M-24                      300         351708000 ns/op        224673426 B/op   6164445 allocs/op
BenchmarkSimpleLoop/call-identity-100M-24                            300         423641326 ns/op        241626949 B/op   6711476 allocs/op
BenchmarkSimpleLoop/loop-100M-24                                     300         205678035 ns/op            3858 B/op         42 allocs/op
BenchmarkSimpleLoop/loop2-100M-24                                    300         220230885 ns/op            3859 B/op         42 allocs/op
BenchmarkSimpleLoop/call-nonexist-100M-24                            300         608773800 ns/op        149273581 B/op   3731316 allocs/op
BenchmarkSimpleLoop/call-EOA-100M-24                                 300         412351029 ns/op        95533857 B/op    2985050 allocs/op
BenchmarkSimpleLoop/call-reverting-100M-24                           300         622313948 ns/op        412718387 B/op   5715753 allocs/op
PASS
  • With optmization:
$ go test -bench=BenchmarkSimpleLoop -benchtime=300x ./core/vm/runtime/
goos: linux
goarch: amd64
pkg: github.com/ethereum/go-ethereum/core/vm/runtime
cpu: AMD Ryzen 9 7900 12-Core Processor
BenchmarkSimpleLoop/staticcall-identity-100M-24                      300         337222851 ns/op        224673593 B/op   6164445 allocs/op
BenchmarkSimpleLoop/call-identity-100M-24                            300         403063559 ns/op        241626933 B/op   6711476 allocs/op
BenchmarkSimpleLoop/loop-100M-24                                     300         183246194 ns/op            3859 B/op         42 allocs/op
BenchmarkSimpleLoop/loop2-100M-24                                    300         203989420 ns/op            3858 B/op         42 allocs/op
BenchmarkSimpleLoop/call-nonexist-100M-24                            300         521509487 ns/op        149273509 B/op   3731316 allocs/op
BenchmarkSimpleLoop/call-EOA-100M-24                                 300         333846242 ns/op        95533859 B/op    2985050 allocs/op
BenchmarkSimpleLoop/call-reverting-100M-24                           300         593545585 ns/op        412723111 B/op   5715769 allocs/op
PASS

Pulled in by OffchainLabs/nitro#4585

Replacing safe/saturating adds with unchecked ones can improve
performance by up to ~19% in synthetic benchmarks. We can safely replace
checked adds when we know that there isn't a chance of overflow. For
instance, in the EVM.run() function, a uint64 gas value won't overflow
because the program would run out of gas first.

Here are some benchmark results of programs that

* Without optmization:

```
$ go test -bench=BenchmarkSimpleLoop -benchtime=300x ./core/vm/runtime/
goos: linux
goarch: amd64
pkg: github.com/ethereum/go-ethereum/core/vm/runtime
cpu: AMD Ryzen 9 7900 12-Core Processor
BenchmarkSimpleLoop/staticcall-identity-100M-24                      300         351708000 ns/op        224673426 B/op   6164445 allocs/op
BenchmarkSimpleLoop/call-identity-100M-24                            300         423641326 ns/op        241626949 B/op   6711476 allocs/op
BenchmarkSimpleLoop/loop-100M-24                                     300         205678035 ns/op            3858 B/op         42 allocs/op
BenchmarkSimpleLoop/loop2-100M-24                                    300         220230885 ns/op            3859 B/op         42 allocs/op
BenchmarkSimpleLoop/call-nonexist-100M-24                            300         608773800 ns/op        149273581 B/op   3731316 allocs/op
BenchmarkSimpleLoop/call-EOA-100M-24                                 300         412351029 ns/op        95533857 B/op    2985050 allocs/op
BenchmarkSimpleLoop/call-reverting-100M-24                           300         622313948 ns/op        412718387 B/op   5715753 allocs/op
PASS
```

* With optmization:

```
$ go test -bench=BenchmarkSimpleLoop -benchtime=300x ./core/vm/runtime/
goos: linux
goarch: amd64
pkg: github.com/ethereum/go-ethereum/core/vm/runtime
cpu: AMD Ryzen 9 7900 12-Core Processor
BenchmarkSimpleLoop/staticcall-identity-100M-24                      300         337222851 ns/op        224673593 B/op   6164445 allocs/op
BenchmarkSimpleLoop/call-identity-100M-24                            300         403063559 ns/op        241626933 B/op   6711476 allocs/op
BenchmarkSimpleLoop/loop-100M-24                                     300         183246194 ns/op            3859 B/op         42 allocs/op
BenchmarkSimpleLoop/loop2-100M-24                                    300         203989420 ns/op            3858 B/op         42 allocs/op
BenchmarkSimpleLoop/call-nonexist-100M-24                            300         521509487 ns/op        149273509 B/op   3731316 allocs/op
BenchmarkSimpleLoop/call-EOA-100M-24                                 300         333846242 ns/op        95533859 B/op    2985050 allocs/op
BenchmarkSimpleLoop/call-reverting-100M-24                           300         593545585 ns/op        412723111 B/op   5715769 allocs/op
PASS
```
gligneul added a commit to OffchainLabs/nitro that referenced this pull request Mar 31, 2026
@gligneul gligneul requested review from MishkaRogachev and eljobe and removed request for eljobe March 31, 2026 14:56
Decrement single gas and increase multi-gas in a single function,
ensuring we don't make mistakes of incrementing multi-gas without
checking for overflow.
@gligneul gligneul marked this pull request as ready for review March 31, 2026 18:22
These functions were made specificly for the interpreter, we probably
don't want them to be used in other places.
Copy link
Copy Markdown
Contributor

@MishkaRogachev MishkaRogachev left a comment

Choose a reason for hiding this comment

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

Everything looks correct, just some nitpicks

Comment thread core/vm/gas.go
if op == SELFDESTRUCT && cost == params.SelfdestructGasEIP150 {
usedMultiGas.SaturatingIncrementInto(multigas.ResourceKindComputation, params.WarmStorageReadCostEIP2929)
usedMultiGas.SaturatingIncrementInto(multigas.ResourceKindStorageAccessWrite, cost-params.WarmStorageReadCostEIP2929)
// It is safe to call UncheckedIncrementInto because because we checked single gas.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

doubled "because"

Comment thread core/vm/gas.go
return ErrOutOfGas
}
c.Gas -= dynamicCost
// It is safe to call UncheckedAddInto because because we checked single gas.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

doubled "because"

return z, true
}

for i := 0; i < int(NumResourceKind); i++ {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Since you are editing this file anyway, maybe update all plain for loops here to range-based form: for i := range int(NumResourceKind)

@gligneul gligneul assigned gligneul and unassigned eljobe Apr 1, 2026
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.

3 participants