1
1
use crate :: fx:: { FxHashMap , FxHasher } ;
2
- use crate :: sync:: { Lock , LockGuard } ;
2
+ use crate :: sync:: LockLike ;
3
+ use parking_lot:: { Mutex , MutexGuard } ;
3
4
use std:: borrow:: Borrow ;
5
+ use std:: cell:: { RefCell , RefMut } ;
4
6
use std:: collections:: hash_map:: RawEntryMut ;
5
7
use std:: hash:: { Hash , Hasher } ;
8
+ use std:: mem;
6
9
7
- const SHARD_BITS : usize = 0 ;
10
+ pub trait Shard {
11
+ type Impl < T > : ShardImpl < T > ;
12
+ }
8
13
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
+ }
10
34
11
35
/// 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 ] ,
14
91
}
15
92
16
- impl < T : Default > Default for Sharded < T > {
93
+ impl < T : Default > Default for ShardedImpl < T > {
17
94
#[ inline]
18
95
fn default ( ) -> Self {
19
96
Self :: new ( T :: default)
20
97
}
21
98
}
22
99
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 > {
24
138
#[ 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 > {
25
150
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
+ }
27
164
}
28
165
29
166
/// The shard is selected by hashing `val` with `FxHasher`.
30
167
#[ inline]
31
168
pub fn with_get_shard_by_value < K : Hash + ?Sized , F : FnOnce ( & mut T ) -> R , R > (
32
169
& self ,
33
- _val : & K ,
170
+ val : & K ,
34
171
f : F ,
35
172
) -> 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
+ }
37
180
}
38
181
39
182
#[ 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
+ }
42
191
}
43
192
44
193
#[ 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
+ }
47
201
}
48
202
49
203
#[ 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
+ }
52
216
}
53
217
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)
56
221
}
57
222
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
60
226
}
61
227
}
62
228
63
- pub type ShardedHashMap < K , V > = Sharded < FxHashMap < K , V > > ;
229
+ pub type ShardedHashMap < K , V > = DynSharded < FxHashMap < K , V > > ;
64
230
65
231
impl < K : Eq , V > ShardedHashMap < K , V > {
66
232
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 ( )
68
234
}
69
235
}
70
236
@@ -120,7 +286,6 @@ pub trait IntoPointer {
120
286
impl < K : Eq + Hash + Copy + IntoPointer > ShardedHashMap < K , ( ) > {
121
287
pub fn contains_pointer_to < T : Hash + IntoPointer > ( & self , value : & T ) -> bool {
122
288
let hash = make_hash ( & value) ;
123
-
124
289
self . with_get_shard_by_hash ( hash, |shard| {
125
290
let value = value. into_pointer ( ) ;
126
291
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 {
135
300
state. finish ( )
136
301
}
137
302
138
- /*
139
303
/// Get a shard with a pre-computed hash value. If `get_shard_by_value` is
140
304
/// ever used in combination with `get_shard_by_hash` on a single `Sharded`
141
305
/// instance, then `hash` must be computed with `FxHasher`. Otherwise,
142
306
/// `hash` can be computed with any hasher, so long as that hasher is used
143
307
/// consistently for each `Sharded` instance.
144
308
#[ inline]
145
309
#[ 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 {
147
311
let hash_len = mem:: size_of :: < usize > ( ) ;
148
312
// Ignore the top 7 bits as hashbrown uses these and get the next SHARD_BITS highest bits.
149
313
// hashbrown also uses the lowest bits, so we can't use those
150
314
let bits = ( hash >> ( hash_len * 8 - 7 - SHARD_BITS ) ) as usize ;
151
315
bits % SHARDS
152
316
}
153
- */
0 commit comments