11use crate :: fx:: { FxHashMap , FxHasher } ;
2+ #[ cfg( parallel_compiler) ]
3+ use crate :: sync:: is_dyn_thread_safe;
24use crate :: sync:: { CacheAligned , Lock , LockGuard } ;
35use std:: borrow:: Borrow ;
46use std:: collections:: hash_map:: RawEntryMut ;
@@ -18,6 +20,8 @@ pub const SHARDS: usize = 1 << SHARD_BITS;
1820
1921/// An array of cache-line aligned inner locked structures with convenience methods.
2022pub struct Sharded < T > {
23+ #[ cfg( parallel_compiler) ]
24+ mask : usize ,
2125 shards : [ CacheAligned < Lock < T > > ; SHARDS ] ,
2226}
2327
@@ -31,31 +35,53 @@ impl<T: Default> Default for Sharded<T> {
3135impl < T > Sharded < T > {
3236 #[ inline]
3337 pub fn new ( mut value : impl FnMut ( ) -> T ) -> Self {
34- Sharded { shards : [ ( ) ; SHARDS ] . map ( |( ) | CacheAligned ( Lock :: new ( value ( ) ) ) ) }
38+ Sharded {
39+ #[ cfg( parallel_compiler) ]
40+ mask : if is_dyn_thread_safe ( ) { SHARDS - 1 } else { 0 } ,
41+ shards : [ ( ) ; SHARDS ] . map ( |( ) | CacheAligned ( Lock :: new ( value ( ) ) ) ) ,
42+ }
43+ }
44+
45+ #[ inline( always) ]
46+ fn mask ( & self ) -> usize {
47+ #[ cfg( parallel_compiler) ]
48+ {
49+ self . mask
50+ }
51+ #[ cfg( not( parallel_compiler) ) ]
52+ {
53+ 0
54+ }
55+ }
56+
57+ #[ inline( always) ]
58+ fn count ( & self ) -> usize {
59+ self . mask ( ) + 1
3560 }
3661
3762 /// The shard is selected by hashing `val` with `FxHasher`.
3863 #[ inline]
3964 pub fn get_shard_by_value < K : Hash + ?Sized > ( & self , val : & K ) -> & Lock < T > {
40- if SHARDS == 1 { & self . shards [ 0 ] . 0 } else { self . get_shard_by_hash ( make_hash ( val) ) }
65+ self . get_shard_by_hash ( if SHARDS == 1 { 0 } else { make_hash ( val) } )
4166 }
4267
4368 #[ inline]
4469 pub fn get_shard_by_hash ( & self , hash : u64 ) -> & Lock < T > {
45- & self . shards [ get_shard_index_by_hash ( hash) ] . 0
70+ self . get_shard_by_index ( get_shard_hash ( hash) )
4671 }
4772
4873 #[ inline]
4974 pub fn get_shard_by_index ( & self , i : usize ) -> & Lock < T > {
50- & self . shards [ i] . 0
75+ // SAFETY: The index get ANDed with the mask, ensuring it is always inbounds.
76+ unsafe { & self . shards . get_unchecked ( i & self . mask ( ) ) . 0 }
5177 }
5278
5379 pub fn lock_shards ( & self ) -> Vec < LockGuard < ' _ , T > > {
54- ( 0 ..SHARDS ) . map ( |i| self . shards [ i ] . 0 . lock ( ) ) . collect ( )
80+ ( 0 ..self . count ( ) ) . map ( |i| self . get_shard_by_index ( i ) . lock ( ) ) . collect ( )
5581 }
5682
5783 pub fn try_lock_shards ( & self ) -> Option < Vec < LockGuard < ' _ , T > > > {
58- ( 0 ..SHARDS ) . map ( |i| self . shards [ i ] . 0 . try_lock ( ) ) . collect ( )
84+ ( 0 ..self . count ( ) ) . map ( |i| self . get_shard_by_index ( i ) . try_lock ( ) ) . collect ( )
5985 }
6086}
6187
@@ -136,11 +162,9 @@ pub fn make_hash<K: Hash + ?Sized>(val: &K) -> u64 {
136162/// `hash` can be computed with any hasher, so long as that hasher is used
137163/// consistently for each `Sharded` instance.
138164#[ inline]
139- #[ allow( clippy:: modulo_one) ]
140- pub fn get_shard_index_by_hash ( hash : u64 ) -> usize {
165+ fn get_shard_hash ( hash : u64 ) -> usize {
141166 let hash_len = mem:: size_of :: < usize > ( ) ;
142167 // Ignore the top 7 bits as hashbrown uses these and get the next SHARD_BITS highest bits.
143168 // hashbrown also uses the lowest bits, so we can't use those
144- let bits = ( hash >> ( hash_len * 8 - 7 - SHARD_BITS ) ) as usize ;
145- bits % SHARDS
169+ ( hash >> ( hash_len * 8 - 7 - SHARD_BITS ) ) as usize
146170}
0 commit comments