diff options
| author | bors <bors@rust-lang.org> | 2023-05-24 10:58:46 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2023-05-24 10:58:46 +0000 |
| commit | 1d1a1195f39b973e7bf64fff853826c28838917f (patch) | |
| tree | 941dedd4bfcef8705494207a26e1a417e97ef9eb | |
| parent | 2120c913c28896ed8e6247906f8884939c268683 (diff) | |
| parent | 12d4355c60954e5c1b744ea49ca2652f1df16d9a (diff) | |
| download | rust-1d1a1195f39b973e7bf64fff853826c28838917f.tar.gz rust-1d1a1195f39b973e7bf64fff853826c28838917f.zip | |
Auto merge of #14880 - Veykril:intern-double, r=Veykril
Remove double lookups from Interned
| -rw-r--r-- | crates/intern/src/lib.rs | 91 |
1 files changed, 45 insertions, 46 deletions
diff --git a/crates/intern/src/lib.rs b/crates/intern/src/lib.rs index d17eddf1560..dabbf3a38b5 100644 --- a/crates/intern/src/lib.rs +++ b/crates/intern/src/lib.rs @@ -9,7 +9,7 @@ use std::{ }; use dashmap::{DashMap, SharedValue}; -use hashbrown::HashMap; +use hashbrown::{hash_map::RawEntryMut, HashMap}; use once_cell::sync::OnceCell; use rustc_hash::FxHasher; use triomphe::Arc; @@ -26,56 +26,58 @@ pub struct Interned<T: Internable + ?Sized> { impl<T: Internable> Interned<T> { pub fn new(obj: T) -> Self { - match Interned::lookup(&obj) { - Ok(this) => this, - Err(shard) => { - let arc = Arc::new(obj); - Self::alloc(arc, shard) - } + let (mut shard, hash) = Self::select(&obj); + // Atomically, + // - check if `obj` is already in the map + // - if so, clone its `Arc` and return it + // - if not, box it up, insert it, and return a clone + // This needs to be atomic (locking the shard) to avoid races with other thread, which could + // insert the same object between us looking it up and inserting it. + match shard.raw_entry_mut().from_key_hashed_nocheck(hash as u64, &obj) { + RawEntryMut::Occupied(occ) => Self { arc: occ.key().clone() }, + RawEntryMut::Vacant(vac) => Self { + arc: vac + .insert_hashed_nocheck(hash as u64, Arc::new(obj), SharedValue::new(())) + .0 + .clone(), + }, } } } -impl<T: Internable + ?Sized> Interned<T> { - fn lookup(obj: &T) -> Result<Self, Guard<T>> { - let storage = T::storage().get(); - let shard_idx = storage.determine_map(obj); - let shard = &storage.shards()[shard_idx]; - let shard = shard.write(); - +impl Interned<str> { + pub fn new_str(s: &str) -> Self { + let (mut shard, hash) = Self::select(s); // Atomically, // - check if `obj` is already in the map // - if so, clone its `Arc` and return it // - if not, box it up, insert it, and return a clone // This needs to be atomic (locking the shard) to avoid races with other thread, which could // insert the same object between us looking it up and inserting it. - - // FIXME: avoid double lookup/hashing by using raw entry API (once stable, or when - // hashbrown can be plugged into dashmap) - match shard.get_key_value(obj) { - Some((arc, _)) => Ok(Self { arc: arc.clone() }), - None => Err(shard), + match shard.raw_entry_mut().from_key_hashed_nocheck(hash as u64, s) { + RawEntryMut::Occupied(occ) => Self { arc: occ.key().clone() }, + RawEntryMut::Vacant(vac) => Self { + arc: vac + .insert_hashed_nocheck(hash as u64, Arc::from(s), SharedValue::new(())) + .0 + .clone(), + }, } } - - fn alloc(arc: Arc<T>, mut shard: Guard<T>) -> Self { - let arc2 = arc.clone(); - - shard.insert(arc2, SharedValue::new(())); - - Self { arc } - } } -impl Interned<str> { - pub fn new_str(s: &str) -> Self { - match Interned::lookup(s) { - Ok(this) => this, - Err(shard) => { - let arc = Arc::<str>::from(s); - Self::alloc(arc, shard) - } - } +impl<T: Internable + ?Sized> Interned<T> { + #[inline] + fn select(obj: &T) -> (Guard<T>, u64) { + let storage = T::storage().get(); + let hash = { + let mut hasher = std::hash::BuildHasher::build_hasher(storage.hasher()); + obj.hash(&mut hasher); + hasher.finish() + }; + let shard_idx = storage.determine_shard(hash as usize); + let shard = &storage.shards()[shard_idx]; + (shard.write(), hash) } } @@ -94,20 +96,17 @@ impl<T: Internable + ?Sized> Drop for Interned<T> { impl<T: Internable + ?Sized> Interned<T> { #[cold] fn drop_slow(&mut self) { - let storage = T::storage().get(); - let shard_idx = storage.determine_map(&self.arc); - let shard = &storage.shards()[shard_idx]; - let mut shard = shard.write(); - - // FIXME: avoid double lookup - let (arc, _) = shard.get_key_value(&self.arc).expect("interned value removed prematurely"); + let (mut shard, hash) = Self::select(&self.arc); - if Arc::count(arc) != 2 { + if Arc::count(&self.arc) != 2 { // Another thread has interned another copy return; } - shard.remove(&self.arc); + match shard.raw_entry_mut().from_key_hashed_nocheck(hash, &self.arc) { + RawEntryMut::Occupied(occ) => occ.remove(), + RawEntryMut::Vacant(_) => unreachable!(), + }; // Shrink the backing storage if the shard is less than 50% occupied. if shard.len() * 2 < shard.capacity() { |
