core/vm: reuse Memory instances (#30137)
This PR adds a sync.Pool to reuse instances of Memory in EVMInterpreter.
This commit is contained in:
parent
3b48b16290
commit
fc88cea648
@ -871,14 +871,14 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
|
|||||||
|
|
||||||
func opReturn(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
func opReturn(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
offset, size := scope.Stack.pop(), scope.Stack.pop()
|
offset, size := scope.Stack.pop(), scope.Stack.pop()
|
||||||
ret := scope.Memory.GetPtr(offset.Uint64(), size.Uint64())
|
ret := scope.Memory.GetCopy(offset.Uint64(), size.Uint64())
|
||||||
|
|
||||||
return ret, errStopToken
|
return ret, errStopToken
|
||||||
}
|
}
|
||||||
|
|
||||||
func opRevert(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
func opRevert(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||||
offset, size := scope.Stack.pop(), scope.Stack.pop()
|
offset, size := scope.Stack.pop(), scope.Stack.pop()
|
||||||
ret := scope.Memory.GetPtr(offset.Uint64(), size.Uint64())
|
ret := scope.Memory.GetCopy(offset.Uint64(), size.Uint64())
|
||||||
|
|
||||||
interpreter.returnData = ret
|
interpreter.returnData = ret
|
||||||
return ret, ErrExecutionReverted
|
return ret, ErrExecutionReverted
|
||||||
|
@ -198,6 +198,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
|||||||
// they are returned to the pools
|
// they are returned to the pools
|
||||||
defer func() {
|
defer func() {
|
||||||
returnStack(stack)
|
returnStack(stack)
|
||||||
|
mem.Free()
|
||||||
}()
|
}()
|
||||||
contract.Input = input
|
contract.Input = input
|
||||||
|
|
||||||
|
@ -17,9 +17,17 @@
|
|||||||
package vm
|
package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var memoryPool = sync.Pool{
|
||||||
|
New: func() any {
|
||||||
|
return &Memory{}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
// Memory implements a simple memory model for the ethereum virtual machine.
|
// Memory implements a simple memory model for the ethereum virtual machine.
|
||||||
type Memory struct {
|
type Memory struct {
|
||||||
store []byte
|
store []byte
|
||||||
@ -28,7 +36,18 @@ type Memory struct {
|
|||||||
|
|
||||||
// NewMemory returns a new memory model.
|
// NewMemory returns a new memory model.
|
||||||
func NewMemory() *Memory {
|
func NewMemory() *Memory {
|
||||||
return &Memory{}
|
return memoryPool.Get().(*Memory)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free returns the memory to the pool.
|
||||||
|
func (m *Memory) Free() {
|
||||||
|
// To reduce peak allocation, return only smaller memory instances to the pool.
|
||||||
|
const maxBufferSize = 16 << 10
|
||||||
|
if cap(m.store) <= maxBufferSize {
|
||||||
|
m.store = m.store[:0]
|
||||||
|
m.lastGasCost = 0
|
||||||
|
memoryPool.Put(m)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set sets offset + size to value
|
// Set sets offset + size to value
|
||||||
|
@ -17,9 +17,11 @@
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -241,6 +243,41 @@ func BenchmarkEVM_SWAP1(b *testing.B) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkEVM_RETURN(b *testing.B) {
|
||||||
|
// returns a contract that returns a zero-byte slice of len size
|
||||||
|
returnContract := func(size uint64) []byte {
|
||||||
|
contract := []byte{
|
||||||
|
byte(vm.PUSH8), 0, 0, 0, 0, 0, 0, 0, 0, // PUSH8 0xXXXXXXXXXXXXXXXX
|
||||||
|
byte(vm.PUSH0), // PUSH0
|
||||||
|
byte(vm.RETURN), // RETURN
|
||||||
|
}
|
||||||
|
binary.BigEndian.PutUint64(contract[1:], size)
|
||||||
|
return contract
|
||||||
|
}
|
||||||
|
|
||||||
|
state, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
|
||||||
|
contractAddr := common.BytesToAddress([]byte("contract"))
|
||||||
|
|
||||||
|
for _, n := range []uint64{1_000, 10_000, 100_000, 1_000_000} {
|
||||||
|
b.Run(strconv.FormatUint(n, 10), func(b *testing.B) {
|
||||||
|
b.ReportAllocs()
|
||||||
|
|
||||||
|
contractCode := returnContract(n)
|
||||||
|
state.SetCode(contractAddr, contractCode)
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
ret, _, err := Call(contractAddr, []byte{}, &Config{State: state})
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
if uint64(len(ret)) != n {
|
||||||
|
b.Fatalf("expected return size %d, got %d", n, len(ret))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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"),
|
||||||
|
Loading…
Reference in New Issue
Block a user