cmd/evm: don't reuse state between evm benchmark iterations. ensure benchmark errors are captured and printed to output.

This commit is contained in:
Jared Wasinger 2024-11-21 19:44:00 +07:00
parent d42d45046c
commit 095b466434
2 changed files with 39 additions and 19 deletions

View file

@ -82,24 +82,34 @@ type execStats struct {
GasUsed uint64 `json:"gasUsed"` // the amount of gas used during execution GasUsed uint64 `json:"gasUsed"` // the amount of gas used during execution
} }
func timedExec(bench bool, execFunc func() ([]byte, uint64, error)) ([]byte, execStats, error) { func timedExec(bench bool, execFunc func() ([]byte, uint64, error)) (output []byte, stats execStats, execErr error, benchErr error) {
if bench { if bench {
// Do one warm-up run // Do one warm-up run
output, gasUsed, err := execFunc() var gasUsed uint64
output, gasUsed, execErr = execFunc()
var benchErr error
testing.Init()
result := testing.Benchmark(func(b *testing.B) { result := testing.Benchmark(func(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
haveOutput, haveGasUsed, haveErr := execFunc() haveOutput, haveGasUsed, haveErr := execFunc()
if !bytes.Equal(haveOutput, output) { if !bytes.Equal(haveOutput, output) {
b.Fatalf("output differs, have\n%x\nwant%x\n", haveOutput, output) benchErr = fmt.Errorf("output differs, have\n%x\nwant %x\n", haveOutput, output)
b.FailNow()
} }
if haveGasUsed != gasUsed { if haveGasUsed != gasUsed {
b.Fatalf("gas differs, have %v want%v", haveGasUsed, gasUsed) benchErr = fmt.Errorf("gas differs, have %v want%v", haveGasUsed, gasUsed)
b.FailNow()
} }
if haveErr != err { if haveErr != execErr {
b.Fatalf("err differs, have %v want%v", haveErr, err) benchErr = fmt.Errorf("err differs, have %v want %v", haveErr, execErr)
b.FailNow()
} }
} }
}) })
if benchErr != nil {
return nil, execStats{}, nil, benchErr
}
// Get the average execution time from the benchmarking result. // Get the average execution time from the benchmarking result.
// There are other useful stats here that could be reported. // There are other useful stats here that could be reported.
stats := execStats{ stats := execStats{
@ -108,7 +118,7 @@ func timedExec(bench bool, execFunc func() ([]byte, uint64, error)) ([]byte, exe
BytesAllocated: result.AllocedBytesPerOp(), BytesAllocated: result.AllocedBytesPerOp(),
GasUsed: gasUsed, GasUsed: gasUsed,
} }
return output, stats, err return output, stats, execErr, nil
} }
var memStatsBefore, memStatsAfter goruntime.MemStats var memStatsBefore, memStatsAfter goruntime.MemStats
goruntime.ReadMemStats(&memStatsBefore) goruntime.ReadMemStats(&memStatsBefore)
@ -116,13 +126,13 @@ func timedExec(bench bool, execFunc func() ([]byte, uint64, error)) ([]byte, exe
output, gasUsed, err := execFunc() output, gasUsed, err := execFunc()
duration := time.Since(t0) duration := time.Since(t0)
goruntime.ReadMemStats(&memStatsAfter) goruntime.ReadMemStats(&memStatsAfter)
stats := execStats{ stats = execStats{
Time: duration, Time: duration,
Allocs: int64(memStatsAfter.Mallocs - memStatsBefore.Mallocs), Allocs: int64(memStatsAfter.Mallocs - memStatsBefore.Mallocs),
BytesAllocated: int64(memStatsAfter.TotalAlloc - memStatsBefore.TotalAlloc), BytesAllocated: int64(memStatsAfter.TotalAlloc - memStatsBefore.TotalAlloc),
GasUsed: gasUsed, GasUsed: gasUsed,
} }
return output, stats, err return output, stats, err, nil
} }
func runCmd(ctx *cli.Context) error { func runCmd(ctx *cli.Context) error {
@ -137,7 +147,7 @@ func runCmd(ctx *cli.Context) error {
var ( var (
tracer *tracing.Hooks tracer *tracing.Hooks
debugLogger *logger.StructLogger debugLogger *logger.StructLogger
statedb *state.StateDB prestate *state.StateDB
chainConfig *params.ChainConfig chainConfig *params.ChainConfig
sender = common.BytesToAddress([]byte("sender")) sender = common.BytesToAddress([]byte("sender"))
receiver = common.BytesToAddress([]byte("receiver")) receiver = common.BytesToAddress([]byte("receiver"))
@ -174,7 +184,7 @@ func runCmd(ctx *cli.Context) error {
defer triedb.Close() defer triedb.Close()
genesis := genesisConfig.MustCommit(db, triedb) genesis := genesisConfig.MustCommit(db, triedb)
sdb := state.NewDatabase(triedb, nil) sdb := state.NewDatabase(triedb, nil)
statedb, _ = state.New(genesis.Root(), sdb) prestate, _ = state.New(genesis.Root(), sdb)
chainConfig = genesisConfig.Config chainConfig = genesisConfig.Config
if ctx.String(SenderFlag.Name) != "" { if ctx.String(SenderFlag.Name) != "" {
@ -231,7 +241,7 @@ func runCmd(ctx *cli.Context) error {
} }
runtimeConfig := runtime.Config{ runtimeConfig := runtime.Config{
Origin: sender, Origin: sender,
State: statedb, State: prestate,
GasLimit: initialGas, GasLimit: initialGas,
GasPrice: flags.GlobalBig(ctx, PriceFlag.Name), GasPrice: flags.GlobalBig(ctx, PriceFlag.Name),
Value: flags.GlobalBig(ctx, ValueFlag.Name), Value: flags.GlobalBig(ctx, ValueFlag.Name),
@ -274,24 +284,32 @@ func runCmd(ctx *cli.Context) error {
if ctx.Bool(CreateFlag.Name) { if ctx.Bool(CreateFlag.Name) {
input = append(code, input...) input = append(code, input...)
execFunc = func() ([]byte, uint64, error) { execFunc = func() ([]byte, uint64, error) {
// don't mutate the state!
runtimeConfig.State = prestate.Copy()
output, _, gasLeft, err := runtime.Create(input, &runtimeConfig) output, _, gasLeft, err := runtime.Create(input, &runtimeConfig)
return output, gasLeft, err return output, gasLeft, err
} }
} else { } else {
if len(code) > 0 { if len(code) > 0 {
statedb.SetCode(receiver, code) prestate.SetCode(receiver, code)
} }
execFunc = func() ([]byte, uint64, error) { execFunc = func() ([]byte, uint64, error) {
// don't mutate the state!
runtimeConfig.State = prestate.Copy()
output, gasLeft, err := runtime.Call(receiver, input, &runtimeConfig) output, gasLeft, err := runtime.Call(receiver, input, &runtimeConfig)
return output, initialGas - gasLeft, err return output, initialGas - gasLeft, err
} }
} }
bench := ctx.Bool(BenchFlag.Name) bench := ctx.Bool(BenchFlag.Name)
output, stats, err := timedExec(bench, execFunc) output, stats, execErr, benchErr := timedExec(bench, execFunc)
if benchErr != nil {
fmt.Printf("benchmarking execution failed: %v\n", benchErr)
return benchErr
}
if ctx.Bool(DumpFlag.Name) { if ctx.Bool(DumpFlag.Name) {
root, err := statedb.Commit(genesisConfig.Number, true) root, err := runtimeConfig.State.Commit(genesisConfig.Number, true)
if err != nil { if err != nil {
fmt.Printf("Failed to commit changes %v\n", err) fmt.Printf("Failed to commit changes %v\n", err)
return err return err
@ -310,7 +328,7 @@ func runCmd(ctx *cli.Context) error {
logger.WriteTrace(os.Stderr, debugLogger.StructLogs()) logger.WriteTrace(os.Stderr, debugLogger.StructLogs())
} }
fmt.Fprintln(os.Stderr, "#### LOGS ####") fmt.Fprintln(os.Stderr, "#### LOGS ####")
logger.WriteLogs(os.Stderr, statedb.Logs()) logger.WriteLogs(os.Stderr, prestate.Logs())
} }
if bench || ctx.Bool(StatDumpFlag.Name) { if bench || ctx.Bool(StatDumpFlag.Name) {
@ -322,8 +340,8 @@ allocated bytes: %d
} }
if tracer == nil { if tracer == nil {
fmt.Printf("%#x\n", output) fmt.Printf("%#x\n", output)
if err != nil { if execErr != nil {
fmt.Printf(" error: %v\n", err) fmt.Printf(" error: %v\n", execErr)
} }
} }

View file

@ -177,7 +177,9 @@ func runStateTest(ctx *cli.Context, fname string, cfg vm.Config, dump bool, benc
} }
}) })
if bench { if bench {
_, stats, _ := timedExec(true, func() ([]byte, uint64, error) { // TODO: verify that each bench exec didn't produce a different result than the first run
// ..
_, stats, _, _ := timedExec(true, func() ([]byte, uint64, error) {
_, _, gasUsed, _ := test.test.RunNoVerify(test.st, cfg, false, rawdb.HashScheme) _, _, gasUsed, _ := test.test.RunNoVerify(test.st, cfg, false, rawdb.HashScheme)
return nil, gasUsed, nil return nil, gasUsed, nil
}) })