Skip to content

Conversation

@bitfaster
Copy link
Owner

@bitfaster bitfaster commented Nov 11, 2023

Implement an expire after access eviction policy with a fixed time to live. This replicates the sliding window expiration provided by MemoryCache and the expireAfterAccess policy implemented in Caffeine:

expireAfterAccess(long, TimeUnit): Expire entries after the specified duration has passed since the entry was last accessed by a read or a write. This could be desirable if the cached data is bound to a session and expires due to inactivity.

Summary of changes:

  • Implemented the AfterAccessLongTicksPolicy item policy, with different versions for .NET standard and .NET3/6, to use the faster Environment.TickCount64 where available.
  • ConcurrentLruBuilder now supports WithExpireAfterAccess
    • Supports both metrics/no metrics, creates ConcurrentLruCore with AfterAccessLongTicksPolicy
    • Specifying both WithExpireAfterAccess and WithExpireAfterWrite throws an exception.
  • CachePolicy now includes an optional ExpireAfterAccess property. This is mapped to underlying cache policy via ConcurrentLruCore.CreatePolicy

@coveralls
Copy link

coveralls commented Nov 12, 2023

Coverage Status

coverage: 98.433% (+0.1%) from 98.323%
when pulling e3842c1 on users/alexpeck/lruexpireafterread
into 4782d2e on main.

@bitfaster bitfaster linked an issue Nov 12, 2023 that may be closed by this pull request
@bitfaster bitfaster changed the title ConcurrentLru expire after read eviction policy ConcurrentLru expire after access eviction policy Nov 12, 2023
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ItemDestination RouteHot(LongTickCountLruItem<K, V> item)
{
if (this.ShouldDiscard(item))
Copy link
Owner Author

@bitfaster bitfaster Nov 12, 2023

Choose a reason for hiding this comment

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

There is a possible optimization here (which also applies to TLRu): all of the route methods call into ShouldDiscard, which evaluates the time most likely 3 times per cycle.

When warmed up, RouteHot is always called first (because we always try to dequeue from hot). During warm up, this may not happen.

If we change core to call route hot as a throw away call from warmup, we could eliminate some time calls in the normal case.

Copy link
Owner Author

Choose a reason for hiding this comment

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

The RouteHot trick could also be applied to TrimExpired routine, which will also make many dupe time calls.

@bitfaster
Copy link
Owner Author

Builder doesn't request events - so AfterAccess is comparable to FastTLru.

Method Runtime Mean Error StdDev Ratio Code Size Allocated
ConcurrentDictionary .NET 6.0 6.005 ns 0.1425 ns 0.1333 ns 1.00 1,523 B -
FastConcurrentLru .NET 6.0 7.334 ns 0.1724 ns 0.1985 ns 1.22 7,047 B -
ConcurrentLru .NET 6.0 11.715 ns 0.2579 ns 0.2649 ns 1.95 4,684 B -
AtomicFastLru .NET 6.0 17.155 ns 0.3543 ns 0.3791 ns 2.86 NA -
FastConcurrentTLru .NET 6.0 8.912 ns 0.1986 ns 0.1858 ns 1.48 4,777 B -
ConcurrentTLru .NET 6.0 13.054 ns 0.2859 ns 0.2808 ns 2.17 5,022 B -
ConcurrentLruAfter .NET 6.0 9.774 ns 0.2185 ns 0.2683 ns 1.62 4,913 B -
ConcurrentLfu .NET 6.0 20.932 ns 0.4016 ns 0.7034 ns 3.47 NA -
ClassicLru .NET 6.0 35.018 ns 0.7031 ns 0.7523 ns 5.83 NA -
RuntimeMemoryCacheGet .NET 6.0 95.834 ns 1.3727 ns 1.2840 ns 15.97 49 B 32 B
ExtensionsMemoryCacheGet .NET 6.0 43.147 ns 0.8660 ns 0.8100 ns 7.19 78 B 24 B
ConcurrentDictionary .NET Framework 4.8 9.936 ns 0.2125 ns 0.1884 ns 1.00 4,127 B -
FastConcurrentLru .NET Framework 4.8 10.627 ns 0.2416 ns 0.3141 ns 1.06 27,372 B -
ConcurrentLru .NET Framework 4.8 16.777 ns 0.3568 ns 0.5117 ns 1.67 27,676 B -
AtomicFastLru .NET Framework 4.8 23.617 ns 0.4655 ns 0.4126 ns 2.38 358 B -
FastConcurrentTLru .NET Framework 4.8 35.286 ns 0.5640 ns 0.5000 ns 3.55 27,644 B -
ConcurrentTLru .NET Framework 4.8 39.065 ns 0.3370 ns 0.2814 ns 3.93 27,996 B -
ConcurrentLruAfter .NET Framework 4.8 36.371 ns 0.5639 ns 0.5275 ns 3.66 358 B -
ConcurrentLfu .NET Framework 4.8 31.591 ns 0.5264 ns 0.4666 ns 3.18 NA -
ClassicLru .NET Framework 4.8 38.896 ns 0.6161 ns 0.5763 ns 3.92 NA -
RuntimeMemoryCacheGet .NET Framework 4.8 262.988 ns 5.1218 ns 5.0303 ns 26.41 33 B 32 B
ExtensionsMemoryCacheGet .NET Framework 4.8 85.309 ns 1.6865 ns 1.9421 ns 8.62 82 B 24 B

@bitfaster bitfaster marked this pull request as ready for review November 12, 2023 09:05
@bitfaster bitfaster merged commit ba6ddc0 into main Nov 12, 2023
@bitfaster bitfaster deleted the users/alexpeck/lruexpireafterread branch November 12, 2023 17:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants