Skip to content

Commit 29d5209

Browse files
MariusVanDerWijdenjwasinger
authored andcommitted
core/vm: optimize push2 opcode (ethereum#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 <[email protected]>
1 parent 75aa1fd commit 29d5209

File tree

2 files changed

+18
-1
lines changed

2 files changed

+18
-1
lines changed

core/vm/instructions.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -971,6 +971,23 @@ func opPush1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by
971971
return nil, nil
972972
}
973973

974+
// opPush2 is a specialized version of pushN
975+
func opPush2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
976+
var (
977+
codeLen = uint64(len(scope.Contract.Code))
978+
integer = new(uint256.Int)
979+
)
980+
if *pc+2 < codeLen {
981+
scope.Stack.push(integer.SetBytes2(scope.Contract.Code[*pc+1 : *pc+3]))
982+
} else if *pc+1 < codeLen {
983+
scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc+1]) << 8))
984+
} else {
985+
scope.Stack.push(integer.Clear())
986+
}
987+
*pc += 2
988+
return nil, nil
989+
}
990+
974991
// make push instruction function
975992
func makePush(size uint64, pushByteSize int) executionFunc {
976993
return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {

core/vm/jump_table.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -631,7 +631,7 @@ func newFrontierInstructionSet() JumpTable {
631631
maxStack: maxStack(0, 1),
632632
},
633633
PUSH2: {
634-
execute: makePush(2, 2),
634+
execute: opPush2,
635635
constantGas: GasFastestStep,
636636
minStack: minStack(0, 1),
637637
maxStack: maxStack(0, 1),

0 commit comments

Comments
 (0)