From d8fb7ce5116946337a0f8c71b0e6fad66bba0aac Mon Sep 17 00:00:00 2001 From: "Jeff Washington (jwash)" Date: Tue, 30 Nov 2021 11:36:46 -0600 Subject: [PATCH] AcctIdx: upsert avoids unnecessary allocation (#21488) --- runtime/src/accounts_index.rs | 84 +++++++++++++++++++++++++---------- 1 file changed, 60 insertions(+), 24 deletions(-) diff --git a/runtime/src/accounts_index.rs b/runtime/src/accounts_index.rs index 71aaa84e79..3b52cce3f7 100644 --- a/runtime/src/accounts_index.rs +++ b/runtime/src/accounts_index.rs @@ -1694,6 +1694,9 @@ impl AccountsIndex { reclaims: &mut SlotList, previous_slot_entry_was_cached: bool, ) { + // vast majority of updates are to item already in accounts index, so store as raw to avoid unnecessary allocations + let store_raw = true; + // We don't atomically update both primary index and secondary index together. // This certainly creates a small time window with inconsistent state across the two indexes. // However, this is acceptable because: @@ -1706,7 +1709,7 @@ impl AccountsIndex { // So, what the accounts_index sees alone is sufficient as a source of truth for other non-scan // account operations. let new_item = - PreAllocatedAccountMapEntry::new(slot, account_info, &self.storage.storage, false); + PreAllocatedAccountMapEntry::new(slot, account_info, &self.storage.storage, store_raw); let map = &self.account_maps[self.bin_calculator.bin_from_pubkey(pubkey)]; { @@ -2883,36 +2886,69 @@ pub mod tests { assert_eq!(num, 1); } + fn get_pre_allocated( + slot: Slot, + account_info: T, + storage: &Arc>, + store_raw: bool, + to_raw_first: bool, + ) -> PreAllocatedAccountMapEntry { + let entry = PreAllocatedAccountMapEntry::new(slot, account_info, storage, store_raw); + + if to_raw_first { + // convert to raw + let (slot2, account_info2) = entry.into(); + // recreate using extracted raw + PreAllocatedAccountMapEntry::new(slot2, account_info2, storage, store_raw) + } else { + entry + } + } + #[test] fn test_new_entry() { - let slot = 0; - // account_info type that IS cached - let account_info = AccountInfoTest::default(); - let index = AccountsIndex::default_for_tests(); + for store_raw in [false, true] { + for to_raw_first in [false, true] { + let slot = 0; + // account_info type that IS cached + let account_info = AccountInfoTest::default(); + let index = AccountsIndex::default_for_tests(); - let new_entry: AccountMapEntry<_> = - PreAllocatedAccountMapEntry::new(slot, account_info, &index.storage.storage, false) + let new_entry = get_pre_allocated( + slot, + account_info, + &index.storage.storage, + store_raw, + to_raw_first, + ) .into_account_map_entry(&index.storage.storage); - assert_eq!(new_entry.ref_count.load(Ordering::Relaxed), 0); - assert_eq!(new_entry.slot_list.read().unwrap().capacity(), 1); - assert_eq!( - new_entry.slot_list.read().unwrap().to_vec(), - vec![(slot, account_info)] - ); + assert_eq!(new_entry.ref_count.load(Ordering::Relaxed), 0); + assert_eq!(new_entry.slot_list.read().unwrap().capacity(), 1); + assert_eq!( + new_entry.slot_list.read().unwrap().to_vec(), + vec![(slot, account_info)] + ); - // account_info type that is NOT cached - let account_info = true; - let index = AccountsIndex::default_for_tests(); + // account_info type that is NOT cached + let account_info = true; + let index = AccountsIndex::default_for_tests(); - let new_entry: AccountMapEntry<_> = - PreAllocatedAccountMapEntry::new(slot, account_info, &index.storage.storage, false) + let new_entry = get_pre_allocated( + slot, + account_info, + &index.storage.storage, + store_raw, + to_raw_first, + ) .into_account_map_entry(&index.storage.storage); - assert_eq!(new_entry.ref_count.load(Ordering::Relaxed), 1); - assert_eq!(new_entry.slot_list.read().unwrap().capacity(), 1); - assert_eq!( - new_entry.slot_list.read().unwrap().to_vec(), - vec![(slot, account_info)] - ); + assert_eq!(new_entry.ref_count.load(Ordering::Relaxed), 1); + assert_eq!(new_entry.slot_list.read().unwrap().capacity(), 1); + assert_eq!( + new_entry.slot_list.read().unwrap().to_vec(), + vec![(slot, account_info)] + ); + } + } } #[test]