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>
This commit is contained in:
parent
e9981bc6f7
commit
b37ac5c102
|
@ -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…
Reference in New Issue