Skip to content

Commit 2956937

Browse files
holimankaralabe
andauthored
core/vm: less allocations for various call variants (#21222)
* core/vm/runtime/tests: add more benchmarks * core/vm: initial work on improving alloc count for calls to precompiles name old time/op new time/op delta SimpleLoop/identity-precompile-10M-6 117ms ±75% 43ms ± 1% -63.09% (p=0.008 n=5+5) SimpleLoop/loop-10M-6 79.6ms ± 4% 70.5ms ± 1% -11.42% (p=0.008 n=5+5) name old alloc/op new alloc/op delta SimpleLoop/identity-precompile-10M-6 24.4MB ± 0% 4.9MB ± 0% -79.94% (p=0.008 n=5+5) SimpleLoop/loop-10M-6 13.2kB ± 0% 13.2kB ± 0% ~ (p=0.357 n=5+5) name old allocs/op new allocs/op delta SimpleLoop/identity-precompile-10M-6 382k ± 0% 153k ± 0% -59.99% (p=0.000 n=5+4) SimpleLoop/loop-10M-6 40.0 ± 0% 40.0 ± 0% ~ (all equal) * core/vm: don't allocate big.int for touch name old time/op new time/op delta SimpleLoop/identity-precompile-10M-6 43.3ms ± 1% 42.4ms ± 7% ~ (p=0.151 n=5+5) SimpleLoop/loop-10M-6 70.5ms ± 1% 76.7ms ± 1% +8.67% (p=0.008 n=5+5) name old alloc/op new alloc/op delta SimpleLoop/identity-precompile-10M-6 4.90MB ± 0% 2.46MB ± 0% -49.83% (p=0.008 n=5+5) SimpleLoop/loop-10M-6 13.2kB ± 0% 13.2kB ± 1% ~ (p=0.571 n=5+5) name old allocs/op new allocs/op delta SimpleLoop/identity-precompile-10M-6 153k ± 0% 76k ± 0% -49.98% (p=0.029 n=4+4) SimpleLoop/loop-10M-6 40.0 ± 0% 40.0 ± 0% ~ (all equal) * core/vm: reduce allocs in staticcall name old time/op new time/op delta SimpleLoop/identity-precompile-10M-6 42.4ms ± 7% 37.5ms ± 6% -11.68% (p=0.008 n=5+5) SimpleLoop/loop-10M-6 76.7ms ± 1% 69.1ms ± 1% -9.82% (p=0.008 n=5+5) name old alloc/op new alloc/op delta SimpleLoop/identity-precompile-10M-6 2.46MB ± 0% 0.02MB ± 0% -99.35% (p=0.008 n=5+5) SimpleLoop/loop-10M-6 13.2kB ± 1% 13.2kB ± 0% ~ (p=0.143 n=5+5) name old allocs/op new allocs/op delta SimpleLoop/identity-precompile-10M-6 76.4k ± 0% 0.1k ± 0% ~ (p=0.079 n=4+5) SimpleLoop/loop-10M-6 40.0 ± 0% 40.0 ± 0% ~ (all equal) * trie: better use of hasher keccakState * core/state/statedb: reduce allocations in getDeletedStateObject * core/vm: reduce allocations in all call derivates * core/vm: reduce allocations in call variants - Make returnstack `uint32` - Use a `sync.Pool` of `stack`s * core/vm: fix tests * core/vm: goimports * core/vm: tracer fix + staticcall gas fix * core/vm: add back snapshot to staticcall * core/vm: review concerns + make returnstack pooled + enable returndata in traces * core/vm: fix some test tracer method signatures * core/vm: run gencodec, minor comment polish Co-authored-by: Péter Szilágyi <[email protected]>
1 parent 240d185 commit 2956937

21 files changed

+450
-191
lines changed

cmd/evm/internal/t8ntool/flags.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ var (
3838
Name: "trace.nostack",
3939
Usage: "Disable stack output in traces",
4040
}
41+
TraceDisableReturnDataFlag = cli.BoolFlag{
42+
Name: "trace.noreturndata",
43+
Usage: "Disable return data output in traces",
44+
}
4145
OutputAllocFlag = cli.StringFlag{
4246
Name: "output.alloc",
4347
Usage: "Determines where to put the `alloc` of the post-state.\n" +

cmd/evm/internal/t8ntool/transition.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,10 @@ func Main(ctx *cli.Context) error {
8383
if ctx.Bool(TraceFlag.Name) {
8484
// Configure the EVM logger
8585
logConfig := &vm.LogConfig{
86-
DisableStack: ctx.Bool(TraceDisableStackFlag.Name),
87-
DisableMemory: ctx.Bool(TraceDisableMemoryFlag.Name),
88-
Debug: true,
86+
DisableStack: ctx.Bool(TraceDisableStackFlag.Name),
87+
DisableMemory: ctx.Bool(TraceDisableMemoryFlag.Name),
88+
DisableReturnData: ctx.Bool(TraceDisableReturnDataFlag.Name),
89+
Debug: true,
8990
}
9091
var prevFile *os.File
9192
// This one closes the last file

cmd/evm/main.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,14 @@ var (
121121
Name: "nostack",
122122
Usage: "disable stack output",
123123
}
124+
DisableStorageFlag = cli.BoolFlag{
125+
Name: "nostorage",
126+
Usage: "disable storage output",
127+
}
128+
DisableReturnDataFlag = cli.BoolFlag{
129+
Name: "noreturndata",
130+
Usage: "disable return data output",
131+
}
124132
EVMInterpreterFlag = cli.StringFlag{
125133
Name: "vm.evm",
126134
Usage: "External EVM configuration (default = built-in interpreter)",
@@ -137,6 +145,7 @@ var stateTransitionCommand = cli.Command{
137145
t8ntool.TraceFlag,
138146
t8ntool.TraceDisableMemoryFlag,
139147
t8ntool.TraceDisableStackFlag,
148+
t8ntool.TraceDisableReturnDataFlag,
140149
t8ntool.OutputAllocFlag,
141150
t8ntool.OutputResultFlag,
142151
t8ntool.InputAllocFlag,
@@ -172,6 +181,8 @@ func init() {
172181
ReceiverFlag,
173182
DisableMemoryFlag,
174183
DisableStackFlag,
184+
DisableStorageFlag,
185+
DisableReturnDataFlag,
175186
EVMInterpreterFlag,
176187
}
177188
app.Commands = []cli.Command{

cmd/evm/runner.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,11 @@ func runCmd(ctx *cli.Context) error {
108108
glogger.Verbosity(log.Lvl(ctx.GlobalInt(VerbosityFlag.Name)))
109109
log.Root().SetHandler(glogger)
110110
logconfig := &vm.LogConfig{
111-
DisableMemory: ctx.GlobalBool(DisableMemoryFlag.Name),
112-
DisableStack: ctx.GlobalBool(DisableStackFlag.Name),
113-
Debug: ctx.GlobalBool(DebugFlag.Name),
111+
DisableMemory: ctx.GlobalBool(DisableMemoryFlag.Name),
112+
DisableStack: ctx.GlobalBool(DisableStackFlag.Name),
113+
DisableStorage: ctx.GlobalBool(DisableStorageFlag.Name),
114+
DisableReturnData: ctx.GlobalBool(DisableReturnDataFlag.Name),
115+
Debug: ctx.GlobalBool(DebugFlag.Name),
114116
}
115117

116118
var (

cmd/evm/staterunner.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,10 @@ func stateTestCmd(ctx *cli.Context) error {
5959

6060
// Configure the EVM logger
6161
config := &vm.LogConfig{
62-
DisableMemory: ctx.GlobalBool(DisableMemoryFlag.Name),
63-
DisableStack: ctx.GlobalBool(DisableStackFlag.Name),
62+
DisableMemory: ctx.GlobalBool(DisableMemoryFlag.Name),
63+
DisableStack: ctx.GlobalBool(DisableStackFlag.Name),
64+
DisableStorage: ctx.GlobalBool(DisableStorageFlag.Name),
65+
DisableReturnData: ctx.GlobalBool(DisableReturnDataFlag.Name),
6466
}
6567
var (
6668
tracer vm.Tracer

core/state/statedb.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -505,7 +505,7 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
505505
}
506506
// If no live objects are available, attempt to use snapshots
507507
var (
508-
data Account
508+
data *Account
509509
err error
510510
)
511511
if s.snap != nil {
@@ -517,11 +517,15 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
517517
if acc == nil {
518518
return nil
519519
}
520-
data.Nonce, data.Balance, data.CodeHash = acc.Nonce, acc.Balance, acc.CodeHash
520+
data = &Account{
521+
Nonce: acc.Nonce,
522+
Balance: acc.Balance,
523+
CodeHash: acc.CodeHash,
524+
Root: common.BytesToHash(acc.Root),
525+
}
521526
if len(data.CodeHash) == 0 {
522527
data.CodeHash = emptyCodeHash
523528
}
524-
data.Root = common.BytesToHash(acc.Root)
525529
if data.Root == (common.Hash{}) {
526530
data.Root = emptyRoot
527531
}
@@ -540,13 +544,14 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
540544
if len(enc) == 0 {
541545
return nil
542546
}
543-
if err := rlp.DecodeBytes(enc, &data); err != nil {
547+
data = new(Account)
548+
if err := rlp.DecodeBytes(enc, data); err != nil {
544549
log.Error("Failed to decode state object", "addr", addr, "err", err)
545550
return nil
546551
}
547552
}
548553
// Insert into the live set
549-
obj := newObject(s, addr, data)
554+
obj := newObject(s, addr, *data)
550555
s.setStateObject(obj)
551556
return obj
552557
}

core/vm/contracts.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -102,12 +102,18 @@ var PrecompiledContractsYoloV1 = map[common.Address]PrecompiledContract{
102102
}
103103

104104
// RunPrecompiledContract runs and evaluates the output of a precompiled contract.
105-
func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contract) (ret []byte, err error) {
106-
gas := p.RequiredGas(input)
107-
if contract.UseGas(gas) {
108-
return p.Run(input)
105+
// It returns
106+
// - the returned bytes,
107+
// - the _remaining_ gas,
108+
// - any error that occurred
109+
func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64) (ret []byte, remainingGas uint64, err error) {
110+
gasCost := p.RequiredGas(input)
111+
if suppliedGas < gasCost {
112+
return nil, 0, ErrOutOfGas
109113
}
110-
return nil, ErrOutOfGas
114+
suppliedGas -= gasCost
115+
output, err := p.Run(input)
116+
return output, suppliedGas, err
111117
}
112118

113119
// ECRECOVER implemented as a native contract.
@@ -197,6 +203,7 @@ func (c *dataCopy) Run(in []byte) ([]byte, error) {
197203
type bigModExp struct{}
198204

199205
var (
206+
big0 = big.NewInt(0)
200207
big1 = big.NewInt(1)
201208
big4 = big.NewInt(4)
202209
big8 = big.NewInt(8)

core/vm/contracts_test.go

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
"encoding/json"
2222
"fmt"
2323
"io/ioutil"
24-
"math/big"
2524
"testing"
2625
"time"
2726

@@ -72,10 +71,9 @@ var blake2FMalformedInputTests = []precompiledFailureTest{
7271
func testPrecompiled(addr string, test precompiledTest, t *testing.T) {
7372
p := allPrecompiles[common.HexToAddress(addr)]
7473
in := common.Hex2Bytes(test.Input)
75-
contract := NewContract(AccountRef(common.HexToAddress("1337")),
76-
nil, new(big.Int), p.RequiredGas(in))
77-
t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, contract.Gas), func(t *testing.T) {
78-
if res, err := RunPrecompiledContract(p, in, contract); err != nil {
74+
gas := p.RequiredGas(in)
75+
t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) {
76+
if res, _, err := RunPrecompiledContract(p, in, gas); err != nil {
7977
t.Error(err)
8078
} else if common.Bytes2Hex(res) != test.Expected {
8179
t.Errorf("Expected %v, got %v", test.Expected, common.Bytes2Hex(res))
@@ -91,10 +89,10 @@ func testPrecompiled(addr string, test precompiledTest, t *testing.T) {
9189
func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) {
9290
p := allPrecompiles[common.HexToAddress(addr)]
9391
in := common.Hex2Bytes(test.Input)
94-
contract := NewContract(AccountRef(common.HexToAddress("1337")),
95-
nil, new(big.Int), p.RequiredGas(in)-1)
96-
t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, contract.Gas), func(t *testing.T) {
97-
_, err := RunPrecompiledContract(p, in, contract)
92+
gas := p.RequiredGas(in) - 1
93+
94+
t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) {
95+
_, _, err := RunPrecompiledContract(p, in, gas)
9896
if err.Error() != "out of gas" {
9997
t.Errorf("Expected error [out of gas], got [%v]", err)
10098
}
@@ -109,11 +107,9 @@ func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) {
109107
func testPrecompiledFailure(addr string, test precompiledFailureTest, t *testing.T) {
110108
p := allPrecompiles[common.HexToAddress(addr)]
111109
in := common.Hex2Bytes(test.Input)
112-
contract := NewContract(AccountRef(common.HexToAddress("31337")),
113-
nil, new(big.Int), p.RequiredGas(in))
114-
110+
gas := p.RequiredGas(in)
115111
t.Run(test.Name, func(t *testing.T) {
116-
_, err := RunPrecompiledContract(p, in, contract)
112+
_, _, err := RunPrecompiledContract(p, in, gas)
117113
if err.Error() != test.ExpectedError {
118114
t.Errorf("Expected error [%v], got [%v]", test.ExpectedError, err)
119115
}
@@ -132,23 +128,20 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) {
132128
p := allPrecompiles[common.HexToAddress(addr)]
133129
in := common.Hex2Bytes(test.Input)
134130
reqGas := p.RequiredGas(in)
135-
contract := NewContract(AccountRef(common.HexToAddress("1337")),
136-
nil, new(big.Int), reqGas)
137131

138132
var (
139133
res []byte
140134
err error
141135
data = make([]byte, len(in))
142136
)
143137

144-
bench.Run(fmt.Sprintf("%s-Gas=%d", test.Name, contract.Gas), func(bench *testing.B) {
138+
bench.Run(fmt.Sprintf("%s-Gas=%d", test.Name, reqGas), func(bench *testing.B) {
145139
bench.ReportAllocs()
146140
start := time.Now().Nanosecond()
147141
bench.ResetTimer()
148142
for i := 0; i < bench.N; i++ {
149-
contract.Gas = reqGas
150143
copy(data, in)
151-
res, err = RunPrecompiledContract(p, data, contract)
144+
res, _, err = RunPrecompiledContract(p, data, reqGas)
152145
}
153146
bench.StopTimer()
154147
elapsed := float64(time.Now().Nanosecond() - start)

0 commit comments

Comments
 (0)