11use crate :: fx:: { FxHashMap , FxHasher } ;
22#[ cfg( parallel_compiler) ]
3- use crate :: sync:: is_dyn_thread_safe;
4- use crate :: sync:: { CacheAligned , Lock , LockGuard } ;
3+ use crate :: sync:: { is_dyn_thread_safe, CacheAligned } ;
4+ use crate :: sync:: { Lock , LockGuard } ;
55use std:: borrow:: Borrow ;
66use std:: collections:: hash_map:: RawEntryMut ;
77use std:: hash:: { Hash , Hasher } ;
88use std:: mem;
99
10- #[ cfg( parallel_compiler) ]
1110// 32 shards is sufficient to reduce contention on an 8-core Ryzen 7 1700,
1211// but this should be tested on higher core count CPUs. How the `Sharded` type gets used
1312// may also affect the ideal number of shards.
14- const SHARD_BITS : usize = 5 ;
15-
16- #[ cfg( not( parallel_compiler) ) ]
17- const SHARD_BITS : usize = 0 ;
13+ const SHARD_BITS : usize = if cfg ! ( parallel_compiler) { 5 } else { 0 } ;
1814
1915pub const SHARDS : usize = 1 << SHARD_BITS ;
2016
2117/// An array of cache-line aligned inner locked structures with convenience methods.
22- pub struct Sharded < T > {
23- /// This mask is used to ensure that accesses are inbounds of `shards`.
24- /// When dynamic thread safety is off, this field is set to 0 causing only
25- /// a single shard to be used for greater cache efficiency.
18+ /// A single field is used when the compiler uses only one thread.
19+ pub enum Sharded < T > {
20+ Single ( Lock < T > ) ,
2621 #[ cfg( parallel_compiler) ]
27- mask : usize ,
28- shards : [ CacheAligned < Lock < T > > ; SHARDS ] ,
22+ Shards ( Box < [ CacheAligned < Lock < T > > ; SHARDS ] > ) ,
2923}
3024
3125impl < T : Default > Default for Sharded < T > {
@@ -38,29 +32,14 @@ impl<T: Default> Default for Sharded<T> {
3832impl < T > Sharded < T > {
3933 #[ inline]
4034 pub fn new ( mut value : impl FnMut ( ) -> T ) -> Self {
41- Sharded {
42- #[ cfg( parallel_compiler) ]
43- mask : if is_dyn_thread_safe ( ) { SHARDS - 1 } else { 0 } ,
44- shards : [ ( ) ; SHARDS ] . map ( |( ) | CacheAligned ( Lock :: new ( value ( ) ) ) ) ,
45- }
46- }
47-
48- #[ inline( always) ]
49- fn mask ( & self ) -> usize {
5035 #[ cfg( parallel_compiler) ]
51- {
52- if SHARDS == 1 { 0 } else { self . mask }
53- }
54- #[ cfg( not( parallel_compiler) ) ]
55- {
56- 0
36+ if is_dyn_thread_safe ( ) {
37+ return Sharded :: Shards ( Box :: new (
38+ [ ( ) ; SHARDS ] . map ( |( ) | CacheAligned ( Lock :: new ( value ( ) ) ) ) ,
39+ ) ) ;
5740 }
58- }
5941
60- #[ inline( always) ]
61- fn count ( & self ) -> usize {
62- // `self.mask` is always one below the used shard count
63- self . mask ( ) + 1
42+ Sharded :: Single ( Lock :: new ( value ( ) ) )
6443 }
6544
6645 /// The shard is selected by hashing `val` with `FxHasher`.
@@ -75,9 +54,24 @@ impl<T> Sharded<T> {
7554 }
7655
7756 #[ inline]
78- pub fn get_shard_by_index ( & self , i : usize ) -> & Lock < T > {
79- // SAFETY: The index get ANDed with the mask, ensuring it is always inbounds.
80- unsafe { & self . shards . get_unchecked ( i & self . mask ( ) ) . 0 }
57+ pub fn get_shard_by_index ( & self , _i : usize ) -> & Lock < T > {
58+ match self {
59+ Self :: Single ( single) => & single,
60+ #[ cfg( parallel_compiler) ]
61+ Self :: Shards ( shards) => {
62+ // SAFETY: The index gets ANDed with the shard mask, ensuring it is always inbounds.
63+ unsafe { & shards. get_unchecked ( _i & ( SHARDS - 1 ) ) . 0 }
64+ }
65+ }
66+ }
67+
68+ #[ inline]
69+ fn count ( & self ) -> usize {
70+ match self {
71+ Self :: Single ( ..) => 1 ,
72+ #[ cfg( parallel_compiler) ]
73+ Self :: Shards ( ..) => SHARDS ,
74+ }
8175 }
8276
8377 pub fn lock_shards ( & self ) -> Vec < LockGuard < ' _ , T > > {
0 commit comments