@@ -34,7 +34,7 @@ namespace BitFaster.Caching.Lfu
3434 [ DebuggerDisplay ( "Count = {Count}/{Capacity}" ) ]
3535 public sealed class ConcurrentLfu < K , V > : ICache < K , V > , IAsyncCache < K , V > , IBoundedPolicy
3636 {
37- private const int MaxWriteBufferRetries = 16 ;
37+ private const int MaxWriteBufferRetries = 64 ;
3838
3939 private readonly ConcurrentDictionary < K , LfuNode < K , V > > dictionary ;
4040
@@ -308,8 +308,6 @@ private static void TakeCandidatesInLruOrder(LfuNodeList<K, V> lru, List<LfuNode
308308
309309 private void AfterWrite ( LfuNode < K , V > node )
310310 {
311- var spinner = new SpinWait ( ) ;
312-
313311 for ( int i = 0 ; i < MaxWriteBufferRetries ; i ++ )
314312 {
315313 if ( writeBuffer . TryAdd ( node ) == BufferStatus . Success )
@@ -319,19 +317,31 @@ private void AfterWrite(LfuNode<K, V> node)
319317 }
320318
321319 TryScheduleDrain ( ) ;
322-
323- spinner . SpinOnce ( ) ;
324320 }
325321
326322 lock ( this . maintenanceLock )
327323 {
324+ // aggressively try to exit the lock early before doing full maintenance
325+ var status = BufferStatus . Contended ;
326+ while ( status != BufferStatus . Full )
327+ {
328+ status = writeBuffer . TryAdd ( node ) ;
329+
330+ if ( status == BufferStatus . Success )
331+ {
332+ ScheduleAfterWrite ( ) ;
333+ return ;
334+ }
335+ }
336+
328337 // if the write was dropped from the buffer, explicitly pass it to maintenance
329338 Maintenance ( node ) ;
330339 }
331340 }
332341
333342 private void ScheduleAfterWrite ( )
334343 {
344+ var spinner = new SpinWait ( ) ;
335345 while ( true )
336346 {
337347 switch ( this . drainStatus . Status ( ) )
@@ -352,6 +362,7 @@ private void ScheduleAfterWrite()
352362 case DrainStatus . ProcessingToRequired :
353363 return ;
354364 }
365+ spinner . SpinOnce ( ) ;
355366 }
356367 }
357368
@@ -424,29 +435,32 @@ private bool Maintenance(LfuNode<K, V> droppedWrite = null)
424435 var localDrainBuffer = RentDrainBuffer ( ) ;
425436
426437 // extract to a buffer before doing book keeping work, ~2x faster
427- var count = readBuffer . DrainTo ( localDrainBuffer ) ;
438+ int readCount = readBuffer . DrainTo ( localDrainBuffer ) ;
428439
429- for ( int i = 0 ; i < count ; i ++ )
440+ for ( int i = 0 ; i < readCount ; i ++ )
430441 {
431442 this . cmSketch . Increment ( localDrainBuffer [ i ] . Key ) ;
432443 }
433444
434- for ( int i = 0 ; i < count ; i ++ )
445+ for ( int i = 0 ; i < readCount ; i ++ )
435446 {
436447 OnAccess ( localDrainBuffer [ i ] ) ;
437448 }
438449
439- var wasDrained = count == 0 ;
440- count = this . writeBuffer . DrainTo ( localDrainBuffer ) ;
450+ int writeCount = this . writeBuffer . DrainTo ( localDrainBuffer ) ;
441451
442- for ( int i = 0 ; i < count ; i ++ )
452+ for ( int i = 0 ; i < writeCount ; i ++ )
443453 {
444454 OnWrite ( localDrainBuffer [ i ] ) ;
445455 }
446456
457+ // we are done only when both buffers are empty
458+ var done = readCount == 0 & writeCount == 0 ;
459+
447460 if ( droppedWrite != null )
448461 {
449462 OnWrite ( droppedWrite ) ;
463+ done = true ;
450464 }
451465
452466 ReturnDrainBuffer ( localDrainBuffer ) ;
@@ -458,14 +472,14 @@ private bool Maintenance(LfuNode<K, V> droppedWrite = null)
458472 // Reset to idle if either
459473 // 1. We drained both input buffers (all work done)
460474 // 2. or scheduler is foreground (since don't run continuously on the foreground)
461- if ( ( wasDrained || ! scheduler . IsBackground ) &&
475+ if ( ( done || ! scheduler . IsBackground ) &&
462476 ( this . drainStatus . Status ( ) != DrainStatus . ProcessingToIdle ||
463477 ! this . drainStatus . Cas ( DrainStatus . ProcessingToIdle , DrainStatus . Idle ) ) )
464478 {
465479 this . drainStatus . Set ( DrainStatus . Required ) ;
466480 }
467481
468- return wasDrained ;
482+ return done ;
469483 }
470484
471485 private void OnAccess ( LfuNode < K , V > node )
@@ -595,7 +609,7 @@ private void EvictFromMain(LfuNode<K, V> candidate)
595609 while ( this . windowLru . Count + this . probationLru . Count + this . protectedLru . Count > this . Capacity )
596610 {
597611 // bail when we run out of options
598- if ( candidate == null || victim == null | | victim == candidate )
612+ if ( candidate == null | victim == null | victim == candidate )
599613 {
600614 break ;
601615 }
0 commit comments