diff --git a/runtime/src/accounts_db.rs b/runtime/src/accounts_db.rs index 864d580b4b..66d547b584 100644 --- a/runtime/src/accounts_db.rs +++ b/runtime/src/accounts_db.rs @@ -950,6 +950,7 @@ struct LatestAccountsIndexRootsStats { roots_len: AtomicUsize, uncleaned_roots_len: AtomicUsize, previous_uncleaned_roots_len: AtomicUsize, + roots_range: AtomicU64, } impl LatestAccountsIndexRootsStats { @@ -964,6 +965,8 @@ impl LatestAccountsIndexRootsStats { accounts_index_roots_stats.previous_uncleaned_roots_len, Ordering::Relaxed, ); + self.roots_range + .store(accounts_index_roots_stats.roots_range, Ordering::Relaxed); } fn report(&self) { @@ -984,6 +987,11 @@ impl LatestAccountsIndexRootsStats { self.previous_uncleaned_roots_len.load(Ordering::Relaxed) as i64, i64 ), + ( + "roots_range_width", + self.roots_range.load(Ordering::Relaxed) as i64, + i64 + ), ); // Don't need to reset since this tracks the latest updates, not a cumulative total diff --git a/runtime/src/accounts_index.rs b/runtime/src/accounts_index.rs index 67dad71516..9f8d576be1 100644 --- a/runtime/src/accounts_index.rs +++ b/runtime/src/accounts_index.rs @@ -224,6 +224,11 @@ impl RollingBitField { ); } + pub fn range_width(&self) -> u64 { + // note that max isn't updated on remove, so it can be above the current max + self.max - self.min + } + pub fn insert(&mut self, key: u64) { self.check_range(key); let address = self.get_address(&key); @@ -254,14 +259,19 @@ impl RollingBitField { // after removing 'key' where 'key' = min, make min the correct new min value fn purge(&mut self, key: &u64) { - if key == &self.min && self.count > 0 { - let start = self.min + 1; // min just got removed - for key in start..self.max { - if self.contains_assume_in_range(&key) { - self.min = key; - break; + if self.count > 0 { + if key == &self.min { + let start = self.min + 1; // min just got removed + for key in start..self.max { + if self.contains_assume_in_range(&key) { + self.min = key; + break; + } } } + } else { + self.min = Slot::default(); + self.max = Slot::default(); } } @@ -335,6 +345,7 @@ pub struct AccountsIndexRootsStats { pub roots_len: usize, pub uncleaned_roots_len: usize, pub previous_uncleaned_roots_len: usize, + pub roots_range: u64, } pub struct AccountsIndexIterator<'a, T> { @@ -1222,7 +1233,7 @@ impl AccountsIndex { /// Accounts no longer reference this slot. pub fn clean_dead_slot(&self, slot: Slot) -> Option { if self.is_root(slot) { - let (roots_len, uncleaned_roots_len, previous_uncleaned_roots_len) = { + let (roots_len, uncleaned_roots_len, previous_uncleaned_roots_len, roots_range) = { let mut w_roots_tracker = self.roots_tracker.write().unwrap(); w_roots_tracker.roots.remove(&slot); w_roots_tracker.uncleaned_roots.remove(&slot); @@ -1231,12 +1242,14 @@ impl AccountsIndex { w_roots_tracker.roots.len(), w_roots_tracker.uncleaned_roots.len(), w_roots_tracker.previous_uncleaned_roots.len(), + w_roots_tracker.roots.range_width(), ) }; Some(AccountsIndexRootsStats { roots_len, uncleaned_roots_len, previous_uncleaned_roots_len, + roots_range, }) } else { None @@ -1561,9 +1574,24 @@ pub mod tests { fn compare(hashset: &HashSet, bitfield: &RollingBitField) { assert_eq!(hashset.len(), bitfield.len()); assert_eq!(hashset.is_empty(), bitfield.is_empty()); + let mut min = Slot::MAX; + let mut max = Slot::MIN; for item in bitfield.get_all() { + min = std::cmp::min(min, item); + max = std::cmp::max(max, item); assert!(hashset.contains(&item)); } + assert!( + bitfield.range_width() + >= if bitfield.is_empty() { + 0 + } else { + max + 1 - min + }, + "hashset: {:?}, bitfield: {:?}", + hashset, + bitfield.get_all() + ); } #[test]