Skip to content

Commit

Permalink
10-35% faster eviction job by getting ticks outside the loop
Browse files Browse the repository at this point in the history
  • Loading branch information
alex-jitbit committed Jun 16, 2023
1 parent 532565b commit 16177f1
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 14 deletions.
14 changes: 14 additions & 0 deletions FastCache.Benchmarks/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,20 @@ public void GlobalSetup()
}
}

/*
[Benchmark]
public void EvictExpired()
{
_cache.EvictExpired();
}
[Benchmark]
public void EvictExpired2()
{
_cache.EvictExpiredOptimized();
}
*/

[Benchmark]
public void DictionaryLookup()
{
Expand Down
37 changes: 23 additions & 14 deletions FastCache/FastCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,35 @@ public class FastCache<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>,
/// <param name="cleanupJobInterval">cleanup interval in milliseconds, default is 10000</param>
public FastCache(int cleanupJobInterval = 10000)
{
_cleanUpTimer = new Timer(_EvictExpired, null, cleanupJobInterval, cleanupJobInterval);
_cleanUpTimer = new Timer(s => EvictExpired(), null, cleanupJobInterval, cleanupJobInterval);
}

void _EvictExpired(object state)
/// <summary>
/// Cleans up expired items (dont' wait for the background job)
/// </summary>
public void EvictExpired()
{
//Eviction already started by another thread? forget it, lets move on
if (Monitor.TryEnter(_cleanUpTimer)) //use the timer-object for our lock, it's local, private and instance-type, so its ok
{
//overlapped execution? forget it, lets move on
if (Monitor.TryEnter(_cleanUpTimer)) //use the timer-object for our lock, it's local, private and instance-type, so its ok
try
{
try
{
foreach (var p in _dict)
{
if (p.Value.IsExpired())
_dict.TryRemove(p);
}
}
finally
//cache current tick count in a var to prevent calling it every iteration inside "IsExpired()" in a tight loop.
//On a 10000-items cache this allows us to slice 30 microseconds: 330 vs 360 microseconds which is 10% faster
//On a 50000-items cache it's even more: 2.057ms vs 2.817ms which is 35% faster!!
//the bigger the cache the bigger the win
var currTime = Environment.TickCount64;

foreach (var p in _dict)
{
Monitor.Exit(_cleanUpTimer);
if (currTime > p.Value.TickCountWhenToKill) //instead of calling "p.Value.IsExpired" we're essentially doing the same thing manually
_dict.TryRemove(p);
}
}
finally
{
Monitor.Exit(_cleanUpTimer);
}
}
}

Expand Down

1 comment on commit 16177f1

@alex-jitbit
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed #3

Please sign in to comment.