core/vm: improved stack swap performance (#30249)

This PR adds the methods `Stack.swap1..16()` that faster than `Stack.swap(1..16)`. 

Co-authored-by: lmittmann <lmittmann@users.noreply.github.com>
pull/30270/head
lmittmann 1 month ago committed by GitHub
parent e9981bc6f7
commit b37ac5c102
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 90
      core/vm/instructions.go
  2. 32
      core/vm/jump_table.go
  3. 29
      core/vm/runtime/runtime_test.go
  4. 51
      core/vm/stack.go

@ -583,6 +583,86 @@ func opGas(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte
return nil, nil return nil, nil
} }
func opSwap1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.swap1()
return nil, nil
}
func opSwap2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.swap2()
return nil, nil
}
func opSwap3(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.swap3()
return nil, nil
}
func opSwap4(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.swap4()
return nil, nil
}
func opSwap5(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.swap5()
return nil, nil
}
func opSwap6(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.swap6()
return nil, nil
}
func opSwap7(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.swap7()
return nil, nil
}
func opSwap8(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.swap8()
return nil, nil
}
func opSwap9(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.swap9()
return nil, nil
}
func opSwap10(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.swap10()
return nil, nil
}
func opSwap11(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.swap11()
return nil, nil
}
func opSwap12(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.swap12()
return nil, nil
}
func opSwap13(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.swap13()
return nil, nil
}
func opSwap14(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.swap14()
return nil, nil
}
func opSwap15(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.swap15()
return nil, nil
}
func opSwap16(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.swap16()
return nil, nil
}
func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
if interpreter.readOnly { if interpreter.readOnly {
return nil, ErrWriteProtection return nil, ErrWriteProtection
@ -923,13 +1003,3 @@ func makeDup(size int64) executionFunc {
return nil, nil return nil, nil
} }
} }
// make swap instruction function
func makeSwap(size int64) executionFunc {
// switch n + 1 otherwise n would be swapped with n
size++
return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.swap(int(size))
return nil, nil
}
}

