cmd/evm: Add --bench flag for benchmarking (#20330)

The --bench flag uses the testing.B to execute the EVM bytecode many times and get the average exeuction time out of it.
This commit is contained in:
Paweł Bylica 2019-12-18 09:43:18 +01:00 committed by Martin Holst Swende
parent c4b7fdd27e
commit 49cf000df7
2 changed files with 48 additions and 11 deletions

View File

@ -87,6 +87,10 @@ var (
Name: "verbosity", Name: "verbosity",
Usage: "sets the verbosity level", Usage: "sets the verbosity level",
} }
BenchFlag = cli.BoolFlag{
Name: "bench",
Usage: "benchmark the execution",
}
CreateFlag = cli.BoolFlag{ CreateFlag = cli.BoolFlag{
Name: "create", Name: "create",
Usage: "indicates the action should be create rather than call", Usage: "indicates the action should be create rather than call",
@ -124,6 +128,7 @@ var (
func init() { func init() {
app.Flags = []cli.Flag{ app.Flags = []cli.Flag{
BenchFlag,
CreateFlag, CreateFlag,
DebugFlag, DebugFlag,
VerbosityFlag, VerbosityFlag,

View File

@ -25,6 +25,7 @@ import (
"os" "os"
goruntime "runtime" goruntime "runtime"
"runtime/pprof" "runtime/pprof"
"testing"
"time" "time"
"github.com/ethereum/go-ethereum/cmd/evm/internal/compiler" "github.com/ethereum/go-ethereum/cmd/evm/internal/compiler"
@ -69,6 +70,33 @@ func readGenesis(genesisPath string) *core.Genesis {
return genesis return genesis
} }
func timedExec(bench bool, execFunc func() ([]byte, uint64, error)) ([]byte, uint64, time.Duration, error) {
var (
output []byte
gasLeft uint64
execTime time.Duration
err error
)
if bench {
result := testing.Benchmark(func(b *testing.B) {
for i := 0; i < b.N; i++ {
output, gasLeft, err = execFunc()
}
})
// Get the average execution time from the benchmarking result.
// There are other useful stats here that could be reported.
execTime = time.Duration(result.NsPerOp())
} else {
startTime := time.Now()
output, gasLeft, err = execFunc()
execTime = time.Since(startTime)
}
return output, gasLeft, execTime, err
}
func runCmd(ctx *cli.Context) error { func runCmd(ctx *cli.Context) error {
glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
glogger.Verbosity(log.Lvl(ctx.GlobalInt(VerbosityFlag.Name))) glogger.Verbosity(log.Lvl(ctx.GlobalInt(VerbosityFlag.Name)))
@ -116,11 +144,7 @@ func runCmd(ctx *cli.Context) error {
receiver = common.HexToAddress(ctx.GlobalString(ReceiverFlag.Name)) receiver = common.HexToAddress(ctx.GlobalString(ReceiverFlag.Name))
} }
var ( var code []byte
code []byte
ret []byte
err error
)
codeFileFlag := ctx.GlobalString(CodeFileFlag.Name) codeFileFlag := ctx.GlobalString(CodeFileFlag.Name)
codeFlag := ctx.GlobalString(CodeFlag.Name) codeFlag := ctx.GlobalString(CodeFlag.Name)
@ -203,10 +227,10 @@ func runCmd(ctx *cli.Context) error {
} else { } else {
runtimeConfig.ChainConfig = params.AllEthashProtocolChanges runtimeConfig.ChainConfig = params.AllEthashProtocolChanges
} }
tstart := time.Now()
var leftOverGas uint64
var hexInput []byte var hexInput []byte
if inputFileFlag := ctx.GlobalString(InputFileFlag.Name); inputFileFlag != "" { if inputFileFlag := ctx.GlobalString(InputFileFlag.Name); inputFileFlag != "" {
var err error
if hexInput, err = ioutil.ReadFile(inputFileFlag); err != nil { if hexInput, err = ioutil.ReadFile(inputFileFlag); err != nil {
fmt.Printf("could not load input from file: %v\n", err) fmt.Printf("could not load input from file: %v\n", err)
os.Exit(1) os.Exit(1)
@ -215,16 +239,24 @@ func runCmd(ctx *cli.Context) error {
hexInput = []byte(ctx.GlobalString(InputFlag.Name)) hexInput = []byte(ctx.GlobalString(InputFlag.Name))
} }
input := common.FromHex(string(bytes.TrimSpace(hexInput))) input := common.FromHex(string(bytes.TrimSpace(hexInput)))
var execFunc func() ([]byte, uint64, error)
if ctx.GlobalBool(CreateFlag.Name) { if ctx.GlobalBool(CreateFlag.Name) {
input = append(code, input...) input = append(code, input...)
ret, _, leftOverGas, err = runtime.Create(input, &runtimeConfig) execFunc = func() ([]byte, uint64, error) {
output, _, gasLeft, err := runtime.Create(input, &runtimeConfig)
return output, gasLeft, err
}
} else { } else {
if len(code) > 0 { if len(code) > 0 {
statedb.SetCode(receiver, code) statedb.SetCode(receiver, code)
} }
ret, leftOverGas, err = runtime.Call(receiver, input, &runtimeConfig) execFunc = func() ([]byte, uint64, error) {
return runtime.Call(receiver, input, &runtimeConfig)
}
} }
execTime := time.Since(tstart)
output, leftOverGas, execTime, err := timedExec(ctx.GlobalBool(BenchFlag.Name), execFunc)
if ctx.GlobalBool(DumpFlag.Name) { if ctx.GlobalBool(DumpFlag.Name) {
statedb.Commit(true) statedb.Commit(true)
@ -267,7 +299,7 @@ Gas used: %d
`, execTime, mem.HeapObjects, mem.Alloc, mem.TotalAlloc, mem.NumGC, initialGas-leftOverGas) `, execTime, mem.HeapObjects, mem.Alloc, mem.TotalAlloc, mem.NumGC, initialGas-leftOverGas)
} }
if tracer == nil { if tracer == nil {
fmt.Printf("0x%x\n", ret) fmt.Printf("0x%x\n", output)
if err != nil { if err != nil {
fmt.Printf(" error: %v\n", err) fmt.Printf(" error: %v\n", err)
} }