-
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, core, eth, light, trie: add trie read caching layer #18087
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.
It looks good to me, fwiw, but should be checked by @fjl aswell
f6ede58
to
cbc87ca
Compare
Comparing another branch with this pr on
both parent time and child time seems off EDIT: I used the wrong output , fixed now |
Do you get the same errors locally? |
Test
|
cbc87ca
to
47df810
Compare
Yeah, so I accidentally pushed some old version of the consensus test repo into this PR. Fixed it now. |
9c7ef0b
to
06de6d5
Compare
@holiman I've rebased on master, renamed the |
06de6d5
to
e966ebc
Compare
e966ebc
to
434dd5b
Compare
@fjl I've reworked the API so the |
Changes look much cleaner now. Let's investigate if this change also allows disabling the generation-based caching. |
Sure, but lets please do that in a followup PR. Removing the cache generations is a non negligible amount of change, after which at least a full sync is needed to validate it, which is +1 week. If this change is ok and works well, I don't think we should postpone it just to add more to it. |
This is an alternative approach to #17873.
In short, this PR introduces a read cache into the
trie.Database
(previously it was a write cache/gc only). The reason behind this is twofold:My original attempt in #17873 cached entire storage tries, and introduced a fancy way to maximize useful data across transactions and blocks. Unfortunately, we have no means to measure the memory usage of that approach, neither easily control it. Alas, although it's cleaner and faster, it was not provably immune to DoS attacks.
This current approach in this PR adds the caching layer in between the database and our internal trie (same way we did for writes/pruning). This is suboptimal because we can only cache rlp encoded nodes, requiring reparsing them every time on access. On the flipside though, it avoid the disk reads the same way my old PR did, but also guarantees memory caps. As a bonus point, the data structure used avoids GC overhead on the millions of read-cached trie nodes, so it my actually be more performant with large enough caches.
Benchmark results (purple = PR, blue = master):
Disk writes grow at the same rate as with master. The absolute value here is not relevant because I restarted the code on top of an existing chain, causing quite some blocks to be reprocessed, but different number on master and pr. The charts are after the system synced. Read wise we can see this pr saves about 75% of disk reads compared to master on mainnet (running with
--cache=2048
).Perhaps a more interesting metric is the propagated block processing time. This PR manages to cut new block times down by about 30%. You can see that the PR doesn't help old blocks that much during sync, since there's no preloading (pending transactions are the preloaders), however after sync is done, new import times are much lower than master ones. Our metrics library uses 1024 samples (blocks) for averaging, hence the couple hours it takes for the speedup to be fully visible on the charts.
Full sync benchmarks
i3.2xlarge: 8vcpu, 61 GiB mem, 1.9 TB NVMe SSD
--syncmode==full --cache=2048