-
Notifications
You must be signed in to change notification settings - Fork 20.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
cmd/evm: benchmarking via statetest
command + filter by name, index and fork
#30442
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Checked to see if there was any noticeable slowdown.
Master:
$ time yes ~/go/src/github.com/holiman/goevmlab/examples/sstore_static/sstore_test.json | head -n 1000 | go run ./cmd/evm/ statetest 2>/dev/null 1>/dev/null
real 0m3.389s
user 0m3.688s
sys 0m1.008s
This PR
$ time yes ~/go/src/github.com/holiman/goevmlab/examples/sstore_static/sstore_test.json | head -n 1000 | go run ./cmd/evm/ statetest 2>/dev/null 1>/dev/null
real 0m3.409s
user 0m3.927s
sys 0m1.042s
So 👍
Just some minor nits, otherwise lgtm
--bench
flag support to include statetest
subcommand. introduce test name, fork, index filtering flags for state test subcommand.statetest
command + filter by name, index and fork
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
cmd/evm/staterunner.go
Outdated
} else if len(matchingTests) != 1 { | ||
return fmt.Errorf("can only benchmark single state test case (more than one matching params)") | ||
} | ||
var gasUsed uint64 | ||
result := testing.Benchmark(func(b *testing.B) { | ||
for i := 0; i < b.N; i++ { | ||
test := matchingTests[0] | ||
_, _, gasUsed, _ = test.test.RunNoVerify(test.st, cfg, false, rawdb.HashScheme) | ||
} | ||
}) | ||
var stats execStats | ||
// Get the average execution time from the benchmarking result. | ||
// There are other useful stats here that could be reported. | ||
stats.time = time.Duration(result.NsPerOp()) | ||
stats.allocs = result.AllocsPerOp() | ||
stats.bytesAllocated = result.AllocedBytesPerOp() | ||
fmt.Fprintf(os.Stderr, `EVM gas used: %d | ||
execution time: %v | ||
allocations: %d | ||
allocated bytes: %d | ||
`, gasUsed, stats.time, stats.allocs, stats.bytesAllocated) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would move this into its own function and do something like this
if bench {
runBenchmark()
}
return nil
which makes it a big cleaner imo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's ok as is
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, one tiny nitpick that you could ignore if you want
cmd/evm/staterunner.go
Outdated
} else if len(matchingTests) != 1 { | ||
return fmt.Errorf("can only benchmark single state test case (more than one matching params)") | ||
} | ||
var gasUsed uint64 | ||
result := testing.Benchmark(func(b *testing.B) { | ||
for i := 0; i < b.N; i++ { | ||
test := matchingTests[0] | ||
_, _, gasUsed, _ = test.test.RunNoVerify(test.st, cfg, false, rawdb.HashScheme) | ||
} | ||
}) | ||
var stats execStats | ||
// Get the average execution time from the benchmarking result. | ||
// There are other useful stats here that could be reported. | ||
stats.time = time.Duration(result.NsPerOp()) | ||
stats.allocs = result.AllocsPerOp() | ||
stats.bytesAllocated = result.AllocedBytesPerOp() | ||
fmt.Fprintf(os.Stderr, `EVM gas used: %d | ||
execution time: %v | ||
allocations: %d | ||
allocated bytes: %d | ||
`, gasUsed, stats.time, stats.allocs, stats.bytesAllocated) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's ok as is
cmd/evm/staterunner.go
Outdated
if !bench { | ||
return nil | ||
} else if len(matchingTests) != 1 { | ||
return fmt.Errorf("can only benchmark single state test case (more than one matching params)") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why though?
Wouldn't it make more sense to put the benchmarking into the loop above, and after each test.test.Run
, you do a bench run, if so desired.
You might even consider integrating the bench stats into the results
, but now I'm just thinking aloud... ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. Benchmark every test unless otherwise-specified sounds like fine default behavior to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SO drop this whole if-clause?
Sorry -- I know I approved it earlier, and I came around to merge it now, but then I found some more things that could be improved :) |
Tried to re-run the exact test you posted from Running a separate test shows no measurable performance difference between the latest changes and master branch. |
cmd/evm/runner.go
Outdated
time time.Duration // The execution time. | ||
allocs int64 // The number of heap allocations during execution. | ||
bytesAllocated int64 // The cumulative number of bytes allocated during execution. | ||
type ExecStats struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this need to be exported? I don't see why
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I think I got confused because non-exported members of structs are not serialized in JSON so I assumed that the type also had to be exported 🤷♂️
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wait but how can StateTestResult
have a public field with a private type...
cmd/evm/staterunner.go
Outdated
if !bench { | ||
return nil | ||
} else if len(matchingTests) != 1 { | ||
return fmt.Errorf("can only benchmark single state test case (more than one matching params)") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SO drop this whole if-clause?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
9f3c9fb
to
ca8beb9
Compare
When
evm statetest --bench
is specified, benchmark the execution similarly toevm run
. It will explicitly error if there is more than one matched subtest to be executed (newstatetest
flags are added to filter which tests are ran).Breaks the state test runner API. But I figure that we are the only consumers of this API, so it's not a big problem?