-
Notifications
You must be signed in to change notification settings - Fork 5.7k
Stop caching sysvars, instead load them ahead of time #21108
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1006,6 +1006,8 @@ pub struct Bank { | |
| vote_only_bank: bool, | ||
|
|
||
| pub cost_tracker: RwLock<CostTracker>, | ||
|
|
||
| sysvar_cache: RwLock<Vec<(Pubkey, Vec<u8>)>>, | ||
| } | ||
|
|
||
| impl Default for BlockhashQueue { | ||
|
|
@@ -1145,6 +1147,7 @@ impl Bank { | |
| freeze_started: AtomicBool::default(), | ||
| vote_only_bank: false, | ||
| cost_tracker: RwLock::<CostTracker>::default(), | ||
| sysvar_cache: RwLock::new(Vec::new()), | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -1254,6 +1257,7 @@ impl Bank { | |
| bank.update_rent(); | ||
| bank.update_epoch_schedule(); | ||
| bank.update_recent_blockhashes(); | ||
| bank.fill_sysvar_cache(); | ||
| bank | ||
| } | ||
|
|
||
|
|
@@ -1402,6 +1406,7 @@ impl Bank { | |
| )), | ||
| freeze_started: AtomicBool::new(false), | ||
| cost_tracker: RwLock::new(CostTracker::default()), | ||
| sysvar_cache: RwLock::new(Vec::new()), | ||
| }; | ||
|
|
||
| datapoint_info!( | ||
|
|
@@ -1458,6 +1463,7 @@ impl Bank { | |
| new.update_stake_history(Some(parent_epoch)); | ||
| new.update_clock(Some(parent_epoch)); | ||
| new.update_fees(); | ||
| new.fill_sysvar_cache(); | ||
|
|
||
| return new; | ||
| } | ||
|
|
@@ -1473,6 +1479,7 @@ impl Bank { | |
| new.update_stake_history(Some(parent_epoch)); | ||
| new.update_clock(Some(parent_epoch)); | ||
| new.update_fees(); | ||
| new.fill_sysvar_cache(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. here back-ref: #30158 |
||
| new | ||
| } | ||
|
|
||
|
|
@@ -1589,6 +1596,7 @@ impl Bank { | |
| freeze_started: AtomicBool::new(fields.hash != Hash::default()), | ||
| vote_only_bank: false, | ||
| cost_tracker: RwLock::new(CostTracker::default()), | ||
| sysvar_cache: RwLock::new(Vec::new()), | ||
| }; | ||
| bank.finish_init( | ||
| genesis_config, | ||
|
|
@@ -1767,6 +1775,14 @@ impl Bank { | |
| } | ||
|
|
||
| self.store_account_and_update_capitalization(pubkey, &new_account); | ||
|
|
||
| // Update the entry in the cache | ||
| let mut sysvar_cache = self.sysvar_cache.write().unwrap(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. any idea of performance difference with this and the current code?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nope, but good point. Shall I add a performance metric for it and let it run on a test validator?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we don't need this to be generic, we update all the sysvars during bank init, maybe add all the relevant sysvars in one action outside of the rw lock?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure what you mean by "outside" of the
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In @sakridge I'm also wondering why in general we update the sysvars during bank creation, why not create bank-specific updated ones, cache them, use the cache to get all sysvars, and then later store them back during bank freeze.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That could be an option. I think it was just more straightforward to store them in accountsdb and load it like other accounts. It also improves as accountsdb improves with in-memory caching and flushing to disk and things like that.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we open a separate issue for that and handle it in a different PR? @sakridge Coming back to the question of the performance: It might be hard to measure the time spent there directly, but we could count how often it used to load the accounts and how often it updates them now. And then at least have that as a comparison.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok. Yea if it's difficult then it's in the noise and we don't need to worry about it. Just wondering what the cost is relative to other bank creation functions are. I wrote this benchmark that just creates banks: #20361 |
||
| if let Some(position) = sysvar_cache.iter().position(|(id, _data)| id == pubkey) { | ||
| sysvar_cache[position].1 = new_account.data().to_vec(); | ||
| } else { | ||
| sysvar_cache.push((*pubkey, new_account.data().to_vec())); | ||
| } | ||
| } | ||
|
|
||
| fn inherit_specially_retained_account_fields( | ||
|
|
@@ -1904,6 +1920,17 @@ impl Bank { | |
| }); | ||
| } | ||
|
|
||
| fn fill_sysvar_cache(&mut self) { | ||
| let mut sysvar_cache = self.sysvar_cache.write().unwrap(); | ||
| for id in sysvar::ALL_IDS.iter() { | ||
| if !sysvar_cache.iter().any(|(key, _data)| key == id) { | ||
| if let Some(account) = self.get_account_with_fixed_root(id) { | ||
| sysvar_cache.push((*id, account.data().to_vec())); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| pub fn get_slot_history(&self) -> SlotHistory { | ||
| from_account(&self.get_account(&sysvar::slot_history::id()).unwrap()).unwrap() | ||
| } | ||
|
|
@@ -3858,8 +3885,7 @@ impl Bank { | |
| compute_budget, | ||
| compute_meter, | ||
| &mut timings.details, | ||
| self.rc.accounts.clone(), | ||
| &self.ancestors, | ||
| &*self.sysvar_cache.read().unwrap(), | ||
| blockhash, | ||
| lamports_per_signature, | ||
| ); | ||
|
|
||
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.
Here, we are saving the accounts to the db in the "update" calls, and then immediately reloading them to populate the cache. Each of these operations requires an accountsdb lock. Can we instead combine these into a more efficient operation?
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.
fill_sysvar_cache()only tries to load them if they are not found in the cache already. So the fill happens after all sysvar updates deliberately.if !sysvar_cache.iter().any(|(key, _data)| key == id) {So the inefficiency I can see here is:
sysvar_cacheupdate_sysvar_account()call reacquires the locksThere 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.
Yeah, that locking...
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 agree that it probably can be optimized. But that is not a change specific to this PR. So same question as above:
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 is specific because the cache is adding a lot ore locks, but yeah, we can optimize that out in another pr