Skip to content

Commit 0796d1b

Browse files
committed
specialize Sharded
1 parent 64714b6 commit 0796d1b

File tree

10 files changed

+325
-125
lines changed

10 files changed

+325
-125
lines changed

compiler/rustc_data_structures/src/sharded.rs

+189-26
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,236 @@
11
use crate::fx::{FxHashMap, FxHasher};
2-
use crate::sync::{Lock, LockGuard};
2+
use crate::sync::LockLike;
3+
use parking_lot::{Mutex, MutexGuard};
34
use std::borrow::Borrow;
5+
use std::cell::{RefCell, RefMut};
46
use std::collections::hash_map::RawEntryMut;
57
use std::hash::{Hash, Hasher};
8+
use std::mem;
69

7-
const SHARD_BITS: usize = 0;
10+
pub trait Shard {
11+
type Impl<T>: ShardImpl<T>;
12+
}
813

9-
pub const SHARDS: usize = 1 << SHARD_BITS;
14+
pub trait ShardImpl<T> {
15+
type Lock: LockLike<T>;
16+
17+
fn new(value: impl FnMut() -> T) -> Self;
18+
19+
fn get_shard_by_value<K: Hash + ?Sized>(&self, _val: &K) -> &Self::Lock;
20+
21+
fn get_shard_by_hash(&self, _hash: u64) -> &Self::Lock;
22+
23+
fn lock_shards(&self) -> Vec<<Self::Lock as LockLike<T>>::LockGuard<'_>>;
24+
25+
fn try_lock_shards(&self) -> Option<Vec<<Self::Lock as LockLike<T>>::LockGuard<'_>>>;
26+
}
27+
28+
#[derive(Default)]
29+
pub struct SingleShard;
30+
31+
impl Shard for SingleShard {
32+
type Impl<T> = SingleShardImpl<T>;
33+
}
1034

1135
/// An array of cache-line aligned inner locked structures with convenience methods.
12-
pub struct Sharded<T> {
13-
shard: Lock<T>,
36+
pub struct SingleShardImpl<T> {
37+
shard: RefCell<T>,
38+
}
39+
40+
impl<T: Default> Default for SingleShardImpl<T> {
41+
#[inline]
42+
fn default() -> Self {
43+
Self { shard: RefCell::new(T::default()) }
44+
}
45+
}
46+
47+
impl<T> ShardImpl<T> for SingleShardImpl<T> {
48+
type Lock = RefCell<T>;
49+
50+
#[inline]
51+
fn new(mut value: impl FnMut() -> T) -> Self {
52+
SingleShardImpl { shard: RefCell::new(value()) }
53+
}
54+
55+
#[inline]
56+
fn get_shard_by_value<K: Hash + ?Sized>(&self, _val: &K) -> &RefCell<T> {
57+
&self.shard
58+
}
59+
60+
#[inline]
61+
fn get_shard_by_hash(&self, _hash: u64) -> &RefCell<T> {
62+
&self.shard
63+
}
64+
65+
fn lock_shards(&self) -> Vec<RefMut<'_, T>> {
66+
vec![self.shard.lock()]
67+
}
68+
69+
fn try_lock_shards(&self) -> Option<Vec<RefMut<'_, T>>> {
70+
Some(vec![self.shard.try_lock()?])
71+
}
72+
}
73+
74+
const SHARD_BITS: usize = 5;
75+
76+
pub const SHARDS: usize = 1 << SHARD_BITS;
77+
78+
#[derive(Default)]
79+
pub struct Sharded;
80+
81+
impl Shard for Sharded {
82+
type Impl<T> = ShardedImpl<T>;
83+
}
84+
85+
#[derive(Default)]
86+
#[repr(align(64))]
87+
pub struct CacheAligned<T>(pub T);
88+
89+
pub struct ShardedImpl<T> {
90+
shards: [CacheAligned<Mutex<T>>; SHARDS],
1491
}
1592

