1
1
use crate :: fx:: { FxHashMap , FxHasher } ;
2
+ #[ cfg( parallel_compiler) ]
3
+ use crate :: sync:: is_dyn_thread_safe;
2
4
use crate :: sync:: { CacheAligned , Lock , LockGuard } ;
3
5
use std:: borrow:: Borrow ;
4
6
use std:: collections:: hash_map:: RawEntryMut ;
@@ -18,6 +20,8 @@ pub const SHARDS: usize = 1 << SHARD_BITS;
18
20
19
21
/// An array of cache-line aligned inner locked structures with convenience methods.
20
22
pub struct Sharded < T > {
23
+ #[ cfg( parallel_compiler) ]
24
+ mask : usize ,
21
25
shards : [ CacheAligned < Lock < T > > ; SHARDS ] ,
22
26
}
23
27
@@ -31,31 +35,53 @@ impl<T: Default> Default for Sharded<T> {
31
35
impl < T > Sharded < T > {
32
36
#[ inline]
33
37
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
35
60
}
36
61
37
62
/// The shard is selected by hashing `val` with `FxHasher`.
38
63
#[ inline]
39
64
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) } )
41
66
}
42
67
43
68
#[ inline]
44
69
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) )
46
71
}
47
72
48
73
#[ inline]
49
74
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 }
51
77
}
52
78
53
79
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 ( )
55
81
}
56
82
57
83
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 ( )
59
85
}
60
86
}
61
87
@@ -136,11 +162,9 @@ pub fn make_hash<K: Hash + ?Sized>(val: &K) -> u64 {
136
162
/// `hash` can be computed with any hasher, so long as that hasher is used
137
163
/// consistently for each `Sharded` instance.
138
164
#[ 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 {
141
166
let hash_len = mem:: size_of :: < usize > ( ) ;
142
167
// Ignore the top 7 bits as hashbrown uses these and get the next SHARD_BITS highest bits.
143
168
// 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
146
170
}
0 commit comments