diff --git a/bucket_map/src/bucket.rs b/bucket_map/src/bucket.rs index bcca7b605c588b..5c29756b6216eb 100644 --- a/bucket_map/src/bucket.rs +++ b/bucket_map/src/bucket.rs @@ -96,6 +96,10 @@ pub struct Bucket { pub data: Vec>, stats: Arc, + /// # entries caller expects the map to need to contain. + /// Used as a hint for the next time we need to grow. + anticipated_size: u64, + pub reallocated: Reallocated, DataBucket>, } @@ -123,6 +127,7 @@ impl<'b, T: Clone + Copy + 'static> Bucket { data: vec![], stats, reallocated: Reallocated::default(), + anticipated_size: 0, } } @@ -420,21 +425,28 @@ impl<'b, T: Clone + Copy + 'static> Bucket { } } + pub(crate) fn set_anticipated_count(&mut self, count: u64) { + self.anticipated_size = count; + } + pub fn grow_index(&self, current_capacity_pow2: u8) { if self.index.capacity_pow2 == current_capacity_pow2 { + let mut starting_size_pow2 = self.index.capacity_pow2; + if self.anticipated_size > 0 { + // start the growth at the next pow2 larger than what would be required to hold `anticipated_size`. + // This will prevent unnecessary repeated grows at startup. + starting_size_pow2 = starting_size_pow2.max(self.anticipated_size.ilog2() as u8); + } let mut m = Measure::start("grow_index"); //debug!("GROW_INDEX: {}", current_capacity_pow2); let increment = 1; for i in increment.. { - //increasing the capacity by ^4 reduces the - //likelihood of a re-index collision of 2^(max_search)^2 - //1 in 2^32 let mut index = BucketStorage::new_with_capacity( Arc::clone(&self.drives), 1, std::mem::size_of::>() as u64, - // *2 causes rapid growth of index buckets - self.index.capacity_pow2 + i, // * 2, + // the subtle `+ i` here causes us to grow from the starting size by a power of 2 on each iteration of the for loop + starting_size_pow2 + i, self.index.max_search, Arc::clone(&self.stats.index), Arc::clone(&self.index.count), diff --git a/bucket_map/src/bucket_api.rs b/bucket_map/src/bucket_api.rs index c4be1cd24e3d8b..05275c5308ef6a 100644 --- a/bucket_map/src/bucket_api.rs +++ b/bucket_map/src/bucket_api.rs @@ -111,6 +111,13 @@ impl BucketApi { } } + /// caller can specify that the index needs to hold approximately `count` entries soon. + /// This gives a hint to the resizing algorithm and prevents repeated incremental resizes. + pub fn set_anticipated_count(&self, count: u64) { + let mut bucket = self.get_write_bucket(); + bucket.as_mut().unwrap().set_anticipated_count(count); + } + pub fn update(&self, key: &Pubkey, updatefn: F) where F: FnMut(Option<(&[T], RefCount)>) -> Option<(Vec, RefCount)>, diff --git a/runtime/src/in_mem_accounts_index.rs b/runtime/src/in_mem_accounts_index.rs index 5ebdf760227592..8217f76489791d 100644 --- a/runtime/src/in_mem_accounts_index.rs +++ b/runtime/src/in_mem_accounts_index.rs @@ -1061,6 +1061,9 @@ impl + Into> InMemAccountsIndex + Into> InMemAccountsIndex