Rollup merge of #137701 - cuviper:sharded-hashtable, r=fmease
Convert `ShardedHashMap` to use `hashbrown::HashTable` The `hash_raw_entry` feature (#56167) has finished fcp-close, so the compiler should stop using it to allow its removal. Several `Sharded` maps were using raw entries to avoid re-hashing between shard and map lookup, and we can do that with `hashbrown::HashTable` instead.
This commit is contained in:
commit
4ffbc32f17
10 changed files with 109 additions and 66 deletions
|
@ -24,7 +24,6 @@
|
|||
#![feature(dropck_eyepatch)]
|
||||
#![feature(extend_one)]
|
||||
#![feature(file_buffered)]
|
||||
#![feature(hash_raw_entry)]
|
||||
#![feature(macro_metavar_expr)]
|
||||
#![feature(map_try_insert)]
|
||||
#![feature(min_specialization)]
|
||||
|
|
|
@ -76,6 +76,7 @@ impl_dyn_send!(
|
|||
[crate::sync::RwLock<T> where T: DynSend]
|
||||
[crate::tagged_ptr::TaggedRef<'a, P, T> where 'a, P: Sync, T: Send + crate::tagged_ptr::Tag]
|
||||
[rustc_arena::TypedArena<T> where T: DynSend]
|
||||
[hashbrown::HashTable<T> where T: DynSend]
|
||||
[indexmap::IndexSet<V, S> where V: DynSend, S: DynSend]
|
||||
[indexmap::IndexMap<K, V, S> where K: DynSend, V: DynSend, S: DynSend]
|
||||
[thin_vec::ThinVec<T> where T: DynSend]
|
||||
|
@ -153,6 +154,7 @@ impl_dyn_sync!(
|
|||
[crate::tagged_ptr::TaggedRef<'a, P, T> where 'a, P: Sync, T: Sync + crate::tagged_ptr::Tag]
|
||||
[parking_lot::lock_api::Mutex<R, T> where R: DynSync, T: ?Sized + DynSend]
|
||||
[parking_lot::lock_api::RwLock<R, T> where R: DynSync, T: ?Sized + DynSend + DynSync]
|
||||
[hashbrown::HashTable<T> where T: DynSync]
|
||||
[indexmap::IndexSet<V, S> where V: DynSync, S: DynSync]
|
||||
[indexmap::IndexMap<K, V, S> where K: DynSync, V: DynSync, S: DynSync]
|
||||
[smallvec::SmallVec<A> where A: smallvec::Array + DynSync]
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use std::borrow::Borrow;
|
||||
use std::collections::hash_map::RawEntryMut;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::iter;
|
||||
use std::{iter, mem};
|
||||
|
||||
use either::Either;
|
||||
use hashbrown::hash_table::{Entry, HashTable};
|
||||
|
||||
use crate::fx::{FxHashMap, FxHasher};
|
||||
use crate::fx::FxHasher;
|
||||
use crate::sync::{CacheAligned, Lock, LockGuard, Mode, is_dyn_thread_safe};
|
||||
|
||||
// 32 shards is sufficient to reduce contention on an 8-core Ryzen 7 1700,
|
||||
|
@ -140,17 +140,67 @@ pub fn shards() -> usize {
|
|||
1
|
||||
}
|
||||
|
||||
pub type ShardedHashMap<K, V> = Sharded<FxHashMap<K, V>>;
|
||||
pub type ShardedHashMap<K, V> = Sharded<HashTable<(K, V)>>;
|
||||
|
||||
impl<K: Eq, V> ShardedHashMap<K, V> {
|
||||
pub fn with_capacity(cap: usize) -> Self {
|
||||
Self::new(|| FxHashMap::with_capacity_and_hasher(cap, rustc_hash::FxBuildHasher::default()))
|
||||
Self::new(|| HashTable::with_capacity(cap))
|
||||
}
|
||||
pub fn len(&self) -> usize {
|
||||
self.lock_shards().map(|shard| shard.len()).sum()
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Eq + Hash, V> ShardedHashMap<K, V> {
|
||||
#[inline]
|
||||
pub fn get<Q>(&self, key: &Q) -> Option<V>
|
||||
where
|
||||
K: Borrow<Q>,
|
||||
Q: Hash + Eq,
|
||||
V: Clone,
|
||||
{
|
||||
let hash = make_hash(key);
|
||||
let shard = self.lock_shard_by_hash(hash);
|
||||
let (_, value) = shard.find(hash, |(k, _)| k.borrow() == key)?;
|
||||
Some(value.clone())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_or_insert_with(&self, key: K, default: impl FnOnce() -> V) -> V
|
||||
where
|
||||
V: Copy,
|
||||
{
|
||||
let hash = make_hash(&key);
|
||||
let mut shard = self.lock_shard_by_hash(hash);
|
||||
|
||||
match table_entry(&mut shard, hash, &key) {
|
||||
Entry::Occupied(e) => e.get().1,
|
||||
Entry::Vacant(e) => {
|
||||
let value = default();
|
||||
e.insert((key, value));
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn insert(&self, key: K, value: V) -> Option<V> {
|
||||
let hash = make_hash(&key);
|
||||
let mut shard = self.lock_shard_by_hash(hash);
|
||||
|
||||
match table_entry(&mut shard, hash, &key) {
|
||||
Entry::Occupied(e) => {
|
||||
let previous = mem::replace(&mut e.into_mut().1, value);
|
||||
Some(previous)
|
||||
}
|
||||
Entry::Vacant(e) => {
|
||||
e.insert((key, value));
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Eq + Hash + Copy> ShardedHashMap<K, ()> {
|
||||
#[inline]
|
||||
pub fn intern_ref<Q: ?Sized>(&self, value: &Q, make: impl FnOnce() -> K) -> K
|
||||
|
@ -160,13 +210,12 @@ impl<K: Eq + Hash + Copy> ShardedHashMap<K, ()> {
|
|||
{
|
||||
let hash = make_hash(value);
|
||||
let mut shard = self.lock_shard_by_hash(hash);
|
||||
let entry = shard.raw_entry_mut().from_key_hashed_nocheck(hash, value);
|
||||
|
||||
match entry {
|
||||
RawEntryMut::Occupied(e) => *e.key(),
|
||||
RawEntryMut::Vacant(e) => {
|
||||
match table_entry(&mut shard, hash, value) {
|
||||
Entry::Occupied(e) => e.get().0,
|
||||
Entry::Vacant(e) => {
|
||||
let v = make();
|
||||
e.insert_hashed_nocheck(hash, v, ());
|
||||
e.insert((v, ()));
|
||||
v
|
||||
}
|
||||
}
|
||||
|
@ -180,13 +229,12 @@ impl<K: Eq + Hash + Copy> ShardedHashMap<K, ()> {
|
|||
{
|
||||
let hash = make_hash(&value);
|
||||
let mut shard = self.lock_shard_by_hash(hash);
|
||||
let entry = shard.raw_entry_mut().from_key_hashed_nocheck(hash, &value);
|
||||
|
||||
match entry {
|
||||
RawEntryMut::Occupied(e) => *e.key(),
|
||||
RawEntryMut::Vacant(e) => {
|
||||
match table_entry(&mut shard, hash, &value) {
|
||||
Entry::Occupied(e) => e.get().0,
|
||||
Entry::Vacant(e) => {
|
||||
let v = make(value);
|
||||
e.insert_hashed_nocheck(hash, v, ());
|
||||
e.insert((v, ()));
|
||||
v
|
||||
}
|
||||
}
|
||||
|
@ -203,17 +251,30 @@ impl<K: Eq + Hash + Copy + IntoPointer> ShardedHashMap<K, ()> {
|
|||
let hash = make_hash(&value);
|
||||
let shard = self.lock_shard_by_hash(hash);
|
||||
let value = value.into_pointer();
|
||||
shard.raw_entry().from_hash(hash, |entry| entry.into_pointer() == value).is_some()
|
||||
shard.find(hash, |(k, ())| k.into_pointer() == value).is_some()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn make_hash<K: Hash + ?Sized>(val: &K) -> u64 {
|
||||
fn make_hash<K: Hash + ?Sized>(val: &K) -> u64 {
|
||||
let mut state = FxHasher::default();
|
||||
val.hash(&mut state);
|
||||
state.finish()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn table_entry<'a, K, V, Q>(
|
||||
table: &'a mut HashTable<(K, V)>,
|
||||
hash: u64,
|
||||
key: &Q,
|
||||
) -> Entry<'a, (K, V)>
|
||||
where
|
||||
K: Hash + Borrow<Q>,
|
||||
Q: ?Sized + Eq,
|
||||
{
|
||||
table.entry(hash, move |(k, _)| k.borrow() == key, |(k, _)| make_hash(k))
|
||||
}
|
||||
|
||||
/// Get a shard with a pre-computed hash value. If `get_shard_by_value` is
|
||||
/// ever used in combination with `get_shard_by_hash` on a single `Sharded`
|
||||
/// instance, then `hash` must be computed with `FxHasher`. Otherwise,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue