-
Notifications
You must be signed in to change notification settings - Fork 844
proposal: set EnableMemory to false when tracing #502
Comments
We benchmarked most of that stuff and it did not seem really significant compared to the proving times TBH. Is there anything I'm missing in here? |
I have tested the same block mentioned in scroll-tech/go-ethereum#94 (comment) both with The proposal to reconstruct the memory based on the steps that touch it seems correct to me. We'll need to be careful for bugs but it seems doable. Here are other ideas that come to my mind:
Another note: if geth requires so much memory to hold the memory dump of each step, probably we'll face the same issue as well, so maybe it makes sense that we don't try to keep the full memory dump of each step. This could be achieved by reconstructing memory at the same time we're iterating the steps to do the |
"I wonder how much memory would be needed to succeed" I tried a 64GiB server, it also failed. |
@CPerezz agree that this time is much smaller than proving time. But they are not something that can be compared and summed. We cannot prove a 30M gas block in 13 seconds, maybe 10m is a better proving time estimation. So for a real zkrollup system, we have to setup tens of ( if not hundreds) proving server. If geth is optimized enough so it can serve traceBlock "realtime", the final cluster will be: 1 geth server + tens of proving machine. |
this can be achieved by using custom js tracers, example. |
js tracer will be slow since we need to record stack/storage/gas etc of every step. And for custom go tracer we need to modify geth code, which we should try to avoid unless we really have to cos it brings long term maintenance burden |
Another approach: ethereum/go-ethereum#24873 |
I think @pinkiebell has a point here. Should we add an issue and try to see the performance of a Go tracer first?? |
While I don't have much faith on the performance when using the js tracer to obtain the memory dumps (probably differential memory dumps), I agree that the best approach is to try it and figure out how good it performs. I can explore this and get some results. Actually we may use the internal tracer with |
I've done a small exploration on the js tracer and found out that it doesn't expose any method to get the memory length in a step; adding that method is very straight forward, but since it's not there it makes the exploration more difficult. I opened a PR in go-ethereum to add this method but they are planning on replacing the JS engine so my PR is on hold. |
This becomes a problem specially when the state is not available on disk or memory and has to be regenerated again for every tx. But if state is available the actual computing overhead shouldnt be too much. You can check that via this script |
I analyzed(traced with mem trace enabled) every tx inside the mentioned block. There are 94 txs in total, 93 of them can be traced successfully(the biggest trace is of ~300MiB size, summing all is about ~900MiB), the failed one(always makes geth OOM) is an Arbitrum Sequencer tx, having calldata of ~100KiB. In the Arbitrum contract, the calldata are copied to memory and hashed. So snapshots of mem for every step will be HUGE.. |
Now that the js tracer memory.length() method has been merged we can access the memory snapshots nicely from within the js tracer. I did some tests with a js tracer that returns memory diffs (so that a full memory snapshot can be reconstructed). Here's the tracer source and the script to test it And here are some quick benchmark results on a 32GB ram machine:
UPDATE: There seems to be a timeout in the test I did with the js tracer, which I need to investigate |
@ed255
|
Thanks. So I've added a timeout of 60m and ran the diff memory js tracer, and well, the memory consumption is stable, but the tracer has been running for 50 minutes now and hasn't finished yet. This tracer does a linear scan on the memory for each step; I guess that's not very efficient (I just implemented a simple diff algorithm). I'll continue with a few more experiments, but maybe reconstructing the memory as @lispc suggested may be the best option. It shouldn't bee too complex and it would be optimal. |
@ed255 Will you plan to do this recently? If not, we planned to start working on this issue("reconstructing the memory") probably 2-3 weeks later. |
I've done another experiment on the js tracer with a different approach. In this approach I've used this tracer which instead of diffing the memory at each step, it only reads the chunk of memory that changes based on the opcode and its arguments. So it's actually similar to the idea of reconstructing the memory, but in this case the script only reconstructs the position and length in memory that will change, and stores whatever is there, so the result is still a diff of memory for every step. This tracer when run on block So in summary I think both options are equally viable. And in either case, I think it will make sense to avoid keeping a complete memory snapshot per step, and only keep one "live" memory snapshot per call context, that evolves as we process each step. |
@ed255 That's huge news!!!!! Thanks for this exploratory work!!!! Which of both ways are you more decided for now? |
I really don't have a preference: I think both options are OK and once implemented can be equally clear and easy to understand. So I'd leave it as a decision for the person who implements the solution!
@lispc I haven't planned working on this in the short term, so feel free to take this task :) |
We have started reconstruct in Rust side. Maybe we can try it two weeks later |
Resolved via #614 |
and "reconstruct" memory in rust side by re-interprete memory related opcodes like MSTORE/MLOAD/MSTORE8/COPYCODETOMEMORY/...
The reason for this is that "EnableMemory: true" costs huge amount of geth server resource(cpu, mem, network). It becomes unviable to set EnableMemory:true.
More details: scroll-tech/go-ethereum#94 (comment)
The text was updated successfully, but these errors were encountered: