-
Notifications
You must be signed in to change notification settings - Fork 20.2k
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, eth/tracers: refactor structlogger + make it streamable #30806
base: master
Are you sure you want to change the base?
Conversation
3990d1c
to
c2da651
Compare
eth/tracers/logger/logger.go
Outdated
// create a log | ||
if l.writer == nil { | ||
// Non-streaming, need to copy slices. | ||
log.Memory = slices.Clone(log.Memory) |
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'm wondering if it makes more sense to write them to a buffer in this case and flush them at the end if we want to support the non-streaming case. I don't see a case in the codebase where we use the structlogs directly, only ever in a formatted/textified form
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.
The buffering is really due to them being included as items in a json-list, in the rpc response.
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 mean shouldn't we marshal them here already into a buffer and not store them as individual logs, but I guess that will break external projects using the struct logger
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.
We kind of could, but then we would have to redefine the other rpc message to not double-encode the already encoded snippets. It would allow us to have a better estimation of how much data we are accumulating, so instead of limiting for n
number of items, we could limit for a certain number of MB.
Is there any other reason for you to suggest this?
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.
Nope, basically these reasons. Will also limit the peak amount of memory, since we can free up the un-encoded data quickly and don't have to keep it around until the end of the encoding. Average memory consumption will go slightly up though (encoded bigger then not encoded)
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.
What you are suggesting makes sense though, because it simplifies a whole lot.
I have now pushed some changes in the non-streaming encoder, which is used when returning "legacy" traces in the debug api. To ensure that nothing substantial changed, this is how I tested it:
The expected response is
The only diff between this PR and master is that this omits empty 9d8
< memory: [],
17d15
< memory: [], The early encoder makes it so we don't have to copy slices explicitly, we just encode a |
6142b72
to
84783af
Compare
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, but I think @s1na should also take a look
eth/tracers/tracers_test.go
Outdated
// Create a tracer which records the number of steps | ||
var steps = 0 | ||
tracer := &tracing.Hooks{ | ||
OnOpcode: func(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { |
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 feel like at this point we are benching EVM execution :) it seems this benchmark was introduced in #23016 to test optimizations to the struct logger. What do you think about dropping it?
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.
Sgtm!
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 fixed it instead in the latest commit. It's horribly much slower though, due to the json-encoding and extra mem usage.
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.
You were not kidding. It's 10x slower.
PR:
goos: darwin
goarch: arm64
pkg: github.com/ethereum/go-ethereum/eth/tracers
BenchmarkTransactionTraceV2-11 15 74619664 ns/op 87275606 B/op 897587 allocs/op
PASS
Master:
goos: darwin
goarch: arm64
pkg: github.com/ethereum/go-ethereum/eth/tracers
BenchmarkTransactionTrace-11 163 6710361 ns/op 3798259 B/op 81648 allocs/op
PASS
|
||
// toLegacyJSON converts the structlog to json-encoded legacy form (StructLogRes). | ||
// | ||
// The differences between the structlog json and the 'legacy' json are: |
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.
Where do we use the non-legacy json format? I can't seem to find it.
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.
See my comment above, with debug tracecall
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.
go run ./cmd/geth --dev console --exec 'debug.traceCall({input: "0x600051600155602051600255"},"latest" , {"enableMemory": true})'
The updated benchmark is a lot slower than previously, since previously the json-encoding was deferred to later. Also, storing the data as json-encoded strings is larger than the raw bytes. The BenchmarkTransactionTraceV2 is thus renamed from the original
d2af996
to
f18d591
Compare
This PR refactors the structlog a bit, making it so that it can be used in a streaming mode.
The commandline usage of structlog now uses the streaming mode, leaving the non-streaming mode of operation for the eth_Call.
There are two benefits of streaming mode
Before:
After