@ -892,97 +892,97 @@ func newFrontierInstructionSet() JumpTable {
maxStack: maxDupStack(16), maxStack: maxDupStack(16),
}, },
SWAP1: { SWAP1: {
execute: makeSwap(1), execute: opSwap1,
constantGas: GasFastestStep, constantGas: GasFastestStep,
minStack: minSwapStack(2), minStack: minSwapStack(2),
maxStack: maxSwapStack(2), maxStack: maxSwapStack(2),
}, },
SWAP2: { SWAP2: {
execute: makeSwap(2), execute: opSwap2,
constantGas: GasFastestStep, constantGas: GasFastestStep,
minStack: minSwapStack(3), minStack: minSwapStack(3),
maxStack: maxSwapStack(3), maxStack: maxSwapStack(3),
}, },
SWAP3: { SWAP3: {
execute: makeSwap(3), execute: opSwap3,
constantGas: GasFastestStep, constantGas: GasFastestStep,
minStack: minSwapStack(4), minStack: minSwapStack(4),
maxStack: maxSwapStack(4), maxStack: maxSwapStack(4),
}, },
SWAP4: { SWAP4: {
execute: makeSwap(4), execute: opSwap4,
constantGas: GasFastestStep, constantGas: GasFastestStep,
minStack: minSwapStack(5), minStack: minSwapStack(5),
maxStack: maxSwapStack(5), maxStack: maxSwapStack(5),
}, },
SWAP5: { SWAP5: {
execute: makeSwap(5), execute: opSwap5,
constantGas: GasFastestStep, constantGas: GasFastestStep,
minStack: minSwapStack(6), minStack: minSwapStack(6),
maxStack: maxSwapStack(6), maxStack: maxSwapStack(6),
}, },
SWAP6: { SWAP6: {
execute: makeSwap(6), execute: opSwap6,
constantGas: GasFastestStep, constantGas: GasFastestStep,
minStack: minSwapStack(7), minStack: minSwapStack(7),
maxStack: maxSwapStack(7), maxStack: maxSwapStack(7),
}, },
SWAP7: { SWAP7: {
execute: makeSwap(7), execute: opSwap7,
constantGas: GasFastestStep, constantGas: GasFastestStep,
minStack: minSwapStack(8), minStack: minSwapStack(8),
maxStack: maxSwapStack(8), maxStack: maxSwapStack(8),
}, },
SWAP8: { SWAP8: {
execute: makeSwap(8), execute: opSwap8,
constantGas: GasFastestStep, constantGas: GasFastestStep,
minStack: minSwapStack(9), minStack: minSwapStack(9),
maxStack: maxSwapStack(9), maxStack: maxSwapStack(9),
}, },
SWAP9: { SWAP9: {
execute: makeSwap(9), execute: opSwap9,
constantGas: GasFastestStep, constantGas: GasFastestStep,
minStack: minSwapStack(10), minStack: minSwapStack(10),
maxStack: maxSwapStack(10), maxStack: maxSwapStack(10),
}, },
SWAP10: { SWAP10: {
execute: makeSwap(10), execute: opSwap10,
constantGas: GasFastestStep, constantGas: GasFastestStep,
minStack: minSwapStack(11), minStack: minSwapStack(11),
maxStack: maxSwapStack(11), maxStack: maxSwapStack(11),
}, },
SWAP11: { SWAP11: {
execute: makeSwap(11), execute: opSwap11,
constantGas: GasFastestStep, constantGas: GasFastestStep,
minStack: minSwapStack(12), minStack: minSwapStack(12),
maxStack: maxSwapStack(12), maxStack: maxSwapStack(12),
}, },
SWAP12: { SWAP12: {
execute: makeSwap(12), execute: opSwap12,
constantGas: GasFastestStep, constantGas: GasFastestStep,
minStack: minSwapStack(13), minStack: minSwapStack(13),
maxStack: maxSwapStack(13), maxStack: maxSwapStack(13),
}, },
SWAP13: { SWAP13: {
execute: makeSwap(13), execute: opSwap13,
constantGas: GasFastestStep, constantGas: GasFastestStep,
minStack: minSwapStack(14), minStack: minSwapStack(14),
maxStack: maxSwapStack(14), maxStack: maxSwapStack(14),
}, },
SWAP14: { SWAP14: {
execute: makeSwap(14), execute: opSwap14,
constantGas: GasFastestStep, constantGas: GasFastestStep,
minStack: minSwapStack(15), minStack: minSwapStack(15),
maxStack: maxSwapStack(15), maxStack: maxSwapStack(15),
}, },
SWAP15: { SWAP15: {
execute: makeSwap(15), execute: opSwap15,
constantGas: GasFastestStep, constantGas: GasFastestStep,
minStack: minSwapStack(16), minStack: minSwapStack(16),
maxStack: maxSwapStack(16), maxStack: maxSwapStack(16),
}, },
SWAP16: { SWAP16: {
execute: makeSwap(16), execute: opSwap16,
constantGas: GasFastestStep, constantGas: GasFastestStep,
minStack: minSwapStack(17), minStack: minSwapStack(17),
maxStack: maxSwapStack(17), maxStack: maxSwapStack(17),

@ -212,6 +212,35 @@ func BenchmarkEVM_CREATE2_1200(bench *testing.B) {
benchmarkEVM_Create(bench, "5b5862124f80600080f5600152600056") benchmarkEVM_Create(bench, "5b5862124f80600080f5600152600056")
} }
func BenchmarkEVM_SWAP1(b *testing.B) {
// returns a contract that does n swaps (SWAP1)
swapContract := func(n uint64) []byte {
contract := []byte{
byte(vm.PUSH0), // PUSH0
byte(vm.PUSH0), // PUSH0
}
for i := uint64(0); i < n; i++ {
contract = append(contract, byte(vm.SWAP1))
}
return contract
}
state, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
contractAddr := common.BytesToAddress([]byte("contract"))
b.Run("10k", func(b *testing.B) {
contractCode := swapContract(10_000)
state.SetCode(contractAddr, contractCode)
for i := 0; i < b.N; i++ {
_, _, err := Call(contractAddr, []byte{}, &Config{State: state})
if err != nil {
b.Fatal(err)
}
}
})
}
func fakeHeader(n uint64, parentHash common.Hash) *types.Header { func fakeHeader(n uint64, parentHash common.Hash) *types.Header {
header := types.Header{ header := types.Header{
Coinbase: common.HexToAddress("0x00000000000000000000000000000000deadbeef"), Coinbase: common.HexToAddress("0x00000000000000000000000000000000deadbeef"),

@ -30,7 +30,7 @@ var stackPool = sync.Pool{
// Stack is an object for basic stack operations. Items popped to the stack are // Stack is an object for basic stack operations. Items popped to the stack are
// expected to be changed and modified. stack does not take care of adding newly // expected to be changed and modified. stack does not take care of adding newly
// initialised objects. // initialized objects.
type Stack struct { type Stack struct {
data []uint256.Int data []uint256.Int
} }
@ -64,8 +64,53 @@ func (st *Stack) len() int {
return len(st.data) return len(st.data)
} }
func (st *Stack) swap(n int) { func (st *Stack) swap1() {
st.data[st.len()-n], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-n] st.data[st.len()-2], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-2]
}
func (st *Stack) swap2() {
st.data[st.len()-3], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-3]
}
func (st *Stack) swap3() {
st.data[st.len()-4], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-4]
}
func (st *Stack) swap4() {
st.data[st.len()-5], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-5]
}
func (st *Stack) swap5() {
st.data[st.len()-6], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-6]
}
func (st *Stack) swap6() {
st.data[st.len()-7], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-7]
}
func (st *Stack) swap7() {
st.data[st.len()-8], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-8]
}
func (st *Stack) swap8() {
st.data[st.len()-9], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-9]
}
func (st *Stack) swap9() {
st.data[st.len()-10], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-10]
}
func (st *Stack) swap10() {
st.data[st.len()-11], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-11]
}
func (st *Stack) swap11() {
st.data[st.len()-12], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-12]
}
func (st *Stack) swap12() {
st.data[st.len()-13], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-13]
}
func (st *Stack) swap13() {
st.data[st.len()-14], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-14]
}
func (st *Stack) swap14() {
st.data[st.len()-15], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-15]
}
func (st *Stack) swap15() {
st.data[st.len()-16], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-16]
}
func (st *Stack) swap16() {
st.data[st.len()-17], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-17]
} }
func (st *Stack) dup(n int) { func (st *Stack) dup(n int) {

Loading…
Cancel
Save