16-
impl<T: Default> Default for Sharded<T> {
93+
impl<T: Default> Default for ShardedImpl<T> {
1794
#[inline]
1895
fn default() -> Self {
1996
Self::new(T::default)
2097
}
2198
}
2299

23-
impl<T: Default> Sharded<T> {
100+
impl<T> ShardImpl<T> for ShardedImpl<T> {
101+
type Lock = Mutex<T>;
102+
103+
#[inline]
104+
fn new(mut value: impl FnMut() -> T) -> Self {
105+
ShardedImpl { shards: [(); SHARDS].map(|()| CacheAligned(Mutex::new(value()))) }
106+
}
107+
108+
/// The shard is selected by hashing `val` with `FxHasher`.
109+
#[inline]
110+
fn get_shard_by_value<K: Hash + ?Sized>(&self, val: &K) -> &Mutex<T> {
111+
self.get_shard_by_hash(make_hash(val))
112+
}
113+
114+
#[inline]
115+
fn get_shard_by_hash(&self, hash: u64) -> &Mutex<T> {
116+
&self.shards[get_shard_index_by_hash(hash)].0
117+
}
118+
119+
fn lock_shards(&self) -> Vec<MutexGuard<'_, T>> {
120+
(0..SHARDS).map(|i| self.shards[i].0.lock()).collect()
121+
}
122+
123+
fn try_lock_shards(&self) -> Option<Vec<MutexGuard<'_, T>>> {
124+
(0..SHARDS).map(|i| self.shards[i].0.try_lock()).collect()
125+
}
126+
}
127+
128+
pub struct DynSharded<T> {
129+
single_thread: bool,
130+
single_shard: RefCell<T>,
131+
parallel_shard: ShardedImpl<T>,
132+
}
133+
134+
// just for speed test
135+
unsafe impl<T> Sync for DynSharded<T> {}
136+
137+
impl<T: Default> Default for DynSharded<T> {
24138
#[inline]
139+
fn default() -> Self {
140+
let single_thread = !crate::sync::active();
141+
DynSharded {
142+
single_thread,
143+
single_shard: RefCell::new(T::default()),
144+
parallel_shard: ShardedImpl::default(),
145+
}
146+
}
147+
}
148+
149+
impl<T: Default> DynSharded<T> {
25150
pub fn new(mut value: impl FnMut() -> T) -> Self {
26-
Sharded { shard: Lock::new(value()) }
151+
if !crate::sync::active() {
152+
DynSharded {
153+
single_thread: true,
154+
single_shard: RefCell::new(value()),
155+
parallel_shard: ShardedImpl::default(),
156+
}
157+
} else {
158+
DynSharded {
159+
single_thread: false,
160+
single_shard: RefCell::new(T::default()),
161+
parallel_shard: ShardedImpl::new(value),
162+
}
163+
}
27164
}
28165

29166
/// The shard is selected by hashing `val` with `FxHasher`.
30167
#[inline]
31168
pub fn with_get_shard_by_value<K: Hash + ?Sized, F: FnOnce(&mut T) -> R, R>(
32169
&self,
33-
_val: &K,
170+
val: &K,
34171
f: F,
35172
) -> R {
36-
self.shard.with_lock(f)
173+
if self.single_thread {
174+
let mut lock = self.single_shard.borrow_mut();
175+
f(&mut *lock)
176+
} else {
177+
let mut lock = self.parallel_shard.get_shard_by_value(val).lock();
178+
f(&mut *lock)
179+
}
37180
}
38181

39182
#[inline]
40-
pub fn with_get_shard_by_hash<F: FnOnce(&mut T) -> R, R>(&self, _hash: u64, f: F) -> R {
41-
self.shard.with_lock(f)
183+
pub fn with_get_shard_by_hash<F: FnOnce(&mut T) -> R, R>(&self, hash: u64, f: F) -> R {
184+
if self.single_thread {
185+
let mut lock = self.single_shard.borrow_mut();
186+
f(&mut *lock)
187+
} else {
188+
let mut lock = self.parallel_shard.get_shard_by_hash(hash).lock();
189+
f(&mut *lock)
190+
}
42191
}
43192

44193
#[inline]
45-
pub fn get_shard_by_value<K: Hash + ?Sized>(&self, _val: &K) -> &Lock<T> {
46-
&self.shard
194+
pub fn with_lock_shards<F: FnMut(&mut T) -> R, R>(&self, mut f: F) -> Vec<R> {
195+
if self.single_thread {
196+
let mut lock = self.single_shard.borrow_mut();
197+
vec![f(&mut *lock)]
198+
} else {
199+
(0..SHARDS).map(|i| f(&mut *self.parallel_shard.shards[i].0.lock())).collect()
200+
}
47201
}
48202

49203
#[inline]
50-
pub fn get_shard_by_hash(&self, _hash: u64) -> &Lock<T> {
51-
&self.shard
204+
pub fn with_try_lock_shards<F: FnMut(&mut T) -> R, R>(&self, mut f: F) -> Option<Vec<R>> {
205+
if self.single_thread {
206+
let mut lock = self.single_shard.try_borrow_mut().ok()?;
207+
Some(vec![f(&mut *lock)])
208+
} else {
209+
(0..SHARDS)
210+
.map(|i| {
211+
let mut shard = self.parallel_shard.shards[i].0.try_lock()?;
212+
Some(f(&mut *shard))
213+
})
214+
.collect()
215+
}
52216
}
53217

54-
pub fn lock_shards(&self) -> Vec<LockGuard<'_, T>> {
55-
vec![self.shard.lock()]
218+
#[inline]
219+
pub fn get_lock_by_value<K: Hash + ?Sized>(&self, val: &K) -> &Mutex<T> {
220+
self.parallel_shard.get_shard_by_value(val)
56221
}
57222

58-
pub fn try_lock_shards(&self) -> Option<Vec<LockGuard<'_, T>>> {
59-
Some(vec![self.shard.try_lock()?])
223+
#[inline]
224+
pub fn get_borrow_by_value<K: Hash + ?Sized>(&self, _val: &K) -> &RefCell<T> {
225+
&self.single_shard
60226
}
61227
}
62228

63-
pub type ShardedHashMap<K, V> = Sharded<FxHashMap<K, V>>;
229+
pub type ShardedHashMap<K, V> = DynSharded<FxHashMap<K, V>>;
64230

65231
impl<K: Eq, V> ShardedHashMap<K, V> {
66232
pub fn len(&self) -> usize {
67-
self.lock_shards().iter().map(|shard| shard.len()).sum()
233+
self.with_lock_shards(|shard| shard.len()).into_iter().sum()
68234
}
69235
}
70236

@@ -120,7 +286,6 @@ pub trait IntoPointer {
120286
impl<K: Eq + Hash + Copy + IntoPointer> ShardedHashMap<K, ()> {
121287
pub fn contains_pointer_to<T: Hash + IntoPointer>(&self, value: &T) -> bool {
122288
let hash = make_hash(&value);
123-
124289
self.with_get_shard_by_hash(hash, |shard| {
125290
let value = value.into_pointer();
126291
shard.raw_entry().from_hash(hash, |entry| entry.into_pointer() == value).is_some()
@@ -135,19 +300,17 @@ pub fn make_hash<K: Hash + ?Sized>(val: &K) -> u64 {
135300
state.finish()
136301
}
137302

138-
/*
139303
/// Get a shard with a pre-computed hash value. If `get_shard_by_value` is
140304
/// ever used in combination with `get_shard_by_hash` on a single `Sharded`
141305
/// instance, then `hash` must be computed with `FxHasher`. Otherwise,
142306
/// `hash` can be computed with any hasher, so long as that hasher is used
143307
/// consistently for each `Sharded` instance.
144308
#[inline]
145309
#[allow(clippy::modulo_one)]
146-
fn get_shard_index_by_hash(hash: u64) -> usize {
310+
pub fn get_shard_index_by_hash(hash: u64) -> usize {
147311
let hash_len = mem::size_of::<usize>();
148312
// Ignore the top 7 bits as hashbrown uses these and get the next SHARD_BITS highest bits.
149313
// hashbrown also uses the lowest bits, so we can't use those
150314
let bits = (hash >> (hash_len * 8 - 7 - SHARD_BITS)) as usize;
151315
bits % SHARDS
152316
}
153-
*/

compiler/rustc_data_structures/src/sync.rs

-18
Original file line numberDiff line numberDiff line change
@@ -767,10 +767,6 @@ impl<'a, T> Drop for LockGuard<'a, T> {
767767
}
768768
}
769769

770-
pub trait SLock: Copy {
771-
type Lock<T>: LockLike<T>;
772-
}
773-
774770
pub trait LockLike<T> {
775771
type LockGuard<'a>: DerefMut<Target = T>
776772
where
@@ -787,13 +783,6 @@ pub trait LockLike<T> {
787783
fn lock(&self) -> Self::LockGuard<'_>;
788784
}
789785

790-
#[derive(Copy, Clone, Default)]
791-
pub struct SRefCell;
792-
793-
impl SLock for SRefCell {
794-
type Lock<T> = RefCell<T>;
795-
}
796-
797786
impl<T> LockLike<T> for RefCell<T> {
798787
type LockGuard<'a> = RefMut<'a, T> where T: 'a;
799788

@@ -824,13 +813,6 @@ impl<T> LockLike<T> for RefCell<T> {
824813
}
825814
}
826815

827-
#[derive(Copy, Clone, Default)]
828-
pub struct SMutex;
829-
830-
impl SLock for SMutex {
831-
type Lock<T> = Mutex<T>;
832-
}
833-
834816
impl<T> LockLike<T> for Mutex<T> {
835817
type LockGuard<'a> = MutexGuard<'a, T> where T: 'a;
836818

compiler/rustc_middle/src/query/plumbing.rs

+14-6
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,16 @@ use crate::query::{
99
use crate::ty::TyCtxt;
1010
use field_offset::FieldOffset;
1111
use measureme::StringId;
12-
use rustc_data_structures::fx::FxHashMap;
13-
use rustc_data_structures::sync::{AtomicU64, Lrc, SLock, SMutex, SRefCell, WorkerLocal};
12+
use rustc_arena::TypedArena;
13+
use rustc_ast as ast;
14+
use rustc_ast::expand::allocator::AllocatorKind;
15+
use rustc_attr as attr;
16+
use rustc_data_structures::fingerprint::Fingerprint;
17+
use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
18+
use rustc_data_structures::sharded::{Shard, Sharded, SingleShard};
19+
use rustc_data_structures::steal::Steal;
20+
use rustc_data_structures::svh::Svh;
21+
use rustc_data_structures::sync::{AtomicU64, Lrc, WorkerLocal};
1422
use rustc_hir::def::DefKind;
1523
use rustc_hir::def_id::{DefId, LocalDefId};
1624
use rustc_hir::hir_id::OwnerId;
@@ -84,8 +92,8 @@ pub struct QuerySystem<'tcx> {
8492
pub dynamic_queries: DynamicQueries<'tcx>,
8593

8694
pub single_thread: bool,
87-
pub single_caches: QueryCaches<'tcx, SRefCell>,
88-
pub parallel_caches: QueryCaches<'tcx, SMutex>,
95+
pub single_caches: QueryCaches<'tcx, SingleShard>,
96+
pub parallel_caches: QueryCaches<'tcx, Sharded>,
8997

9098
/// This provides access to the incremental compilation on-disk cache for query results.
9199
/// Do not access this directly. It is only meant to be used by
@@ -377,8 +385,8 @@ macro_rules! define_callbacks {
377385
}
378386

379387
#[derive(Default)]
380-
pub struct QueryCaches<'tcx, L: SLock> {
381-
$($(#[$attr])* pub $name: query_storage::$name<'tcx, L>,)*
388+
pub struct QueryCaches<'tcx, S: Shard> {
389+
$($(#[$attr])* pub $name: query_storage::$name<'tcx, S>,)*
382390
}
383391

384392
impl<'tcx> TyCtxtEnsure<'tcx> {

0 commit comments

Comments
 (0)