-
Notifications
You must be signed in to change notification settings - Fork 4.4k
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
wip: pull IndexList into crate, get_mut -> get #32423
Conversation
runtime/src/index_list.rs
Outdated
|
||
/// Move the element at the index to the end. | ||
/// The index remains the same. | ||
pub(crate) fn move_to_last(&mut self, index: Index) { |
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.
new fn
Codecov Report
@@ Coverage Diff @@
## master #32423 +/- ##
========================================
Coverage 82.0% 82.0%
========================================
Files 785 786 +1
Lines 211736 211939 +203
========================================
+ Hits 173641 173854 +213
+ Misses 38095 38085 -10 |
Looking at the code change, we still need an exclusive lock on the Maybe the issue is that diff --git a/runtime/src/read_only_accounts_cache.rs b/runtime/src/read_only_accounts_cache.rs
index 9956af64bb..cbbb1d901a 100644
--- a/runtime/src/read_only_accounts_cache.rs
+++ b/runtime/src/read_only_accounts_cache.rs
@@ -88,6 +88,7 @@ impl ReadOnlyAccountsCache {
queue.remove(entry.index);
entry.index = queue.insert_last(key);
}
+ let entry = entry.downgrade();
Some(entry.account.clone())
} |
The odd thing is that Some possibilities are that:
|
branch: It cycles through 5 'selector' values of how to do the read cache::load(). measurement of |
viewing the sum metrics per account. You can try this dashboard. |
@behzadnouri here are the top loads on the read only cache in 1 s.
Another adjustment here is we could never update the lru for the vote program, and never throw it out of the read only cache. Except, it is rewritten once per epoch and the entries in the read only cache are (Pubkey, Slot) tuples... |
@jeffwashington #32518 is an alternative which uses atomics for indices in the cache entries. It has equivalent impact as Can you also test #32518 in your setup and compare? |
been busy, but I got numbers on this last night. another variant I tried was only updating the lru on the vote account 1/10,000 of the time. (selector=6) Bottom graph is log scale of same middle graph. Bottom 2 graphs show total time (get + lru update). |
The perf difference between #32518 and this pr is pretty strange. |
The difference inside the mutex lock is Here are metrics between three version on a steady state mnb validator: being explicit about the differences between #3 and #9:
Clearly, 3 (this pr) is the fastest. |
it is interesting that |
In theory, yes, we could do something special. |
Problem
We are investigating the cost of loading accounts. We found a 100x performance improvement on account load from read cache. The hit rate on the read cache is very high. Here are examples from metrics of a running validator:
This graph shows a steady state validator using
get_mut
(write lcok) on the left, then a restart and back to steady state usingget
(using read lock) on the right. Note the y scale is log.Currently, loading from read cache requires a write lock on the
DashMap
.solana/runtime/src/read_only_accounts_cache.rs
Line 75 in a3171d3
This is because to keep a LRU, we use IndexList. The api to move an entry in the list to back requires a
remove()
followed byinsert_last()
.solana/runtime/src/read_only_accounts_cache.rs
Lines 88 to 89 in a3171d3
This process produces a new index. The new index must be set into the entry in the
DashMap
. Write locks cause more contention than read locks.get
doesn't need a write lock.We need an api on
IndexList
like:move_to_last
which does not modify the index. Then, we can get an immutable ref to the entry in theDashMap
, and thus, a read lock instead of a write lock.One option is to pull the code into our crate, remove the code we don't need, and add the new method
move_to_last
. This PR is to evaluate whether this is the best approach.We'd want to copy over all tests that make sense.
We'd want to create new test(s) for
move_to_last
.Another option is to get a change into the external project.
an alternative might be to create a vec of DashMaps, arranged by slot. We'd mod the slot to find the vec index for the dashmap that would contain an item of a given slot. The result would be more available write locks as a result of having more dashmaps. However, the lru list itself is currently still behind a single mutex since the lru list would presumably be shared across all (slot, pubkey) entries as it is today.
Other alternatives could be a different model where we were looser with strict lru usage ordering. Some kind of atomic age or something we increment and then search only occasionally to kick out older things. The previous implementation used a timestamp and searched the whole dashmap to find the strictly oldest items. We could store a slot (or a timestamp). When we need to evict (which is pretty uncommon), we could scan and get rid of the first things we find that are older than (10s, 1000 slots, whatever heuristic we came up with) and then stop when we've evicted enough. There are obviously downsides to different impls, but this read only cache is very stable in contents. It is also very conservatively sized at the moment.
Summary of Changes
Fixes #