@@ -37,18 +37,18 @@ public class ConcurrentLfu<K, V> : ICache<K, V>, IBoundedPolicy
37
37
38
38
public const int BufferSize = 128 ;
39
39
40
- private readonly ConcurrentDictionary < K , LinkedListNode < LfuNode < K , V > > > dictionary ;
40
+ private readonly ConcurrentDictionary < K , LfuNode < K , V > > dictionary ;
41
41
42
- private readonly StripedBuffer < LinkedListNode < LfuNode < K , V > > > readBuffer ;
43
- private readonly StripedBuffer < LinkedListNode < LfuNode < K , V > > > writeBuffer ;
42
+ private readonly StripedBuffer < LfuNode < K , V > > readBuffer ;
43
+ private readonly StripedBuffer < LfuNode < K , V > > writeBuffer ;
44
44
45
45
private readonly CacheMetrics metrics = new CacheMetrics ( ) ;
46
46
47
47
private readonly CmSketch < K > cmSketch ;
48
48
49
- private readonly LinkedList < LfuNode < K , V > > windowLru ;
50
- private readonly LinkedList < LfuNode < K , V > > probationLru ;
51
- private readonly LinkedList < LfuNode < K , V > > protectedLru ;
49
+ private readonly LfuNodeList < K , V > windowLru ;
50
+ private readonly LfuNodeList < K , V > probationLru ;
51
+ private readonly LfuNodeList < K , V > protectedLru ;
52
52
53
53
private readonly LfuCapacityPartition capacity ;
54
54
@@ -58,7 +58,7 @@ public class ConcurrentLfu<K, V> : ICache<K, V>, IBoundedPolicy
58
58
private readonly IScheduler scheduler ;
59
59
60
60
#if NETSTANDARD2_0
61
- private readonly LinkedListNode < LfuNode < K , V > > [ ] localDrainBuffer = new LinkedListNode < LfuNode < K , V > > [ TakeBufferSize ] ;
61
+ private readonly LfuNode < K , V > [ ] localDrainBuffer = new LfuNode < K , V > [ TakeBufferSize ] ;
62
62
#endif
63
63
64
64
public ConcurrentLfu ( int capacity )
@@ -70,16 +70,16 @@ public ConcurrentLfu(int concurrencyLevel, int capacity, IScheduler scheduler)
70
70
{
71
71
var comparer = EqualityComparer < K > . Default ;
72
72
73
- this . dictionary = new ConcurrentDictionary < K , LinkedListNode < LfuNode < K , V > > > ( concurrencyLevel , capacity , comparer ) ;
73
+ this . dictionary = new ConcurrentDictionary < K , LfuNode < K , V > > ( concurrencyLevel , capacity , comparer ) ;
74
74
75
- this . readBuffer = new StripedBuffer < LinkedListNode < LfuNode < K , V > > > ( concurrencyLevel , BufferSize ) ;
76
- this . writeBuffer = new StripedBuffer < LinkedListNode < LfuNode < K , V > > > ( concurrencyLevel , BufferSize ) ;
75
+ this . readBuffer = new StripedBuffer < LfuNode < K , V > > ( concurrencyLevel , BufferSize ) ;
76
+ this . writeBuffer = new StripedBuffer < LfuNode < K , V > > ( concurrencyLevel , BufferSize ) ;
77
77
78
78
this . cmSketch = new CmSketch < K > ( 1 , comparer ) ;
79
79
this . cmSketch . EnsureCapacity ( capacity ) ;
80
- this . windowLru = new LinkedList < LfuNode < K , V > > ( ) ;
81
- this . probationLru = new LinkedList < LfuNode < K , V > > ( ) ;
82
- this . protectedLru = new LinkedList < LfuNode < K , V > > ( ) ;
80
+ this . windowLru = new LfuNodeList < K , V > ( ) ;
81
+ this . probationLru = new LfuNodeList < K , V > ( ) ;
82
+ this . protectedLru = new LfuNodeList < K , V > ( ) ;
83
83
84
84
this . capacity = new LfuCapacityPartition ( capacity ) ;
85
85
@@ -109,7 +109,7 @@ public void AddOrUpdate(K key, V value)
109
109
return ;
110
110
}
111
111
112
- var node = new LinkedListNode < LfuNode < K , V > > ( new LfuNode < K , V > ( key , value ) ) ;
112
+ var node = new LfuNode < K , V > ( key , value ) ;
113
113
if ( this . dictionary . TryAdd ( key , node ) )
114
114
{
115
115
AfterWrite ( node ) ;
@@ -133,7 +133,7 @@ public void Clear()
133
133
public void Trim ( int itemCount )
134
134
{
135
135
itemCount = Math . Min ( itemCount , this . Count ) ;
136
- var candidates = new List < LinkedListNode < LfuNode < K , V > > > ( itemCount ) ;
136
+ var candidates = new List < LfuNode < K , V > > ( itemCount ) ;
137
137
138
138
// TODO: this is LRU order eviction, Caffeine void evictFromMain(int candidates) is based on frequency
139
139
lock ( maintenanceLock )
@@ -146,7 +146,7 @@ public void Trim(int itemCount)
146
146
147
147
foreach ( var candidate in candidates )
148
148
{
149
- this . TryRemove ( candidate . Value . Key ) ;
149
+ this . TryRemove ( candidate . Key ) ;
150
150
}
151
151
}
152
152
@@ -159,11 +159,11 @@ public V GetOrAdd(K key, Func<K, V> valueFactory)
159
159
return value ;
160
160
}
161
161
162
- var node = new LinkedListNode < LfuNode < K , V > > ( new LfuNode < K , V > ( key , valueFactory ( key ) ) ) ;
162
+ var node = new LfuNode < K , V > ( key , valueFactory ( key ) ) ;
163
163
if ( this . dictionary . TryAdd ( key , node ) )
164
164
{
165
165
AfterWrite ( node ) ;
166
- return node . Value . Value ;
166
+ return node . Value ;
167
167
}
168
168
}
169
169
}
@@ -178,7 +178,7 @@ public bool TryGet(K key, out V value)
178
178
{
179
179
TryScheduleDrain ( ) ;
180
180
}
181
- value = node . Value . Value ;
181
+ value = node . Value ;
182
182
return true ;
183
183
}
184
184
@@ -192,7 +192,7 @@ public bool TryRemove(K key)
192
192
{
193
193
if ( this . dictionary . TryRemove ( key , out var node ) )
194
194
{
195
- node . Value . WasRemoved = true ;
195
+ node . WasRemoved = true ;
196
196
AfterWrite ( node ) ;
197
197
return true ;
198
198
}
@@ -204,7 +204,7 @@ public bool TryUpdate(K key, V value)
204
204
{
205
205
if ( this . dictionary . TryGetValue ( key , out var node ) )
206
206
{
207
- node . Value . Value = value ;
207
+ node . Value = value ;
208
208
209
209
// It's ok for this to be lossy, since the node is already tracked
210
210
// and we will just lose ordering/hit count, but not orphan the node.
@@ -225,11 +225,11 @@ public IEnumerator<KeyValuePair<K, V>> GetEnumerator()
225
225
{
226
226
foreach ( var kvp in this . dictionary )
227
227
{
228
- yield return new KeyValuePair < K , V > ( kvp . Key , kvp . Value . Value . Value ) ;
228
+ yield return new KeyValuePair < K , V > ( kvp . Key , kvp . Value . Value ) ;
229
229
}
230
230
}
231
231
232
- private static void TakeCandidatesInLruOrder ( LinkedList < LfuNode < K , V > > lru , List < LinkedListNode < LfuNode < K , V > > > candidates , int itemCount )
232
+ private static void TakeCandidatesInLruOrder ( LfuNodeList < K , V > lru , List < LfuNode < K , V > > candidates , int itemCount )
233
233
{
234
234
var curr = lru . First ;
235
235
@@ -240,7 +240,7 @@ private static void TakeCandidatesInLruOrder(LinkedList<LfuNode<K, V>> lru, List
240
240
}
241
241
}
242
242
243
- private void AfterWrite ( LinkedListNode < LfuNode < K , V > > node )
243
+ private void AfterWrite ( LfuNode < K , V > node )
244
244
{
245
245
var spinner = new SpinWait ( ) ;
246
246
@@ -351,16 +351,14 @@ private void DrainBuffers()
351
351
}
352
352
}
353
353
354
- const int takeBufferSize = 1024 ;
355
-
356
354
private bool Maintenance ( )
357
355
{
358
356
this . drainStatus . Set ( DrainStatus . ProcessingToIdle ) ;
359
357
360
358
bool wasDrained = false ;
361
359
362
360
#if ! NETSTANDARD2_0
363
- var localDrainBuffer = ArrayPool < LinkedListNode < LfuNode < K , V > > > . Shared . Rent ( TakeBufferSize ) ;
361
+ var localDrainBuffer = ArrayPool < LfuNode < K , V > > . Shared . Rent ( TakeBufferSize ) ;
364
362
#endif
365
363
int maxSweeps = 1 ;
366
364
int count = 0 ;
@@ -374,7 +372,7 @@ private bool Maintenance()
374
372
375
373
for ( int i = 0 ; i < count ; i ++ )
376
374
{
377
- this . cmSketch . Increment ( localDrainBuffer [ i ] . Value . Key ) ;
375
+ this . cmSketch . Increment ( localDrainBuffer [ i ] . Key ) ;
378
376
}
379
377
380
378
for ( int i = 0 ; i < count ; i ++ )
@@ -393,7 +391,7 @@ private bool Maintenance()
393
391
}
394
392
395
393
#if ! NETSTANDARD2_0
396
- ArrayPool < LinkedListNode < LfuNode < K , V > > > . Shared . Return ( localDrainBuffer ) ;
394
+ ArrayPool < LfuNode < K , V > > . Shared . Return ( localDrainBuffer ) ;
397
395
#endif
398
396
399
397
// TODO: hill climb
@@ -411,18 +409,18 @@ private bool Maintenance()
411
409
return wasDrained ;
412
410
}
413
411
414
- private void OnAccess ( LinkedListNode < LfuNode < K , V > > node )
412
+ private void OnAccess ( LfuNode < K , V > node )
415
413
{
416
414
// there was a cache hit even if the item was removed or is not yet added.
417
415
this . metrics . requestHitCount ++ ;
418
416
419
417
// Node is added to read buffer while it is removed by maintenance, or it is read before it has been added.
420
- if ( node . List == null )
418
+ if ( node . list == null )
421
419
{
422
420
return ;
423
421
}
424
422
425
- switch ( node . Value . Position )
423
+ switch ( node . Position )
426
424
{
427
425
case Position . Window :
428
426
this . windowLru . MoveToEnd ( node ) ;
@@ -436,27 +434,27 @@ private void OnAccess(LinkedListNode<LfuNode<K, V>> node)
436
434
}
437
435
}
438
436
439
- private void OnWrite ( LinkedListNode < LfuNode < K , V > > node )
437
+ private void OnWrite ( LfuNode < K , V > node )
440
438
{
441
439
// Nodes can be removed while they are in the write buffer, in which case they should
442
440
// not be added back into the LRU.
443
- if ( node . Value . WasRemoved )
441
+ if ( node . WasRemoved )
444
442
{
445
- if ( node . List != null )
443
+ if ( node . list != null )
446
444
{
447
- node . List . Remove ( node ) ;
445
+ node . list . Remove ( node ) ;
448
446
}
449
447
450
448
return ;
451
449
}
452
450
453
- this . cmSketch . Increment ( node . Value . Key ) ;
451
+ this . cmSketch . Increment ( node . Key ) ;
454
452
455
453
// node can already be in one of the queues due to update
456
- switch ( node . Value . Position )
454
+ switch ( node . Position )
457
455
{
458
456
case Position . Window :
459
- if ( node . List == null )
457
+ if ( node . list == null )
460
458
{
461
459
this . windowLru . AddLast ( node ) ;
462
460
TryEvict ( ) ;
@@ -490,42 +488,42 @@ private void TryEvict()
490
488
if ( this . protectedLru . Count < capacity . Protected )
491
489
{
492
490
this . protectedLru . AddLast ( candidate ) ;
493
- candidate . Value . Position = Position . Protected ;
491
+ candidate . Position = Position . Protected ;
494
492
return ;
495
493
}
496
494
497
495
this . probationLru . AddLast ( candidate ) ;
498
- candidate . Value . Position = Position . Probation ;
496
+ candidate . Position = Position . Probation ;
499
497
500
498
// remove either candidate or probation.first
501
499
if ( this . probationLru . Count > capacity . Probation )
502
500
{
503
- var c = this . cmSketch . EstimateFrequency ( candidate . Value . Key ) ;
504
- var p = this . cmSketch . EstimateFrequency ( this . probationLru . First . Value . Key ) ;
501
+ var c = this . cmSketch . EstimateFrequency ( candidate . Key ) ;
502
+ var p = this . cmSketch . EstimateFrequency ( this . probationLru . First . Key ) ;
505
503
506
504
// TODO: random factor?
507
505
var victim = ( c > p ) ? this . probationLru . First : candidate ;
508
506
509
- this . dictionary . TryRemove ( victim . Value . Key , out var _ ) ;
510
- victim . List . Remove ( victim ) ;
507
+ this . dictionary . TryRemove ( victim . Key , out var _ ) ;
508
+ victim . list . Remove ( victim ) ;
511
509
512
510
this . metrics . evictedCount ++ ;
513
511
}
514
512
}
515
513
}
516
514
517
- private void PromoteProbation ( LinkedListNode < LfuNode < K , V > > node )
515
+ private void PromoteProbation ( LfuNode < K , V > node )
518
516
{
519
517
this . probationLru . Remove ( node ) ;
520
518
this . protectedLru . AddLast ( node ) ;
521
- node . Value . Position = Position . Protected ;
519
+ node . Position = Position . Protected ;
522
520
523
521
if ( this . protectedLru . Count > capacity . Protected )
524
522
{
525
523
var demoted = this . protectedLru . First ;
526
524
this . protectedLru . RemoveFirst ( ) ;
527
525
528
- demoted . Value . Position = Position . Probation ;
526
+ demoted . Position = Position . Probation ;
529
527
this . probationLru . AddLast ( demoted ) ;
530
528
}
531
529
}
0 commit comments