Skip to content

Commit 3e9e30d

Browse files
lmittmannucwong
authored andcommitted
core/vm: reuse Memory instances (#30137)
This PR adds a sync.Pool to reuse instances of Memory in EVMInterpreter.
1 parent 5ef7176 commit 3e9e30d

File tree

4 files changed

+59
-3
lines changed

4 files changed

+59
-3
lines changed

core/vm/instructions.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -1120,14 +1120,14 @@ func opStaticCall(pc *uint64, interpreter *CVMInterpreter, scope *ScopeContext)
11201120

11211121
func opReturn(pc *uint64, interpreter *CVMInterpreter, scope *ScopeContext) ([]byte, error) {
11221122
offset, size := scope.Stack.pop(), scope.Stack.pop()
1123-
ret := scope.Memory.GetPtr(offset.Uint64(), size.Uint64())
1123+
ret := scope.Memory.GetCopy(offset.Uint64(), size.Uint64())
11241124

11251125
return ret, errStopToken
11261126
}
11271127

11281128
func opRevert(pc *uint64, interpreter *CVMInterpreter, scope *ScopeContext) ([]byte, error) {
11291129
offset, size := scope.Stack.pop(), scope.Stack.pop()
1130-
ret := scope.Memory.GetPtr(offset.Uint64(), size.Uint64())
1130+
ret := scope.Memory.GetCopy(offset.Uint64(), size.Uint64())
11311131

11321132
if interpreter.cvm.chainRules.IsNeo {
11331133
interpreter.returnData = ret // set REVERT data to return data buffer

core/vm/interpreter.go

+1
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ func (in *CVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
225225
// they are returned to the pools
226226
defer func() {
227227
returnStack(stack)
228+
mem.Free()
228229
}()
229230
contract.Input = input
230231

core/vm/memory.go

+19-1
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,17 @@ package vm
1919
import (
2020
"errors"
2121
"fmt"
22+
"sync"
2223

2324
"github.com/holiman/uint256"
2425
)
2526

27+
var memoryPool = sync.Pool{
28+
New: func() any {
29+
return &Memory{}
30+
},
31+
}
32+
2633
// Memory implements a simple memory model for the cortex virtual machine.
2734
type Memory struct {
2835
store []byte
@@ -31,7 +38,18 @@ type Memory struct {
3138

3239
// NewMemory returns a new memory memory model.
3340
func NewMemory() *Memory {
34-
return &Memory{}
41+
return memoryPool.Get().(*Memory)
42+
}
43+
44+
// Free returns the memory to the pool.
45+
func (m *Memory) Free() {
46+
// To reduce peak allocation, return only smaller memory instances to the pool.
47+
const maxBufferSize = 16 << 10
48+
if cap(m.store) <= maxBufferSize {
49+
m.store = m.store[:0]
50+
m.lastGasCost = 0
51+
memoryPool.Put(m)
52+
}
3553
}
3654

3755
// Set sets offset + size to value

core/vm/runtime/runtime_test.go

+37
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
package runtime
1818

1919
import (
20+
"encoding/binary"
2021
"math/big"
22+
"strconv"
2123
"strings"
2224
"testing"
2325

@@ -204,6 +206,41 @@ func BenchmarkCVM_CREATE2_1200(bench *testing.B) {
204206
benchmarkCVM_Create(bench, "5b5862124f80600080f5600152600056")
205207
}
206208

209+
func BenchmarkEVM_RETURN(b *testing.B) {
210+
// returns a contract that returns a zero-byte slice of len size
211+
returnContract := func(size uint64) []byte {
212+
contract := []byte{
213+
byte(vm.PUSH8), 0, 0, 0, 0, 0, 0, 0, 0, // PUSH8 0xXXXXXXXXXXXXXXXX
214+
byte(vm.PUSH0), // PUSH0
215+
byte(vm.RETURN), // RETURN
216+
}
217+
binary.BigEndian.PutUint64(contract[1:], size)
218+
return contract
219+
}
220+
221+
state, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
222+
contractAddr := common.BytesToAddress([]byte("contract"))
223+
224+
for _, n := range []uint64{1_000, 10_000, 100_000, 1_000_000} {
225+
b.Run(strconv.FormatUint(n, 10), func(b *testing.B) {
226+
b.ReportAllocs()
227+
228+
contractCode := returnContract(n)
229+
state.SetCode(contractAddr, contractCode)
230+
231+
for i := 0; i < b.N; i++ {
232+
ret, _, err := Call(contractAddr, []byte{}, &Config{State: state})
233+
if err != nil {
234+
b.Fatal(err)
235+
}
236+
if uint64(len(ret)) != n {
237+
b.Fatalf("expected return size %d, got %d", n, len(ret))
238+
}
239+
}
240+
})
241+
}
242+
}
243+
207244
func fakeHeader(n uint64, parentHash common.Hash) *types.Header {
208245
header := types.Header{
209246
Coinbase: common.HexToAddress("0x00000000000000000000000000000000deadbeef"),

0 commit comments

Comments
 (0)