core/vm: improve memory resize (#33056)
Some checks are pending
/ Linux Build (push) Waiting to run
/ Linux Build (arm) (push) Waiting to run
/ Keeper Build (push) Waiting to run
/ Windows Build (push) Waiting to run
/ Docker Image (push) Waiting to run

Looks like (in some very EVM specific tests) we spent a lot of time
resizing memory. If the underlying array is big enough, we can speed it 
up a bit by simply slicing the memory.

goos: linux
goarch: amd64
pkg: github.com/ethereum/go-ethereum/core/vm
cpu: Intel(R) Core(TM) Ultra 7 155U
           │ /tmp/old.txt │              /tmp/new.txt              │
           │    sec/op    │    sec/op     vs base                  │
Resize-14     6.145n ± 9%   1.854n ± 14%  -69.83% (p=0.000 n=10)

           │ /tmp/old.txt │            /tmp/new.txt             │
           │     B/op     │    B/op     vs base                 │
Resize-14      5.000 ± 0%   5.000 ± 0%       ~ (p=1.000 n=10)

           │ /tmp/old.txt │             /tmp/new.txt              │
           │  allocs/op   │ allocs/op   vs base                   │
Resize-14    0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=10) ¹

From the blocktest benchmark: 

     620ms     10.93s (flat, cum)  9.92% of Total
         .          .     80:func (m *Memory) Resize(size uint64) {
      30ms       60ms     81:	if uint64(m.Len()) < size {
     590ms     10.87s     82:		m.store = append(m.store, make([]byte, size-uint64(m.Len()))...)
         .          .     83:	}
         .          .     84:}

---------

Co-authored-by: Felix Lange <fjl@twurst.com>
This commit is contained in:
Marius van der Wijden 2025-11-26 16:50:16 +01:00 committed by GitHub
parent c55a12197e
commit 3bbf5f5b6a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 15 additions and 3 deletions

View file

@ -44,6 +44,7 @@ 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 {
clear(m.store)
m.store = m.store[:0]
m.lastGasCost = 0
memoryPool.Put(m)
@ -76,10 +77,14 @@ func (m *Memory) Set32(offset uint64, val *uint256.Int) {
val.PutUint256(m.store[offset:])
}
// Resize resizes the memory to size
// Resize grows the memory to the requested size.
func (m *Memory) Resize(size uint64) {
if uint64(m.Len()) < size {
m.store = append(m.store, make([]byte, size-uint64(m.Len()))...)
if uint64(len(m.store)) < size {
if uint64(cap(m.store)) >= size {
m.store = m.store[:size]
} else {
m.store = append(m.store, make([]byte, size-uint64(len(m.store)))...)
}
}
}

View file

@ -83,3 +83,10 @@ func TestMemoryCopy(t *testing.T) {
}
}
}
func BenchmarkResize(b *testing.B) {
memory := NewMemory()
for i := range b.N {
memory.Resize(uint64(i))
}
}