diff --git a/cmd/evm/main.go b/cmd/evm/main.go index 67447d58a..72cb1ab85 100644 --- a/cmd/evm/main.go +++ b/cmd/evm/main.go @@ -87,6 +87,10 @@ var ( Name: "verbosity", Usage: "sets the verbosity level", } + BenchFlag = cli.BoolFlag{ + Name: "bench", + Usage: "benchmark the execution", + } CreateFlag = cli.BoolFlag{ Name: "create", Usage: "indicates the action should be create rather than call", @@ -124,6 +128,7 @@ var ( func init() { app.Flags = []cli.Flag{ + BenchFlag, CreateFlag, DebugFlag, VerbosityFlag, diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go index cecbf3606..da301ff5e 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -25,6 +25,7 @@ import ( "os" goruntime "runtime" "runtime/pprof" + "testing" "time" "github.com/ethereum/go-ethereum/cmd/evm/internal/compiler" @@ -69,6 +70,33 @@ func readGenesis(genesisPath string) *core.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 { glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) 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)) } - var ( - code []byte - ret []byte - err error - ) + var code []byte codeFileFlag := ctx.GlobalString(CodeFileFlag.Name) codeFlag := ctx.GlobalString(CodeFlag.Name) @@ -203,10 +227,10 @@ func runCmd(ctx *cli.Context) error { } else { runtimeConfig.ChainConfig = params.AllEthashProtocolChanges } - tstart := time.Now() - var leftOverGas uint64 + var hexInput []byte if inputFileFlag := ctx.GlobalString(InputFileFlag.Name); inputFileFlag != "" { + var err error if hexInput, err = ioutil.ReadFile(inputFileFlag); err != nil { fmt.Printf("could not load input from file: %v\n", err) os.Exit(1) @@ -215,16 +239,24 @@ func runCmd(ctx *cli.Context) error { hexInput = []byte(ctx.GlobalString(InputFlag.Name)) } input := common.FromHex(string(bytes.TrimSpace(hexInput))) + + var execFunc func() ([]byte, uint64, error) if ctx.GlobalBool(CreateFlag.Name) { 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 { if len(code) > 0 { 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) { statedb.Commit(true) @@ -267,7 +299,7 @@ Gas used: %d `, execTime, mem.HeapObjects, mem.Alloc, mem.TotalAlloc, mem.NumGC, initialGas-leftOverGas) } if tracer == nil { - fmt.Printf("0x%x\n", ret) + fmt.Printf("0x%x\n", output) if err != nil { fmt.Printf(" error: %v\n", err) }