@@ -10,31 +10,20 @@ namespace Orleans.Runtime;
1010
1111/// <summary>
1212/// A striped dictionary that distributes entries across multiple internal dictionaries
13- /// to reduce lock contention. The stripe is determined by bits embedded in the CorrelationId .
13+ /// to reduce lock contention by hashing correlation ids across stripes .
1414/// </summary>
1515/// <typeparam name="TValue">The type of values stored in the dictionary.</typeparam>
16- internal sealed class StripedCallbackDictionary < TValue > : IEnumerable < KeyValuePair < CorrelationId , TValue > >
16+ internal sealed class StripedCallbackDictionary < TValue > : IEnumerable < TValue >
1717 where TValue : notnull
1818{
19- /// <summary>
20- /// The number of bits used to identify the stripe (stored in the upper bits of the CorrelationId).
21- /// </summary>
22- public const int StripeBits = 7 ;
23-
24- /// <summary>
25- /// The number of stripes (must be a power of 2).
26- /// </summary>
27- public const int StripeCount = 1 << StripeBits ; // 128 stripes
28-
29- /// <summary>
30- /// Mask to extract the stripe index from the upper bits.
31- /// </summary>
32- private const long StripeMask = ( long ) ( StripeCount - 1 ) << ( 64 - StripeBits ) ;
19+ private const int StripeBits = 7 ;
20+ // Fibonacci hashing spreads sequential and strided ids using one multiply and shift.
21+ private const ulong HashFactor = 11_400_714_819_323_198_485 ;
3322
3423 /// <summary>
35- /// The shift amount to move the stripe bits to the lowest position .
24+ /// The number of stripes .
3625 /// </summary>
37- private const int StripeShift = 64 - StripeBits ;
26+ public const int StripeCount = 1 << StripeBits ;
3827
3928 private readonly Stripe [ ] _stripes ;
4029
@@ -48,40 +37,14 @@ public StripedCallbackDictionary()
4837 }
4938
5039 /// <summary>
51- /// Encodes a stripe index into the upper bits of a base value to create a CorrelationId.
52- /// </summary>
53- /// <param name="baseValue">The base value (e.g., from an incrementing counter XORed with a seed).</param>
54- /// <param name="stripeIndex">The stripe index (typically derived from thread id).</param>
55- /// <returns>A CorrelationId with the stripe encoded in the upper bits.</returns>
56- [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
57- public static CorrelationId CreateCorrelationId ( long baseValue , int stripeIndex )
58- {
59- // Clear the upper StripeBits of the base value and set the stripe index there
60- long maskedBase = baseValue & ~ StripeMask ;
61- long stripeValue = ( long ) ( stripeIndex & ( StripeCount - 1 ) ) << StripeShift ;
62- return new CorrelationId ( maskedBase | stripeValue ) ;
63- }
64-
65- /// <summary>
66- /// Extracts the stripe index from a CorrelationId.
40+ /// Computes the stripe index for a correlation id.
6741 /// </summary>
6842 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
6943 public static int GetStripeIndex ( CorrelationId correlationId )
70- {
71- return ( int ) ( ( correlationId . ToInt64 ( ) & StripeMask ) >>> StripeShift ) ;
72- }
44+ => ( int ) ( unchecked ( ( ulong ) correlationId . ToInt64 ( ) * HashFactor ) >> ( 64 - StripeBits ) ) ;
7345
7446 /// <summary>
75- /// Gets the stripe index for the current thread. Use this when creating new CorrelationIds.
76- /// </summary>
77- [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
78- public static int GetCurrentThreadStripeIndex ( )
79- {
80- return Environment . CurrentManagedThreadId & ( StripeCount - 1 ) ;
81- }
82-
83- /// <summary>
84- /// Gets the stripe for the given correlation id.
47+ /// Gets the stripe for the given callback id.
8548 /// </summary>
8649 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
8750 private Stripe GetStripe ( CorrelationId correlationId )
@@ -93,38 +56,38 @@ private Stripe GetStripe(CorrelationId correlationId)
9356 /// Attempts to add the specified key and value to the dictionary.
9457 /// </summary>
9558 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
96- public bool TryAdd ( CorrelationId key , TValue value )
59+ public bool TryAdd ( GrainId owner , CorrelationId id , TValue value )
9760 {
98- var stripe = GetStripe ( key ) ;
61+ var stripe = GetStripe ( id ) ;
9962 lock ( stripe . Lock )
10063 {
101- return stripe . Dictionary . TryAdd ( key , value ) ;
64+ return stripe . Dictionary . TryAdd ( new ( owner , id ) , value ) ;
10265 }
10366 }
10467
10568 /// <summary>
10669 /// Attempts to get the value associated with the specified key.
10770 /// </summary>
10871 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
109- public bool TryGetValue ( CorrelationId key , [ NotNullWhen ( true ) ] out TValue ? value )
72+ public bool TryGetValue ( GrainId owner , CorrelationId id , [ NotNullWhen ( true ) ] out TValue ? value )
11073 {
111- var stripe = GetStripe ( key ) ;
74+ var stripe = GetStripe ( id ) ;
11275 lock ( stripe . Lock )
11376 {
114- return stripe . Dictionary . TryGetValue ( key , out value ) ;
77+ return stripe . Dictionary . TryGetValue ( new ( owner , id ) , out value ) ;
11578 }
11679 }
11780
11881 /// <summary>
11982 /// Attempts to remove the value with the specified key.
12083 /// </summary>
12184 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
122- public bool TryRemove ( CorrelationId key , [ NotNullWhen ( true ) ] out TValue ? value )
85+ public bool TryRemove ( GrainId owner , CorrelationId id , [ NotNullWhen ( true ) ] out TValue ? value )
12386 {
124- var stripe = GetStripe ( key ) ;
87+ var stripe = GetStripe ( id ) ;
12588 lock ( stripe . Lock )
12689 {
127- return stripe . Dictionary . Remove ( key , out value ) ;
90+ return stripe . Dictionary . Remove ( new ( owner , id ) , out value ) ;
12891 }
12992 }
13093
@@ -150,16 +113,16 @@ public int Count
150113 /// <summary>
151114 /// Counts items matching a predicate across all stripes.
152115 /// </summary>
153- public int CountWhere ( Func < KeyValuePair < CorrelationId , TValue > , bool > predicate )
116+ public int CountWhere ( Func < TValue , bool > predicate )
154117 {
155118 int count = 0 ;
156119 foreach ( var stripe in _stripes )
157120 {
158121 lock ( stripe . Lock )
159122 {
160- foreach ( var kvp in stripe . Dictionary )
123+ foreach ( var value in stripe . Dictionary . Values )
161124 {
162- if ( predicate ( kvp ) )
125+ if ( predicate ( value ) )
163126 {
164127 count ++ ;
165128 }
@@ -175,21 +138,23 @@ public int CountWhere(Func<KeyValuePair<CorrelationId, TValue>, bool> predicate)
175138 /// </summary>
176139 public Enumerator GetEnumerator ( ) => new ( this ) ;
177140
178- IEnumerator < KeyValuePair < CorrelationId , TValue > > IEnumerable < KeyValuePair < CorrelationId , TValue > > . GetEnumerator ( ) => GetEnumerator ( ) ;
141+ IEnumerator < TValue > IEnumerable < TValue > . GetEnumerator ( ) => GetEnumerator ( ) ;
179142
180143 IEnumerator IEnumerable . GetEnumerator ( ) => GetEnumerator ( ) ;
181144
182145 private sealed class Stripe
183146 {
184147 public readonly object Lock = new ( ) ;
185- public readonly Dictionary < CorrelationId , TValue > Dictionary = new ( ) ;
148+ public readonly Dictionary < CallbackKey , TValue > Dictionary = new ( ) ;
186149 }
187150
188- public sealed class Enumerator : IEnumerator < KeyValuePair < CorrelationId , TValue > >
151+ private readonly record struct CallbackKey ( GrainId Owner , CorrelationId Id ) ;
152+
153+ public sealed class Enumerator : IEnumerator < TValue >
189154 {
190155 private readonly StripedCallbackDictionary < TValue > _dictionary ;
191156 private int _stripeIndex ;
192- private KeyValuePair < CorrelationId , TValue > [ ] ? _currentSnapshot ;
157+ private TValue [ ] ? _currentSnapshot ;
193158 private int _snapshotCount ;
194159 private int _snapshotIndex ;
195160
@@ -202,7 +167,7 @@ internal Enumerator(StripedCallbackDictionary<TValue> dictionary)
202167 _snapshotIndex = - 1 ;
203168 }
204169
205- public KeyValuePair < CorrelationId , TValue > Current => _currentSnapshot ! [ _snapshotIndex ] ;
170+ public TValue Current => _currentSnapshot ! [ _snapshotIndex ] ;
206171
207172 object IEnumerator . Current => Current ;
208173
@@ -236,11 +201,11 @@ public bool MoveNext()
236201 {
237202 if ( stripe . Dictionary . Count > 0 )
238203 {
239- _currentSnapshot = ArrayPool < KeyValuePair < CorrelationId , TValue > > . Shared . Rent ( stripe . Dictionary . Count ) ;
204+ _currentSnapshot = ArrayPool < TValue > . Shared . Rent ( stripe . Dictionary . Count ) ;
240205 _snapshotCount = 0 ;
241- foreach ( var pair in stripe . Dictionary )
206+ foreach ( var value in stripe . Dictionary . Values )
242207 {
243- _currentSnapshot [ _snapshotCount ++ ] = pair ;
208+ _currentSnapshot [ _snapshotCount ++ ] = value ;
244209 }
245210 _snapshotIndex = - 1 ;
246211 }
@@ -265,9 +230,9 @@ private void ReturnSnapshot()
265230 {
266231 if ( _currentSnapshot is { } snapshot )
267232 {
268- ArrayPool < KeyValuePair < CorrelationId , TValue > > . Shared . Return (
233+ ArrayPool < TValue > . Shared . Return (
269234 snapshot ,
270- clearArray : RuntimeHelpers . IsReferenceOrContainsReferences < KeyValuePair < CorrelationId , TValue > > ( ) ) ;
235+ clearArray : RuntimeHelpers . IsReferenceOrContainsReferences < TValue > ( ) ) ;
271236 _currentSnapshot = null ;
272237 _snapshotCount = 0 ;
273238 }
0 commit comments