@@ -21,26 +21,35 @@ public class FastCache<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>,
2121 /// <param name="cleanupJobInterval">cleanup interval in milliseconds, default is 10000</param>
2222 public FastCache ( int cleanupJobInterval = 10000 )
2323 {
24- _cleanUpTimer = new Timer ( _EvictExpired , null , cleanupJobInterval , cleanupJobInterval ) ;
24+ _cleanUpTimer = new Timer ( s => EvictExpired ( ) , null , cleanupJobInterval , cleanupJobInterval ) ;
25+ }
2526
26- void _EvictExpired ( object state )
27+ /// <summary>
28+ /// Cleans up expired items (dont' wait for the background job)
29+ /// </summary>
30+ public void EvictExpired ( )
31+ {
32+ //Eviction already started by another thread? forget it, lets move on
33+ if ( Monitor . TryEnter ( _cleanUpTimer ) ) //use the timer-object for our lock, it's local, private and instance-type, so its ok
2734 {
28- //overlapped execution? forget it, lets move on
29- if ( Monitor . TryEnter ( _cleanUpTimer ) ) //use the timer-object for our lock, it's local, private and instance-type, so its ok
35+ try
3036 {
31- try
32- {
33- foreach ( var p in _dict )
34- {
35- if ( p . Value . IsExpired ( ) )
36- _dict . TryRemove ( p ) ;
37- }
38- }
39- finally
37+ //cache current tick count in a var to prevent calling it every iteration inside "IsExpired()" in a tight loop.
38+ //On a 10000-items cache this allows us to slice 30 microseconds: 330 vs 360 microseconds which is 10% faster
39+ //On a 50000-items cache it's even more: 2.057ms vs 2.817ms which is 35% faster!!
40+ //the bigger the cache the bigger the win
41+ var currTime = Environment . TickCount64 ;
42+
43+ foreach ( var p in _dict )
4044 {
41- Monitor . Exit ( _cleanUpTimer ) ;
45+ if ( currTime > p . Value . TickCountWhenToKill ) //instead of calling "p.Value.IsExpired" we're essentially doing the same thing manually
46+ _dict . TryRemove ( p ) ;
4247 }
4348 }
49+ finally
50+ {
51+ Monitor . Exit ( _cleanUpTimer ) ;
52+ }
4453 }
4554 }
4655
0 commit comments