1
0
Fork 0
forked from forks/go-ethereum

core/vm: optimize push2 opcode (#31267)

During my benchmarks on Holesky, around 10% of all CPU time was spent in
PUSH2
```
ROUTINE ======================== github.com/ethereum/go-ethereum/core/vm.newFrontierInstructionSet.makePush.func1 in github.com/ethereum/go-ethereum/core/vm/instructions.go
    16.38s     20.35s (flat, cum) 10.31% of Total
     740ms      740ms    976:	return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
         .          .    977:		var (
      40ms       40ms    978:			codeLen = len(scope.Contract.Code)
     970ms      970ms    979:			start   = min(codeLen, int(*pc+1))
     200ms      200ms    980:			end     = min(codeLen, start+pushByteSize)
         .          .    981:		)
     670ms      2.39s    982:		a := new(uint256.Int).SetBytes(scope.Contract.Code[start:end])
         .          .    983:
         .          .    984:		// Missing bytes: pushByteSize - len(pushData)
     410ms      410ms    985:		if missing := pushByteSize - (end - start); missing > 0 {
         .          .    986:			a.Lsh(a, uint(8*missing))
         .          .    987:		}
    12.69s     14.94s    988:		scope.Stack.push2(*a)
      10ms       10ms    989:		*pc += size
     650ms      650ms    990:		return nil, nil
         .          .    991:	}
         .          .    992:}
```

Which is quite crazy. We have a handwritten encoder for PUSH1 already,
this PR adds one for PUSH2.

PUSH2 is the second most used opcode as shown here:
https://gist.github.com/shemnon/fb9b292a103abb02d98d64df6fbd35c8 since
it is used by solidity quite significantly. Its used ~20 times as much
as PUSH20 and PUSH32.

# Benchmarks

```
BenchmarkPush/makePush-14         	94196547	        12.27 ns/op	       0 B/op	       0 allocs/op
BenchmarkPush/push-14             	429976924	         2.829 ns/op	       0 B/op	       0 allocs/op
``` 

---------

Co-authored-by: jwasinger <j-wasinger@hotmail.com>
This commit is contained in:
Marius van der Wijden 2025-04-08 19:57:45 +02:00 committed by GitHub
parent 2e739fce58
commit 5cc9137c9c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 18 additions and 1 deletions

View file

@ -971,6 +971,23 @@ func opPush1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by
return nil, nil
}
// opPush2 is a specialized version of pushN
func opPush2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
var (
codeLen = uint64(len(scope.Contract.Code))
integer = new(uint256.Int)
)
if *pc+2 < codeLen {
scope.Stack.push(integer.SetBytes2(scope.Contract.Code[*pc+1 : *pc+3]))
} else if *pc+1 < codeLen {
scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc+1]) << 8))
} else {
scope.Stack.push(integer.Clear())
}
*pc += 2
return nil, nil
}
// make push instruction function
func makePush(size uint64, pushByteSize int) executionFunc {
return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {

View file

@ -631,7 +631,7 @@ func newFrontierInstructionSet() JumpTable {
maxStack: maxStack(0, 1),
},
PUSH2: {
execute: makePush(2, 2),
execute: opPush2,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),