From 8ce1f14f3993f482d22d27d6672c2e89c9439b07 Mon Sep 17 00:00:00 2001 From: Alex Peck Date: Wed, 10 Nov 2021 14:50:53 -0800 Subject: [PATCH 01/13] outline --- .../Lru/TLruTimeBenchmark.cs | 18 +++++++-------- .../Lru/ConcurrentLruTests.cs | 23 +++++++++++++++++++ .../Lru/HitCounterTests.cs | 6 ++--- .../Lru/NullHitCounterTests.cs | 2 +- BitFaster.Caching/Lru/ConcurrentLru.cs | 12 +++++++--- BitFaster.Caching/Lru/ConcurrentTLru.cs | 12 +++++++--- BitFaster.Caching/Lru/FastConcurrentLru.cs | 6 ++--- BitFaster.Caching/Lru/FastConcurrentTLru.cs | 6 ++--- BitFaster.Caching/Lru/HitCounter.cs | 22 +++++++++++++++++- BitFaster.Caching/Lru/IHitCounter.cs | 4 +++- BitFaster.Caching/Lru/NullHitCounter.cs | 7 +++++- .../Lru/TemplateConcurrentLru.cs | 4 +++- 12 files changed, 93 insertions(+), 29 deletions(-) diff --git a/BitFaster.Caching.Benchmarks/Lru/TLruTimeBenchmark.cs b/BitFaster.Caching.Benchmarks/Lru/TLruTimeBenchmark.cs index 447dea29..ccaff109 100644 --- a/BitFaster.Caching.Benchmarks/Lru/TLruTimeBenchmark.cs +++ b/BitFaster.Caching.Benchmarks/Lru/TLruTimeBenchmark.cs @@ -11,17 +11,17 @@ namespace BitFaster.Caching.Benchmarks.Lru /// public class TLruTimeBenchmark { - private static readonly TemplateConcurrentLru, TLruDateTimePolicy, NullHitCounter> dateTimeTLru - = new TemplateConcurrentLru, TLruDateTimePolicy, NullHitCounter> - (1, 3, EqualityComparer.Default, new TLruDateTimePolicy(TimeSpan.FromSeconds(1)), new NullHitCounter()); + private static readonly TemplateConcurrentLru, TLruDateTimePolicy, NullHitCounter> dateTimeTLru + = new TemplateConcurrentLru, TLruDateTimePolicy, NullHitCounter> + (1, 3, EqualityComparer.Default, new TLruDateTimePolicy(TimeSpan.FromSeconds(1)), new NullHitCounter()); - private static readonly TemplateConcurrentLru, TLruTicksPolicy, NullHitCounter> tickCountTLru - = new TemplateConcurrentLru, TLruTicksPolicy, NullHitCounter> - (1, 3, EqualityComparer.Default, new TLruTicksPolicy(TimeSpan.FromSeconds(1)), new NullHitCounter()); + private static readonly TemplateConcurrentLru, TLruTicksPolicy, NullHitCounter> tickCountTLru + = new TemplateConcurrentLru, TLruTicksPolicy, NullHitCounter> + (1, 3, EqualityComparer.Default, new TLruTicksPolicy(TimeSpan.FromSeconds(1)), new NullHitCounter()); - private static readonly TemplateConcurrentLru, TLruLongTicksPolicy, NullHitCounter> stopwatchTLru - = new TemplateConcurrentLru, TLruLongTicksPolicy, NullHitCounter> - (1, 3, EqualityComparer.Default, new TLruLongTicksPolicy(TimeSpan.FromSeconds(1)), new NullHitCounter()); + private static readonly TemplateConcurrentLru, TLruLongTicksPolicy, NullHitCounter> stopwatchTLru + = new TemplateConcurrentLru, TLruLongTicksPolicy, NullHitCounter> + (1, 3, EqualityComparer.Default, new TLruLongTicksPolicy(TimeSpan.FromSeconds(1)), new NullHitCounter()); [Benchmark(Baseline = true)] public void DateTimeUtcNow() diff --git a/BitFaster.Caching.UnitTests/Lru/ConcurrentLruTests.cs b/BitFaster.Caching.UnitTests/Lru/ConcurrentLruTests.cs index 90d9cf70..10bf7a2f 100644 --- a/BitFaster.Caching.UnitTests/Lru/ConcurrentLruTests.cs +++ b/BitFaster.Caching.UnitTests/Lru/ConcurrentLruTests.cs @@ -359,6 +359,29 @@ public void WhenValueExpiresItIsDisposed() disposableValueFactory.Items[1].IsDisposed.Should().BeFalse(); } + [Fact] + public void WhenValueExpiresItemRemovedEventIsFired() + { + var lruEvents = new ConcurrentLru(1, 6, EqualityComparer.Default); + lruEvents.ItemRemoved += OnLruItemRemoved; + + for (int i = 0; i < 5; i++) + { + lruEvents.GetOrAdd(i, i => i); + } + + removedItems.Count.Should().Be(1); + removedItems[0].Key.Should().Be(0); + removedItems[0].Value.Should().Be(0); + } + + private List> removedItems = new List>(); + + private void OnLruItemRemoved(object sender, ItemRemovedEventArgs e) + { + removedItems.Add(e); + } + [Fact] public void WhenKeyExistsTryRemoveRemovesItemAndReturnsTrue() { diff --git a/BitFaster.Caching.UnitTests/Lru/HitCounterTests.cs b/BitFaster.Caching.UnitTests/Lru/HitCounterTests.cs index d973703e..811a99c4 100644 --- a/BitFaster.Caching.UnitTests/Lru/HitCounterTests.cs +++ b/BitFaster.Caching.UnitTests/Lru/HitCounterTests.cs @@ -12,7 +12,7 @@ public class HitCounterTests [Fact] public void WhenHitCountAndTotalCountAreEqualRatioIs1() { - HitCounter counter = new HitCounter(); + HitCounter counter = new HitCounter(); counter.IncrementHit(); @@ -22,7 +22,7 @@ public void WhenHitCountAndTotalCountAreEqualRatioIs1() [Fact] public void WhenHitCountIsEqualToMissCountRatioIsHalf() { - HitCounter counter = new HitCounter(); + HitCounter counter = new HitCounter(); counter.IncrementMiss(); counter.IncrementHit(); @@ -33,7 +33,7 @@ public void WhenHitCountIsEqualToMissCountRatioIsHalf() [Fact] public void WhenTotalCountIsZeroRatioReturnsZero() { - HitCounter counter = new HitCounter(); + HitCounter counter = new HitCounter(); counter.HitRatio.Should().Be(0.0); } diff --git a/BitFaster.Caching.UnitTests/Lru/NullHitCounterTests.cs b/BitFaster.Caching.UnitTests/Lru/NullHitCounterTests.cs index 14372a82..259cb146 100644 --- a/BitFaster.Caching.UnitTests/Lru/NullHitCounterTests.cs +++ b/BitFaster.Caching.UnitTests/Lru/NullHitCounterTests.cs @@ -9,7 +9,7 @@ namespace BitFaster.Caching.UnitTests.Lru { public class NullHitCounterTests { - private NullHitCounter counter = new NullHitCounter(); + private NullHitCounter counter = new NullHitCounter(); [Fact] public void HitRatioIsZero() diff --git a/BitFaster.Caching/Lru/ConcurrentLru.cs b/BitFaster.Caching/Lru/ConcurrentLru.cs index de133902..7f75ca4d 100644 --- a/BitFaster.Caching/Lru/ConcurrentLru.cs +++ b/BitFaster.Caching/Lru/ConcurrentLru.cs @@ -7,7 +7,7 @@ namespace BitFaster.Caching.Lru { /// - public sealed class ConcurrentLru : TemplateConcurrentLru, LruPolicy, HitCounter> + public sealed class ConcurrentLru : TemplateConcurrentLru, LruPolicy, HitCounter> { /// /// Initializes a new instance of the ConcurrentLru class with the specified capacity that has the default @@ -15,7 +15,7 @@ public sealed class ConcurrentLru : TemplateConcurrentLru /// The maximum number of elements that the ConcurrentLru can contain. public ConcurrentLru(int capacity) - : base(Defaults.ConcurrencyLevel, capacity, EqualityComparer.Default, new LruPolicy(), new HitCounter()) + : base(Defaults.ConcurrencyLevel, capacity, EqualityComparer.Default, new LruPolicy(), new HitCounter()) { } @@ -27,7 +27,7 @@ public ConcurrentLru(int capacity) /// The maximum number of elements that the ConcurrentLru can contain. /// The IEqualityComparer implementation to use when comparing keys. public ConcurrentLru(int concurrencyLevel, int capacity, IEqualityComparer comparer) - : base(concurrencyLevel, capacity, comparer, new LruPolicy(), new HitCounter()) + : base(concurrencyLevel, capacity, comparer, new LruPolicy(), new HitCounter()) { } @@ -35,5 +35,11 @@ public ConcurrentLru(int concurrencyLevel, int capacity, IEqualityComparer co /// Gets the ratio of hits to misses, where a value of 1 indicates 100% hits. /// public double HitRatio => this.hitCounter.HitRatio; + + public event EventHandler> ItemRemoved + { + add { this.hitCounter.ItemRemoved += value; } + remove { this.hitCounter.ItemRemoved -= value; } + } } } diff --git a/BitFaster.Caching/Lru/ConcurrentTLru.cs b/BitFaster.Caching/Lru/ConcurrentTLru.cs index 72fed08c..6e6294ff 100644 --- a/BitFaster.Caching/Lru/ConcurrentTLru.cs +++ b/BitFaster.Caching/Lru/ConcurrentTLru.cs @@ -7,7 +7,7 @@ namespace BitFaster.Caching.Lru { /// - public sealed class ConcurrentTLru : TemplateConcurrentLru, TLruLongTicksPolicy, HitCounter> + public sealed class ConcurrentTLru : TemplateConcurrentLru, TLruLongTicksPolicy, HitCounter> { /// /// Initializes a new instance of the ConcurrentTLru class with the specified capacity and time to live that has the default @@ -16,7 +16,7 @@ public sealed class ConcurrentTLru : TemplateConcurrentLruThe maximum number of elements that the ConcurrentTLru can contain. /// The time to live for cached values. public ConcurrentTLru(int capacity, TimeSpan timeToLive) - : base(Defaults.ConcurrencyLevel, capacity, EqualityComparer.Default, new TLruLongTicksPolicy(timeToLive), new HitCounter()) + : base(Defaults.ConcurrencyLevel, capacity, EqualityComparer.Default, new TLruLongTicksPolicy(timeToLive), new HitCounter()) { } @@ -29,7 +29,7 @@ public ConcurrentTLru(int capacity, TimeSpan timeToLive) /// The IEqualityComparer implementation to use when comparing keys. /// The time to live for cached values. public ConcurrentTLru(int concurrencyLevel, int capacity, IEqualityComparer comparer, TimeSpan timeToLive) - : base(concurrencyLevel, capacity, comparer, new TLruLongTicksPolicy(timeToLive), new HitCounter()) + : base(concurrencyLevel, capacity, comparer, new TLruLongTicksPolicy(timeToLive), new HitCounter()) { } @@ -37,5 +37,11 @@ public ConcurrentTLru(int concurrencyLevel, int capacity, IEqualityComparer c /// Gets the ratio of hits to misses, where a value of 1 indicates 100% hits. /// public double HitRatio => this.hitCounter.HitRatio; + + public event EventHandler> ItemRemoved + { + add { this.hitCounter.ItemRemoved += value; } + remove { this.hitCounter.ItemRemoved -= value; } + } } } diff --git a/BitFaster.Caching/Lru/FastConcurrentLru.cs b/BitFaster.Caching/Lru/FastConcurrentLru.cs index 10db549d..49842be1 100644 --- a/BitFaster.Caching/Lru/FastConcurrentLru.cs +++ b/BitFaster.Caching/Lru/FastConcurrentLru.cs @@ -5,7 +5,7 @@ namespace BitFaster.Caching.Lru { /// - public sealed class FastConcurrentLru : TemplateConcurrentLru, LruPolicy, NullHitCounter> + public sealed class FastConcurrentLru : TemplateConcurrentLru, LruPolicy, NullHitCounter> { /// /// Initializes a new instance of the FastConcurrentLru class with the specified capacity that has the default @@ -13,7 +13,7 @@ public sealed class FastConcurrentLru : TemplateConcurrentLru /// The maximum number of elements that the FastConcurrentLru can contain. public FastConcurrentLru(int capacity) - : base(Defaults.ConcurrencyLevel, capacity, EqualityComparer.Default, new LruPolicy(), new NullHitCounter()) + : base(Defaults.ConcurrencyLevel, capacity, EqualityComparer.Default, new LruPolicy(), new NullHitCounter()) { } @@ -25,7 +25,7 @@ public FastConcurrentLru(int capacity) /// The maximum number of elements that the FastConcurrentLru can contain. /// The IEqualityComparer implementation to use when comparing keys. public FastConcurrentLru(int concurrencyLevel, int capacity, IEqualityComparer comparer) - : base(concurrencyLevel, capacity, comparer, new LruPolicy(), new NullHitCounter()) + : base(concurrencyLevel, capacity, comparer, new LruPolicy(), new NullHitCounter()) { } } diff --git a/BitFaster.Caching/Lru/FastConcurrentTLru.cs b/BitFaster.Caching/Lru/FastConcurrentTLru.cs index 75e0545d..44056c11 100644 --- a/BitFaster.Caching/Lru/FastConcurrentTLru.cs +++ b/BitFaster.Caching/Lru/FastConcurrentTLru.cs @@ -5,7 +5,7 @@ namespace BitFaster.Caching.Lru { /// - public sealed class FastConcurrentTLru : TemplateConcurrentLru, TLruLongTicksPolicy, NullHitCounter> + public sealed class FastConcurrentTLru : TemplateConcurrentLru, TLruLongTicksPolicy, NullHitCounter> { /// /// Initializes a new instance of the FastConcurrentTLru class with the specified capacity and time to live that has the default @@ -14,7 +14,7 @@ public sealed class FastConcurrentTLru : TemplateConcurrentLruThe maximum number of elements that the FastConcurrentTLru can contain. /// The time to live for cached values. public FastConcurrentTLru(int capacity, TimeSpan timeToLive) - : base(Defaults.ConcurrencyLevel, capacity, EqualityComparer.Default, new TLruLongTicksPolicy(timeToLive), new NullHitCounter()) + : base(Defaults.ConcurrencyLevel, capacity, EqualityComparer.Default, new TLruLongTicksPolicy(timeToLive), new NullHitCounter()) { } @@ -27,7 +27,7 @@ public FastConcurrentTLru(int capacity, TimeSpan timeToLive) /// The IEqualityComparer implementation to use when comparing keys. /// The time to live for cached values. public FastConcurrentTLru(int concurrencyLevel, int capacity, IEqualityComparer comparer, TimeSpan timeToLive) - : base(concurrencyLevel, capacity, comparer, new TLruLongTicksPolicy(timeToLive), new NullHitCounter()) + : base(concurrencyLevel, capacity, comparer, new TLruLongTicksPolicy(timeToLive), new NullHitCounter()) { } } diff --git a/BitFaster.Caching/Lru/HitCounter.cs b/BitFaster.Caching/Lru/HitCounter.cs index 96553fdb..dbc309fd 100644 --- a/BitFaster.Caching/Lru/HitCounter.cs +++ b/BitFaster.Caching/Lru/HitCounter.cs @@ -8,7 +8,7 @@ namespace BitFaster.Caching.Lru { - public struct HitCounter : IHitCounter + public struct HitCounter : IHitCounter { private long hitCount; private long missCount; @@ -17,6 +17,8 @@ public struct HitCounter : IHitCounter public long Total => this.hitCount + this.missCount; + public EventHandler> ItemRemoved; + public void IncrementMiss() { Interlocked.Increment(ref this.missCount); @@ -26,5 +28,23 @@ public void IncrementHit() { Interlocked.Increment(ref this.hitCount); } + + public void OnItemRemoved(K key, V value) + { + this.ItemRemoved?.Invoke(this, new ItemRemovedEventArgs(key, value)); + } + } + + public class ItemRemovedEventArgs + { + public ItemRemovedEventArgs(K key, V value) + { + this.Key = key; + this.Value = value; + } + + public K Key { get; } + + public V Value { get; } } } diff --git a/BitFaster.Caching/Lru/IHitCounter.cs b/BitFaster.Caching/Lru/IHitCounter.cs index 5dff4450..e7d1b614 100644 --- a/BitFaster.Caching/Lru/IHitCounter.cs +++ b/BitFaster.Caching/Lru/IHitCounter.cs @@ -6,12 +6,14 @@ namespace BitFaster.Caching.Lru { - public interface IHitCounter + public interface IHitCounter { void IncrementMiss(); void IncrementHit(); + void OnItemRemoved(K key, V value); + double HitRatio { get; } } } diff --git a/BitFaster.Caching/Lru/NullHitCounter.cs b/BitFaster.Caching/Lru/NullHitCounter.cs index 431729e0..ce033aa6 100644 --- a/BitFaster.Caching/Lru/NullHitCounter.cs +++ b/BitFaster.Caching/Lru/NullHitCounter.cs @@ -7,7 +7,7 @@ namespace BitFaster.Caching.Lru { - public struct NullHitCounter : IHitCounter + public struct NullHitCounter : IHitCounter { public double HitRatio => 0.0; @@ -20,5 +20,10 @@ public void IncrementMiss() public void IncrementHit() { } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void OnItemRemoved(K key, V value) + { + } } } diff --git a/BitFaster.Caching/Lru/TemplateConcurrentLru.cs b/BitFaster.Caching/Lru/TemplateConcurrentLru.cs index eb4a7861..37dc285c 100644 --- a/BitFaster.Caching/Lru/TemplateConcurrentLru.cs +++ b/BitFaster.Caching/Lru/TemplateConcurrentLru.cs @@ -30,7 +30,7 @@ namespace BitFaster.Caching.Lru public class TemplateConcurrentLru : ICache where I : LruItem where P : struct, IPolicy - where H : struct, IHitCounter + where H : struct, IHitCounter { private readonly ConcurrentDictionary dictionary; @@ -423,6 +423,8 @@ private void Move(I item, ItemDestination where) { item.WasRemoved = true; + this.hitCounter.OnItemRemoved(item.Key, item.Value); + lock (item) { Disposer.Dispose(item.Value); From e60f833e9737f725946aca7371216eb9107e309f Mon Sep 17 00:00:00 2001 From: Alex Peck Date: Wed, 10 Nov 2021 20:00:05 -0800 Subject: [PATCH 02/13] cycle bench --- .../Lru/LruCycleBench.cs | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 BitFaster.Caching.Benchmarks/Lru/LruCycleBench.cs diff --git a/BitFaster.Caching.Benchmarks/Lru/LruCycleBench.cs b/BitFaster.Caching.Benchmarks/Lru/LruCycleBench.cs new file mode 100644 index 00000000..414f3692 --- /dev/null +++ b/BitFaster.Caching.Benchmarks/Lru/LruCycleBench.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using BenchmarkDotNet.Attributes; +using BitFaster.Caching.Lru; + +namespace BitFaster.Caching.Benchmarks.Lru +{ + [DisassemblyDiagnoser(printSource: true)] + [MemoryDiagnoser] + public class LruCycleBench + { + private static readonly ClassicLru classicLru = new(8, 9, EqualityComparer.Default); + private static readonly ConcurrentLru concurrentLru = new(8, 9, EqualityComparer.Default); + private static readonly ConcurrentTLru concurrentTlru = new(8, 9, EqualityComparer.Default, TimeSpan.FromMinutes(10)); + private static readonly FastConcurrentLru fastConcurrentLru = new(8, 9, EqualityComparer.Default); + private static readonly FastConcurrentTLru fastConcurrentTLru = new(8, 9, EqualityComparer.Default, TimeSpan.FromMinutes(1)); + + [Benchmark()] + public void FastConcurrentLru() + { + Func func = x => x; + + for (int i = 0; i < 128; i++) + fastConcurrentLru.GetOrAdd(i, func); + } + + [Benchmark()] + public void ConcurrentLru() + { + Func func = x => x; + + for (int i = 0; i < 128; i++) + concurrentLru.GetOrAdd(i, func); + } + + [Benchmark()] + public void FastConcurrentTLru() + { + Func func = x => x; + + for (int i = 0; i < 128; i++) + fastConcurrentTLru.GetOrAdd(i, func); + } + + [Benchmark()] + public void ConcurrentTLru() + { + Func func = x => x; + + for (int i = 0; i < 128; i++) + concurrentTlru.GetOrAdd(i, func); + } + } +} From 5ec58cbeb377e7177203884341d06203f136560e Mon Sep 17 00:00:00 2001 From: Alex Peck Date: Thu, 11 Nov 2021 17:47:44 -0800 Subject: [PATCH 03/13] docs etc --- .../Lru/LruCycleBench.cs | 49 +++++++++++++++- .../Lru/LruJustGetOrAdd.cs | 17 ++++++ .../Lru/ConcurrentLruTests.cs | 58 +++++++++++++++---- .../Lru/ConcurrentTLruTests.cs | 45 ++++++++++++++ BitFaster.Caching/Lru/ConcurrentLru.cs | 3 + BitFaster.Caching/Lru/ConcurrentTLru.cs | 3 + BitFaster.Caching/Lru/HitCounter.cs | 17 +----- BitFaster.Caching/Lru/IHitCounter.cs | 2 +- BitFaster.Caching/Lru/ItemRemovedEventArgs.cs | 24 ++++++++ BitFaster.Caching/Lru/ItemRemovedReason.cs | 24 ++++++++ BitFaster.Caching/Lru/NullHitCounter.cs | 2 +- .../Lru/TemplateConcurrentLru.cs | 4 +- 12 files changed, 219 insertions(+), 29 deletions(-) create mode 100644 BitFaster.Caching/Lru/ItemRemovedEventArgs.cs create mode 100644 BitFaster.Caching/Lru/ItemRemovedReason.cs diff --git a/BitFaster.Caching.Benchmarks/Lru/LruCycleBench.cs b/BitFaster.Caching.Benchmarks/Lru/LruCycleBench.cs index 414f3692..4fb2a760 100644 --- a/BitFaster.Caching.Benchmarks/Lru/LruCycleBench.cs +++ b/BitFaster.Caching.Benchmarks/Lru/LruCycleBench.cs @@ -8,17 +8,46 @@ namespace BitFaster.Caching.Benchmarks.Lru { + //BenchmarkDotNet=v0.13.1, OS=Windows 10.0.22000 + //Intel Xeon W-2133 CPU 3.60GHz, 1 CPU, 12 logical and 6 physical cores + //.NET SDK= 6.0.100 + // [Host] : .NET 6.0.0 (6.0.21.52210), X64 RyuJIT + // DefaultJob : .NET 6.0.0 (6.0.21.52210), X64 RyuJIT + + + //| Method | Mean | Error | StdDev | Ratio | Code Size | Gen 0 | Allocated | + //|------------------- |---------:|---------:|---------:|------:|----------:|-------:|----------:| + //| FastConcurrentLru | 22.74 us | 0.083 us | 0.069 us | 1.00 | 0 KB | 2.1362 | 9 KB | + //| ConcurrentLru | 24.02 us | 0.097 us | 0.086 us | 1.06 | 0 KB | 2.1362 | 9 KB | + //| ConcurrentLruEvent | 24.82 us | 0.117 us | 0.104 us | 1.09 | 0 KB | 4.2725 | 18 KB | + //| FastConcurrentTLru | 31.38 us | 0.066 us | 0.058 us | 1.38 | 1 KB | 2.3193 | 10 KB | + //| ConcurrentTLru | 32.03 us | 0.175 us | 0.147 us | 1.41 | 1 KB | 2.3193 | 10 KB | + //| ClassicLru | 16.26 us | 0.146 us | 0.129 us | 0.72 | 1 KB | 3.2959 | 14 KB | [DisassemblyDiagnoser(printSource: true)] [MemoryDiagnoser] public class LruCycleBench { private static readonly ClassicLru classicLru = new(8, 9, EqualityComparer.Default); private static readonly ConcurrentLru concurrentLru = new(8, 9, EqualityComparer.Default); + private static readonly ConcurrentLru concurrentLruEvent = new(8, 9, EqualityComparer.Default); private static readonly ConcurrentTLru concurrentTlru = new(8, 9, EqualityComparer.Default, TimeSpan.FromMinutes(10)); private static readonly FastConcurrentLru fastConcurrentLru = new(8, 9, EqualityComparer.Default); private static readonly FastConcurrentTLru fastConcurrentTLru = new(8, 9, EqualityComparer.Default, TimeSpan.FromMinutes(1)); - [Benchmark()] + [GlobalSetup] + public void GlobalSetup() + { + concurrentLruEvent.ItemRemoved += OnItemRemoved; + } + + private int field; + + private void OnItemRemoved(object sender, ItemRemovedEventArgs e) + { + field = e.Key; + } + + [Benchmark(Baseline =true)] public void FastConcurrentLru() { Func func = x => x; @@ -36,6 +65,15 @@ public void ConcurrentLru() concurrentLru.GetOrAdd(i, func); } + [Benchmark()] + public void ConcurrentLruEvent() + { + Func func = x => x; + + for (int i = 0; i < 128; i++) + concurrentLruEvent.GetOrAdd(i, func); + } + [Benchmark()] public void FastConcurrentTLru() { @@ -53,5 +91,14 @@ public void ConcurrentTLru() for (int i = 0; i < 128; i++) concurrentTlru.GetOrAdd(i, func); } + + [Benchmark()] + public void ClassicLru() + { + Func func = x => x; + + for (int i = 0; i < 128; i++) + classicLru.GetOrAdd(i, func); + } } } diff --git a/BitFaster.Caching.Benchmarks/Lru/LruJustGetOrAdd.cs b/BitFaster.Caching.Benchmarks/Lru/LruJustGetOrAdd.cs index 7351bf92..12b04efa 100644 --- a/BitFaster.Caching.Benchmarks/Lru/LruJustGetOrAdd.cs +++ b/BitFaster.Caching.Benchmarks/Lru/LruJustGetOrAdd.cs @@ -12,6 +12,23 @@ namespace BitFaster.Caching.Benchmarks { + //BenchmarkDotNet=v0.13.1, OS=Windows 10.0.22000 + //Intel Xeon W-2133 CPU 3.60GHz, 1 CPU, 12 logical and 6 physical cores + //.NET SDK= 6.0.100 + // [Host] : .NET 6.0.0 (6.0.21.52210), X64 RyuJIT + // DefaultJob : .NET 6.0.0 (6.0.21.52210), X64 RyuJIT + + + //| Method | Mean | Error | StdDev | Ratio | RatioSD | Code Size | Gen 0 | Allocated | + //|------------------------- |-----------:|----------:|----------:|------:|--------:|----------:|-------:|----------:| + //| ConcurrentDictionary | 7.730 ns | 0.0457 ns | 0.0427 ns | 1.00 | 0.00 | 340 B | - | - | + //| FastConcurrentLru | 9.524 ns | 0.0147 ns | 0.0122 ns | 1.23 | 0.01 | 427 B | - | - | + //| ConcurrentLru | 13.872 ns | 0.2476 ns | 0.2195 ns | 1.79 | 0.03 | 453 B | - | - | + //| FastConcurrentTLru | 25.819 ns | 0.1336 ns | 0.1250 ns | 3.34 | 0.03 | 613 B | - | - | + //| ConcurrentTLru | 30.176 ns | 0.5099 ns | 0.4520 ns | 3.90 | 0.06 | 688 B | - | - | + //| ClassicLru | 48.357 ns | 0.6203 ns | 0.5802 ns | 6.26 | 0.08 | 738 B | - | - | + //| RuntimeMemoryCacheGet | 106.552 ns | 0.5145 ns | 0.4561 ns | 13.78 | 0.09 | 49 B | 0.0074 | 32 B | + //| ExtensionsMemoryCacheGet | 97.458 ns | 1.2148 ns | 1.1363 ns | 12.61 | 0.14 | 78 B | 0.0055 | 24 B | [DisassemblyDiagnoser(printSource: true)] [MemoryDiagnoser] public class LruJustGetOrAdd diff --git a/BitFaster.Caching.UnitTests/Lru/ConcurrentLruTests.cs b/BitFaster.Caching.UnitTests/Lru/ConcurrentLruTests.cs index 10bf7a2f..e84008f1 100644 --- a/BitFaster.Caching.UnitTests/Lru/ConcurrentLruTests.cs +++ b/BitFaster.Caching.UnitTests/Lru/ConcurrentLruTests.cs @@ -20,6 +20,13 @@ public class ConcurrentLruTests private ConcurrentLru lru = new ConcurrentLru(1, hotCap + warmCap + coldCap, EqualityComparer.Default); private ValueFactory valueFactory = new ValueFactory(); + private List> removedItems = new List>(); + + private void OnLruItemRemoved(object sender, ItemRemovedEventArgs e) + { + removedItems.Add(e); + } + public ConcurrentLruTests(ITestOutputHelper testOutputHelper) { this.testOutputHelper = testOutputHelper; @@ -360,26 +367,41 @@ public void WhenValueExpiresItIsDisposed() } [Fact] - public void WhenValueExpiresItemRemovedEventIsFired() + public void WhenValueEvictedItemRemovedEventIsFired() { var lruEvents = new ConcurrentLru(1, 6, EqualityComparer.Default); lruEvents.ItemRemoved += OnLruItemRemoved; - for (int i = 0; i < 5; i++) + for (int i = 0; i < 6; i++) { - lruEvents.GetOrAdd(i, i => i); + lruEvents.GetOrAdd(i+1, i => i + 1); } - removedItems.Count.Should().Be(1); - removedItems[0].Key.Should().Be(0); - removedItems[0].Value.Should().Be(0); - } + removedItems.Count.Should().Be(2); - private List> removedItems = new List>(); + removedItems[0].Key.Should().Be(1); + removedItems[0].Value.Should().Be(2); + removedItems[0].Reason.Should().Be(ItemRemovedReason.Evicted); - private void OnLruItemRemoved(object sender, ItemRemovedEventArgs e) + removedItems[1].Key.Should().Be(2); + removedItems[1].Value.Should().Be(3); + removedItems[1].Reason.Should().Be(ItemRemovedReason.Evicted); + } + + [Fact] + public void WhenItemRemovedEventIsUnregisteredEventIsNotFired() { - removedItems.Add(e); + var lruEvents = new ConcurrentLru(1, 6, EqualityComparer.Default); + + lruEvents.ItemRemoved += OnLruItemRemoved; + lruEvents.ItemRemoved -= OnLruItemRemoved; + + for (int i = 0; i < 6; i++) + { + lruEvents.GetOrAdd(i + 1, i => i + 1); + } + + removedItems.Count.Should().Be(0); } [Fact] @@ -403,6 +425,22 @@ public void WhenItemIsRemovedItIsDisposed() disposableValueFactory.Items[1].IsDisposed.Should().BeTrue(); } + [Fact] + public void WhenItemIsRemovedRemovedEventIsFired() + { + var lruEvents = new ConcurrentLru(1, 6, EqualityComparer.Default); + lruEvents.ItemRemoved += OnLruItemRemoved; + + lruEvents.GetOrAdd(1, i => i+2); + + lruEvents.TryRemove(1).Should().BeTrue(); + + removedItems.Count().Should().Be(1); + removedItems[0].Key.Should().Be(1); + removedItems[0].Value.Should().Be(3); + removedItems[0].Reason.Should().Be(ItemRemovedReason.Removed); + } + [Fact] public void WhenKeyDoesNotExistTryRemoveReturnsFalse() { diff --git a/BitFaster.Caching.UnitTests/Lru/ConcurrentTLruTests.cs b/BitFaster.Caching.UnitTests/Lru/ConcurrentTLruTests.cs index 4157e492..c274ef3b 100644 --- a/BitFaster.Caching.UnitTests/Lru/ConcurrentTLruTests.cs +++ b/BitFaster.Caching.UnitTests/Lru/ConcurrentTLruTests.cs @@ -16,6 +16,13 @@ public class ConcurrentTLruTests private ValueFactory valueFactory = new ValueFactory(); + private List> removedItems = new List>(); + + private void OnLruItemRemoved(object sender, ItemRemovedEventArgs e) + { + removedItems.Add(e); + } + public ConcurrentTLruTests() { lru = new ConcurrentTLru(1, capacity, EqualityComparer.Default, timeToLive); @@ -47,6 +54,44 @@ public async Task WhenItemIsExpiredItIsRemoved() lru.TryGet(1, out var value).Should().BeFalse(); } + [Fact] + public void WhenValueEvictedItemRemovedEventIsFired() + { + var lruEvents = new ConcurrentTLru(1, 6, EqualityComparer.Default, timeToLive); + lruEvents.ItemRemoved += OnLruItemRemoved; + + for (int i = 0; i < 6; i++) + { + lruEvents.GetOrAdd(i + 1, i => i + 1); + } + + removedItems.Count.Should().Be(2); + + removedItems[0].Key.Should().Be(1); + removedItems[0].Value.Should().Be(2); + removedItems[0].Reason.Should().Be(ItemRemovedReason.Evicted); + + removedItems[1].Key.Should().Be(2); + removedItems[1].Value.Should().Be(3); + removedItems[1].Reason.Should().Be(ItemRemovedReason.Evicted); + } + + [Fact] + public void WhenItemRemovedEventIsUnregisteredEventIsNotFired() + { + var lruEvents = new ConcurrentTLru(1, 6, EqualityComparer.Default, timeToLive); + + lruEvents.ItemRemoved += OnLruItemRemoved; + lruEvents.ItemRemoved -= OnLruItemRemoved; + + for (int i = 0; i < 6; i++) + { + lruEvents.GetOrAdd(i + 1, i => i + 1); + } + + removedItems.Count.Should().Be(0); + } + [Fact] public void WhenItemIsAddedThenRetrievedHitRatioIsHalf() { diff --git a/BitFaster.Caching/Lru/ConcurrentLru.cs b/BitFaster.Caching/Lru/ConcurrentLru.cs index 7f75ca4d..5d40418c 100644 --- a/BitFaster.Caching/Lru/ConcurrentLru.cs +++ b/BitFaster.Caching/Lru/ConcurrentLru.cs @@ -36,6 +36,9 @@ public ConcurrentLru(int concurrencyLevel, int capacity, IEqualityComparer co /// public double HitRatio => this.hitCounter.HitRatio; + /// + /// Occurs when an item is removed from the cache. + /// public event EventHandler> ItemRemoved { add { this.hitCounter.ItemRemoved += value; } diff --git a/BitFaster.Caching/Lru/ConcurrentTLru.cs b/BitFaster.Caching/Lru/ConcurrentTLru.cs index 6e6294ff..0c93d123 100644 --- a/BitFaster.Caching/Lru/ConcurrentTLru.cs +++ b/BitFaster.Caching/Lru/ConcurrentTLru.cs @@ -38,6 +38,9 @@ public ConcurrentTLru(int concurrencyLevel, int capacity, IEqualityComparer c /// public double HitRatio => this.hitCounter.HitRatio; + /// + /// Occurs when an item is removed from the cache. + /// public event EventHandler> ItemRemoved { add { this.hitCounter.ItemRemoved += value; } diff --git a/BitFaster.Caching/Lru/HitCounter.cs b/BitFaster.Caching/Lru/HitCounter.cs index dbc309fd..9dda925e 100644 --- a/BitFaster.Caching/Lru/HitCounter.cs +++ b/BitFaster.Caching/Lru/HitCounter.cs @@ -29,22 +29,9 @@ public void IncrementHit() Interlocked.Increment(ref this.hitCount); } - public void OnItemRemoved(K key, V value) + public void OnItemRemoved(K key, V value, ItemRemovedReason reason) { - this.ItemRemoved?.Invoke(this, new ItemRemovedEventArgs(key, value)); + this.ItemRemoved?.Invoke(this, new ItemRemovedEventArgs(key, value, reason)); } } - - public class ItemRemovedEventArgs - { - public ItemRemovedEventArgs(K key, V value) - { - this.Key = key; - this.Value = value; - } - - public K Key { get; } - - public V Value { get; } - } } diff --git a/BitFaster.Caching/Lru/IHitCounter.cs b/BitFaster.Caching/Lru/IHitCounter.cs index e7d1b614..5c31edc3 100644 --- a/BitFaster.Caching/Lru/IHitCounter.cs +++ b/BitFaster.Caching/Lru/IHitCounter.cs @@ -12,7 +12,7 @@ public interface IHitCounter void IncrementHit(); - void OnItemRemoved(K key, V value); + void OnItemRemoved(K key, V value, ItemRemovedReason reason); double HitRatio { get; } } diff --git a/BitFaster.Caching/Lru/ItemRemovedEventArgs.cs b/BitFaster.Caching/Lru/ItemRemovedEventArgs.cs new file mode 100644 index 00000000..3e4acf2c --- /dev/null +++ b/BitFaster.Caching/Lru/ItemRemovedEventArgs.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BitFaster.Caching.Lru +{ + public class ItemRemovedEventArgs : EventArgs + { + public ItemRemovedEventArgs(K key, V value, ItemRemovedReason reason) + { + this.Key = key; + this.Value = value; + this.Reason = reason; + } + + public K Key { get; } + + public V Value { get; } + + public ItemRemovedReason Reason { get; } + } +} diff --git a/BitFaster.Caching/Lru/ItemRemovedReason.cs b/BitFaster.Caching/Lru/ItemRemovedReason.cs new file mode 100644 index 00000000..3fe26b25 --- /dev/null +++ b/BitFaster.Caching/Lru/ItemRemovedReason.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BitFaster.Caching.Lru +{ + /// + /// Specifies the reason an item was removed from the Cache. + /// + public enum ItemRemovedReason + { + /// + /// The item is removed from the cache by a remove method call. + /// + Removed, + + /// + /// The item is removed from the cache by the cache eviction policy. + /// + Evicted, + } +} diff --git a/BitFaster.Caching/Lru/NullHitCounter.cs b/BitFaster.Caching/Lru/NullHitCounter.cs index ce033aa6..28d9dff8 100644 --- a/BitFaster.Caching/Lru/NullHitCounter.cs +++ b/BitFaster.Caching/Lru/NullHitCounter.cs @@ -22,7 +22,7 @@ public void IncrementHit() } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void OnItemRemoved(K key, V value) + public void OnItemRemoved(K key, V value, ItemRemovedReason reason) { } } diff --git a/BitFaster.Caching/Lru/TemplateConcurrentLru.cs b/BitFaster.Caching/Lru/TemplateConcurrentLru.cs index 37dc285c..9fffde96 100644 --- a/BitFaster.Caching/Lru/TemplateConcurrentLru.cs +++ b/BitFaster.Caching/Lru/TemplateConcurrentLru.cs @@ -198,6 +198,8 @@ public bool TryRemove(K key) existing.WasAccessed = false; existing.WasRemoved = true; + this.hitCounter.OnItemRemoved(existing.Key, existing.Value, ItemRemovedReason.Removed); + // serialize dispose (common case dispose not thread safe) lock (existing) { @@ -423,7 +425,7 @@ private void Move(I item, ItemDestination where) { item.WasRemoved = true; - this.hitCounter.OnItemRemoved(item.Key, item.Value); + this.hitCounter.OnItemRemoved(item.Key, item.Value, ItemRemovedReason.Evicted); lock (item) { From a98189ead4e96d2e600781a0a09b4dd99d2a85f7 Mon Sep 17 00:00:00 2001 From: Alex Peck Date: Thu, 11 Nov 2021 17:57:15 -0800 Subject: [PATCH 04/13] results --- BitFaster.Caching.Benchmarks/Lru/LruCycleBench.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/BitFaster.Caching.Benchmarks/Lru/LruCycleBench.cs b/BitFaster.Caching.Benchmarks/Lru/LruCycleBench.cs index 4fb2a760..5006a11c 100644 --- a/BitFaster.Caching.Benchmarks/Lru/LruCycleBench.cs +++ b/BitFaster.Caching.Benchmarks/Lru/LruCycleBench.cs @@ -17,12 +17,12 @@ namespace BitFaster.Caching.Benchmarks.Lru //| Method | Mean | Error | StdDev | Ratio | Code Size | Gen 0 | Allocated | //|------------------- |---------:|---------:|---------:|------:|----------:|-------:|----------:| - //| FastConcurrentLru | 22.74 us | 0.083 us | 0.069 us | 1.00 | 0 KB | 2.1362 | 9 KB | - //| ConcurrentLru | 24.02 us | 0.097 us | 0.086 us | 1.06 | 0 KB | 2.1362 | 9 KB | - //| ConcurrentLruEvent | 24.82 us | 0.117 us | 0.104 us | 1.09 | 0 KB | 4.2725 | 18 KB | - //| FastConcurrentTLru | 31.38 us | 0.066 us | 0.058 us | 1.38 | 1 KB | 2.3193 | 10 KB | - //| ConcurrentTLru | 32.03 us | 0.175 us | 0.147 us | 1.41 | 1 KB | 2.3193 | 10 KB | - //| ClassicLru | 16.26 us | 0.146 us | 0.129 us | 0.72 | 1 KB | 3.2959 | 14 KB | + //| FastConcurrentLru | 22.99 us | 0.048 us | 0.037 us | 1.00 | 0 KB | 2.1362 | 9 KB | + //| ConcurrentLru | 23.48 us | 0.107 us | 0.100 us | 1.02 | 0 KB | 2.1362 | 9 KB | + //| ConcurrentLruEvent | 24.54 us | 0.098 us | 0.087 us | 1.07 | 0 KB | 4.2725 | 18 KB | + //| FastConcurrentTLru | 30.99 us | 0.068 us | 0.056 us | 1.35 | 1 KB | 2.3193 | 10 KB | + //| ConcurrentTLru | 32.53 us | 0.247 us | 0.219 us | 1.41 | 1 KB | 2.3193 | 10 KB | + //| ClassicLru | 16.15 us | 0.034 us | 0.029 us | 0.70 | 1 KB | 3.2959 | 14 KB | [DisassemblyDiagnoser(printSource: true)] [MemoryDiagnoser] public class LruCycleBench From 849f08ff6810bdb020e4b786a132a51f925a46f0 Mon Sep 17 00:00:00 2001 From: Alex Peck Date: Thu, 11 Nov 2021 23:20:37 -0800 Subject: [PATCH 05/13] avoid boxing struct --- .../Lru/LruCycleBench.cs | 18 ++++++++++-------- BitFaster.Caching/Lru/HitCounter.cs | 10 +++++++++- BitFaster.Caching/Lru/IHitCounter.cs | 2 ++ BitFaster.Caching/Lru/NullHitCounter.cs | 5 +++++ BitFaster.Caching/Lru/TemplateConcurrentLru.cs | 1 + 5 files changed, 27 insertions(+), 9 deletions(-) diff --git a/BitFaster.Caching.Benchmarks/Lru/LruCycleBench.cs b/BitFaster.Caching.Benchmarks/Lru/LruCycleBench.cs index 5006a11c..58e53d53 100644 --- a/BitFaster.Caching.Benchmarks/Lru/LruCycleBench.cs +++ b/BitFaster.Caching.Benchmarks/Lru/LruCycleBench.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; using BenchmarkDotNet.Attributes; @@ -17,13 +18,13 @@ namespace BitFaster.Caching.Benchmarks.Lru //| Method | Mean | Error | StdDev | Ratio | Code Size | Gen 0 | Allocated | //|------------------- |---------:|---------:|---------:|------:|----------:|-------:|----------:| - //| FastConcurrentLru | 22.99 us | 0.048 us | 0.037 us | 1.00 | 0 KB | 2.1362 | 9 KB | - //| ConcurrentLru | 23.48 us | 0.107 us | 0.100 us | 1.02 | 0 KB | 2.1362 | 9 KB | - //| ConcurrentLruEvent | 24.54 us | 0.098 us | 0.087 us | 1.07 | 0 KB | 4.2725 | 18 KB | - //| FastConcurrentTLru | 30.99 us | 0.068 us | 0.056 us | 1.35 | 1 KB | 2.3193 | 10 KB | - //| ConcurrentTLru | 32.53 us | 0.247 us | 0.219 us | 1.41 | 1 KB | 2.3193 | 10 KB | - //| ClassicLru | 16.15 us | 0.034 us | 0.029 us | 0.70 | 1 KB | 3.2959 | 14 KB | - [DisassemblyDiagnoser(printSource: true)] + //| FastConcurrentLru | 22.86 us | 0.183 us | 0.162 us | 1.00 | 5 KB | 2.1362 | 9 KB | + //| ConcurrentLru | 23.40 us | 0.092 us | 0.077 us | 1.02 | 5 KB | 2.1362 | 9 KB | + //| ConcurrentLruEvent | 24.23 us | 0.097 us | 0.086 us | 1.06 | 5 KB | 3.0823 | 13 KB | + //| FastConcurrentTLru | 31.70 us | 0.087 us | 0.077 us | 1.39 | 6 KB | 2.3193 | 10 KB | + //| ConcurrentTLru | 31.85 us | 0.080 us | 0.071 us | 1.39 | 6 KB | 2.3193 | 10 KB | + //| ClassicLru | 16.35 us | 0.091 us | 0.076 us | 0.72 | 4 KB | 3.2959 | 14 KB | + [DisassemblyDiagnoser(printSource: true, maxDepth: 5)] [MemoryDiagnoser] public class LruCycleBench { @@ -40,8 +41,9 @@ public void GlobalSetup() concurrentLruEvent.ItemRemoved += OnItemRemoved; } - private int field; + public static int field; + [MethodImpl(MethodImplOptions.NoOptimization)] private void OnItemRemoved(object sender, ItemRemovedEventArgs e) { field = e.Key; diff --git a/BitFaster.Caching/Lru/HitCounter.cs b/BitFaster.Caching/Lru/HitCounter.cs index 9dda925e..4617e2c2 100644 --- a/BitFaster.Caching/Lru/HitCounter.cs +++ b/BitFaster.Caching/Lru/HitCounter.cs @@ -19,6 +19,8 @@ public struct HitCounter : IHitCounter public EventHandler> ItemRemoved; + private object eventSource; + public void IncrementMiss() { Interlocked.Increment(ref this.missCount); @@ -31,7 +33,13 @@ public void IncrementHit() public void OnItemRemoved(K key, V value, ItemRemovedReason reason) { - this.ItemRemoved?.Invoke(this, new ItemRemovedEventArgs(key, value, reason)); + // passing 'this' as source boxes the struct, and is anyway the wrong object + this.ItemRemoved?.Invoke(this.eventSource, new ItemRemovedEventArgs(key, value, reason)); + } + + public void SetEventSource(object source) + { + this.eventSource = source; } } } diff --git a/BitFaster.Caching/Lru/IHitCounter.cs b/BitFaster.Caching/Lru/IHitCounter.cs index 5c31edc3..ab61c42e 100644 --- a/BitFaster.Caching/Lru/IHitCounter.cs +++ b/BitFaster.Caching/Lru/IHitCounter.cs @@ -15,5 +15,7 @@ public interface IHitCounter void OnItemRemoved(K key, V value, ItemRemovedReason reason); double HitRatio { get; } + + void SetEventSource(object source); } } diff --git a/BitFaster.Caching/Lru/NullHitCounter.cs b/BitFaster.Caching/Lru/NullHitCounter.cs index 28d9dff8..615389d3 100644 --- a/BitFaster.Caching/Lru/NullHitCounter.cs +++ b/BitFaster.Caching/Lru/NullHitCounter.cs @@ -25,5 +25,10 @@ public void IncrementHit() public void OnItemRemoved(K key, V value, ItemRemovedReason reason) { } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetEventSource(object source) + { + } } } diff --git a/BitFaster.Caching/Lru/TemplateConcurrentLru.cs b/BitFaster.Caching/Lru/TemplateConcurrentLru.cs index 9fffde96..f60c51eb 100644 --- a/BitFaster.Caching/Lru/TemplateConcurrentLru.cs +++ b/BitFaster.Caching/Lru/TemplateConcurrentLru.cs @@ -84,6 +84,7 @@ public TemplateConcurrentLru( this.dictionary = new ConcurrentDictionary(concurrencyLevel, dictionaryCapacity, comparer); this.policy = itemPolicy; this.hitCounter = hitCounter; + this.hitCounter.SetEventSource(this); } // No lock count: https://arbel.net/2013/02/03/best-practices-for-using-concurrentdictionary/ From e1db0a838117bdfa4e6f3bef0edfc8dac58eb015 Mon Sep 17 00:00:00 2001 From: Alex Peck Date: Fri, 12 Nov 2021 12:59:27 -0800 Subject: [PATCH 06/13] seal --- BitFaster.Caching/Lifetime.cs | 2 +- BitFaster.Caching/Scoped.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/BitFaster.Caching/Lifetime.cs b/BitFaster.Caching/Lifetime.cs index 74ebf899..378d3eea 100644 --- a/BitFaster.Caching/Lifetime.cs +++ b/BitFaster.Caching/Lifetime.cs @@ -9,7 +9,7 @@ namespace BitFaster.Caching /// lifetime is disposed. /// /// The type of value - public class Lifetime : IDisposable + public sealed class Lifetime : IDisposable { private readonly Action onDisposeAction; private readonly ReferenceCount refCount; diff --git a/BitFaster.Caching/Scoped.cs b/BitFaster.Caching/Scoped.cs index 6379e442..dad6ee46 100644 --- a/BitFaster.Caching/Scoped.cs +++ b/BitFaster.Caching/Scoped.cs @@ -11,7 +11,7 @@ namespace BitFaster.Caching /// the wrapped object from being diposed until the calling code completes. /// /// The type of scoped value. - public class Scoped : IDisposable where T : IDisposable + public sealed class Scoped : IDisposable where T : IDisposable { private ReferenceCount refCount; private bool isDisposed; From 6843f9c547c59f44e865976ae1178a5c7e1ad4c8 Mon Sep 17 00:00:00 2001 From: Alex Peck Date: Sat, 13 Nov 2021 17:11:19 -0800 Subject: [PATCH 07/13] fix merge --- .../DataStructureBenchmarks.cs | 3 +++ BitFaster.Caching.Benchmarks/DisposerBench.cs | 5 ++++- BitFaster.Caching.Benchmarks/Lru/LruCycleBench.cs | 15 +++++++++------ .../Lru/LruJustGetOrAdd.cs | 15 +++++++++------ BitFaster.Caching.Benchmarks/Lru/LruJustTryGet.cs | 3 +++ BitFaster.Caching.Benchmarks/Lru/LruMultiGet.cs | 2 ++ .../Lru/LruZipDistribution.cs | 3 +++ .../Lru/TLruTimeBenchmark.cs | 3 +++ BitFaster.Caching.Benchmarks/TimeBenchmarks.cs | 3 +++ 9 files changed, 39 insertions(+), 13 deletions(-) diff --git a/BitFaster.Caching.Benchmarks/DataStructureBenchmarks.cs b/BitFaster.Caching.Benchmarks/DataStructureBenchmarks.cs index ff085a89..2d6c77a6 100644 --- a/BitFaster.Caching.Benchmarks/DataStructureBenchmarks.cs +++ b/BitFaster.Caching.Benchmarks/DataStructureBenchmarks.cs @@ -1,4 +1,5 @@ using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -9,6 +10,8 @@ namespace BitFaster.Caching.Benchmarks { + [SimpleJob(RuntimeMoniker.Net48)] + [SimpleJob(RuntimeMoniker.Net60)] [MemoryDiagnoser] public class DataStructureBenchmarks { diff --git a/BitFaster.Caching.Benchmarks/DisposerBench.cs b/BitFaster.Caching.Benchmarks/DisposerBench.cs index 4daf4518..ecafbd61 100644 --- a/BitFaster.Caching.Benchmarks/DisposerBench.cs +++ b/BitFaster.Caching.Benchmarks/DisposerBench.cs @@ -3,6 +3,7 @@ using System.Runtime.CompilerServices; using System.Text; using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.Diagnostics.Runtime.Interop; @@ -10,7 +11,9 @@ namespace BitFaster.Caching.Benchmarks { // Is it possible to write a class to eliminate the dispose code for types that are not IDisposable? // https://github.com/dotnet/runtime/issues/4920 - [DisassemblyDiagnoser(printSource: true)] + [SimpleJob(RuntimeMoniker.Net48)] + [SimpleJob(RuntimeMoniker.Net60)] + [DisassemblyDiagnoser(printSource: true, maxDepth: 3)] [MemoryDiagnoser] public class DisposerBench { diff --git a/BitFaster.Caching.Benchmarks/Lru/LruCycleBench.cs b/BitFaster.Caching.Benchmarks/Lru/LruCycleBench.cs index 58e53d53..edde4829 100644 --- a/BitFaster.Caching.Benchmarks/Lru/LruCycleBench.cs +++ b/BitFaster.Caching.Benchmarks/Lru/LruCycleBench.cs @@ -5,6 +5,7 @@ using System.Text; using System.Threading.Tasks; using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; using BitFaster.Caching.Lru; namespace BitFaster.Caching.Benchmarks.Lru @@ -24,16 +25,18 @@ namespace BitFaster.Caching.Benchmarks.Lru //| FastConcurrentTLru | 31.70 us | 0.087 us | 0.077 us | 1.39 | 6 KB | 2.3193 | 10 KB | //| ConcurrentTLru | 31.85 us | 0.080 us | 0.071 us | 1.39 | 6 KB | 2.3193 | 10 KB | //| ClassicLru | 16.35 us | 0.091 us | 0.076 us | 0.72 | 4 KB | 3.2959 | 14 KB | + [SimpleJob(RuntimeMoniker.Net48)] + [SimpleJob(RuntimeMoniker.Net60)] [DisassemblyDiagnoser(printSource: true, maxDepth: 5)] [MemoryDiagnoser] public class LruCycleBench { - private static readonly ClassicLru classicLru = new(8, 9, EqualityComparer.Default); - private static readonly ConcurrentLru concurrentLru = new(8, 9, EqualityComparer.Default); - private static readonly ConcurrentLru concurrentLruEvent = new(8, 9, EqualityComparer.Default); - private static readonly ConcurrentTLru concurrentTlru = new(8, 9, EqualityComparer.Default, TimeSpan.FromMinutes(10)); - private static readonly FastConcurrentLru fastConcurrentLru = new(8, 9, EqualityComparer.Default); - private static readonly FastConcurrentTLru fastConcurrentTLru = new(8, 9, EqualityComparer.Default, TimeSpan.FromMinutes(1)); + private static readonly ClassicLru classicLru = new ClassicLru(8, 9, EqualityComparer.Default); + private static readonly ConcurrentLru concurrentLru = new ConcurrentLru(8, 9, EqualityComparer.Default); + private static readonly ConcurrentLru concurrentLruEvent = new ConcurrentLru(8, 9, EqualityComparer.Default); + private static readonly ConcurrentTLru concurrentTlru = new ConcurrentTLru(8, 9, EqualityComparer.Default, TimeSpan.FromMinutes(10)); + private static readonly FastConcurrentLru fastConcurrentLru = new FastConcurrentLru(8, 9, EqualityComparer.Default); + private static readonly FastConcurrentTLru fastConcurrentTLru = new FastConcurrentTLru(8, 9, EqualityComparer.Default, TimeSpan.FromMinutes(1)); [GlobalSetup] public void GlobalSetup() diff --git a/BitFaster.Caching.Benchmarks/Lru/LruJustGetOrAdd.cs b/BitFaster.Caching.Benchmarks/Lru/LruJustGetOrAdd.cs index 88c97aed..588d463f 100644 --- a/BitFaster.Caching.Benchmarks/Lru/LruJustGetOrAdd.cs +++ b/BitFaster.Caching.Benchmarks/Lru/LruJustGetOrAdd.cs @@ -1,4 +1,5 @@ using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; using BitFaster.Caching; using BitFaster.Caching.Benchmarks.Lru; using BitFaster.Caching.Lru; @@ -29,17 +30,19 @@ namespace BitFaster.Caching.Benchmarks //| ClassicLru | 49.041 ns | 0.8575 ns | 0.8021 ns | 6.23 | 0.11 | 3,013 B | - | - | //| RuntimeMemoryCacheGet | 107.769 ns | 1.1901 ns | 0.9938 ns | 13.69 | 0.15 | 49 B | 0.0074 | 32 B | //| ExtensionsMemoryCacheGet | 93.188 ns | 0.2321 ns | 0.2171 ns | 11.85 | 0.07 | 78 B | 0.0055 | 24 B | + [SimpleJob(RuntimeMoniker.Net48)] + [SimpleJob(RuntimeMoniker.Net60)] [DisassemblyDiagnoser(printSource: true, maxDepth: 5)] [MemoryDiagnoser] public class LruJustGetOrAdd { - private static readonly ConcurrentDictionary dictionary = new(8, 9, EqualityComparer.Default); + private static readonly ConcurrentDictionary dictionary = new ConcurrentDictionary(8, 9, EqualityComparer.Default); - private static readonly ClassicLru classicLru = new(8, 9, EqualityComparer.Default); - private static readonly ConcurrentLru concurrentLru = new(8, 9, EqualityComparer.Default); - private static readonly ConcurrentTLru concurrentTlru = new(8, 9, EqualityComparer.Default, TimeSpan.FromMinutes(10)); - private static readonly FastConcurrentLru fastConcurrentLru = new(8, 9, EqualityComparer.Default); - private static readonly FastConcurrentTLru fastConcurrentTLru = new(8, 9, EqualityComparer.Default, TimeSpan.FromMinutes(1)); + private static readonly ClassicLru classicLru = new ClassicLru(8, 9, EqualityComparer.Default); + private static readonly ConcurrentLru concurrentLru = new ConcurrentLru(8, 9, EqualityComparer.Default); + private static readonly ConcurrentTLru concurrentTlru = new ConcurrentTLru(8, 9, EqualityComparer.Default, TimeSpan.FromMinutes(10)); + private static readonly FastConcurrentLru fastConcurrentLru = new FastConcurrentLru(8, 9, EqualityComparer.Default); + private static readonly FastConcurrentTLru fastConcurrentTLru = new FastConcurrentTLru(8, 9, EqualityComparer.Default, TimeSpan.FromMinutes(1)); private static readonly int key = 1; private static System.Runtime.Caching.MemoryCache memoryCache = System.Runtime.Caching.MemoryCache.Default; diff --git a/BitFaster.Caching.Benchmarks/Lru/LruJustTryGet.cs b/BitFaster.Caching.Benchmarks/Lru/LruJustTryGet.cs index 13a243a3..a6ff1e52 100644 --- a/BitFaster.Caching.Benchmarks/Lru/LruJustTryGet.cs +++ b/BitFaster.Caching.Benchmarks/Lru/LruJustTryGet.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Text; using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; using BitFaster.Caching.Lru; namespace BitFaster.Caching.Benchmarks.Lru @@ -19,6 +20,8 @@ namespace BitFaster.Caching.Benchmarks.Lru //| ConcurrentDictionary | 4.480 ns | 0.0230 ns | 0.0204 ns | 1.00 | 0.00 | 364 B | - | //| FastConcurrentLru | 7.705 ns | 0.0343 ns | 0.0286 ns | 1.72 | 0.01 | 448 B | - | //| FastConcurrentTLru | 25.350 ns | 0.3301 ns | 0.3088 ns | 5.66 | 0.08 | 546 B | - | + [SimpleJob(RuntimeMoniker.Net48)] + [SimpleJob(RuntimeMoniker.Net60)] [DisassemblyDiagnoser(printSource: true, maxDepth: 5)] [MemoryDiagnoser] public class LruJustTryGet diff --git a/BitFaster.Caching.Benchmarks/Lru/LruMultiGet.cs b/BitFaster.Caching.Benchmarks/Lru/LruMultiGet.cs index 0406621b..f7eb8cba 100644 --- a/BitFaster.Caching.Benchmarks/Lru/LruMultiGet.cs +++ b/BitFaster.Caching.Benchmarks/Lru/LruMultiGet.cs @@ -28,6 +28,8 @@ namespace BitFaster.Caching.Benchmarks.Lru //| ConcurrentTLru | 33.733 ns | 0.6613 ns | 0.6791 ns | 4.02 | 0.09 | 3,539 B | - | - | //| ClassicLru | 52.898 ns | 0.3079 ns | 0.2404 ns | 6.30 | 0.03 | 3,021 B | - | - | //| MemoryCache | 117.075 ns | 1.7664 ns | 1.5658 ns | 13.96 | 0.18 | 94 B | 0.0073 | 32 B | + [SimpleJob(RuntimeMoniker.Net48)] + [SimpleJob(RuntimeMoniker.Net60)] [DisassemblyDiagnoser(printSource: true, maxDepth: 5)] [MemoryDiagnoser] public class LruMultiGet diff --git a/BitFaster.Caching.Benchmarks/Lru/LruZipDistribution.cs b/BitFaster.Caching.Benchmarks/Lru/LruZipDistribution.cs index 8aa300d9..3b9ad3ac 100644 --- a/BitFaster.Caching.Benchmarks/Lru/LruZipDistribution.cs +++ b/BitFaster.Caching.Benchmarks/Lru/LruZipDistribution.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Text; using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; using BitFaster.Caching.Lru; using MathNet.Numerics.Distributions; using MathNet.Numerics.Random; @@ -22,6 +23,8 @@ namespace BitFaster.Caching.Benchmarks.Lru //| ConcurrentLru | 127.4 ns | 0.51 ns | 0.48 ns | 1.14 | 0.01 | 0.0093 | 5,107 B | 41 B | //| FastConcurrentTLru | 175.6 ns | 1.08 ns | 1.01 ns | 1.58 | 0.02 | 0.0100 | 5,911 B | 44 B | //| ConcurrentTLru | 169.7 ns | 0.86 ns | 0.80 ns | 1.52 | 0.02 | 0.0098 | 5,982 B | 43 B | + [SimpleJob(RuntimeMoniker.Net48)] + [SimpleJob(RuntimeMoniker.Net60)] [DisassemblyDiagnoser(printSource: true, maxDepth: 5)] [MemoryDiagnoser] public class LruZipDistribution diff --git a/BitFaster.Caching.Benchmarks/Lru/TLruTimeBenchmark.cs b/BitFaster.Caching.Benchmarks/Lru/TLruTimeBenchmark.cs index ccaff109..f7cec266 100644 --- a/BitFaster.Caching.Benchmarks/Lru/TLruTimeBenchmark.cs +++ b/BitFaster.Caching.Benchmarks/Lru/TLruTimeBenchmark.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Text; using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; using BitFaster.Caching.Lru; namespace BitFaster.Caching.Benchmarks.Lru @@ -9,6 +10,8 @@ namespace BitFaster.Caching.Benchmarks.Lru /// /// Compare different implementations of the TLRU policy. In particular, which clock impl is fastest? /// + [SimpleJob(RuntimeMoniker.Net48)] + [SimpleJob(RuntimeMoniker.Net60)] public class TLruTimeBenchmark { private static readonly TemplateConcurrentLru, TLruDateTimePolicy, NullHitCounter> dateTimeTLru diff --git a/BitFaster.Caching.Benchmarks/TimeBenchmarks.cs b/BitFaster.Caching.Benchmarks/TimeBenchmarks.cs index 31953ea8..bad69370 100644 --- a/BitFaster.Caching.Benchmarks/TimeBenchmarks.cs +++ b/BitFaster.Caching.Benchmarks/TimeBenchmarks.cs @@ -3,9 +3,12 @@ using System.Diagnostics; using System.Text; using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; namespace BitFaster.Caching.Benchmarks { + [SimpleJob(RuntimeMoniker.Net48)] + [SimpleJob(RuntimeMoniker.Net60)] public class TimeBenchmarks { private static readonly Stopwatch sw = Stopwatch.StartNew(); From 80c2c200c916ea5454cc35595c5ec3672b9460b7 Mon Sep 17 00:00:00 2001 From: Alex Peck Date: Sat, 13 Nov 2021 18:36:46 -0800 Subject: [PATCH 08/13] rename hitcounter --- .../Lru/TLruTimeBenchmark.cs | 18 +++++++++--------- .../Lru/HitCounterTests.cs | 6 +++--- .../Lru/NullHitCounterTests.cs | 2 +- BitFaster.Caching/Lru/ConcurrentLru.cs | 6 +++--- BitFaster.Caching/Lru/ConcurrentTLru.cs | 6 +++--- BitFaster.Caching/Lru/FastConcurrentLru.cs | 6 +++--- BitFaster.Caching/Lru/FastConcurrentTLru.cs | 6 +++--- .../{IHitCounter.cs => ITelemetryPolicy.cs} | 2 +- ...{NullHitCounter.cs => NoTelemetryPolicy.cs} | 2 +- .../Lru/{HitCounter.cs => TelemetryPolicy.cs} | 2 +- BitFaster.Caching/Lru/TemplateConcurrentLru.cs | 2 +- 11 files changed, 29 insertions(+), 29 deletions(-) rename BitFaster.Caching/Lru/{IHitCounter.cs => ITelemetryPolicy.cs} (89%) rename BitFaster.Caching/Lru/{NullHitCounter.cs => NoTelemetryPolicy.cs} (91%) rename BitFaster.Caching/Lru/{HitCounter.cs => TelemetryPolicy.cs} (94%) diff --git a/BitFaster.Caching.Benchmarks/Lru/TLruTimeBenchmark.cs b/BitFaster.Caching.Benchmarks/Lru/TLruTimeBenchmark.cs index f7cec266..226e06f1 100644 --- a/BitFaster.Caching.Benchmarks/Lru/TLruTimeBenchmark.cs +++ b/BitFaster.Caching.Benchmarks/Lru/TLruTimeBenchmark.cs @@ -14,17 +14,17 @@ namespace BitFaster.Caching.Benchmarks.Lru [SimpleJob(RuntimeMoniker.Net60)] public class TLruTimeBenchmark { - private static readonly TemplateConcurrentLru, TLruDateTimePolicy, NullHitCounter> dateTimeTLru - = new TemplateConcurrentLru, TLruDateTimePolicy, NullHitCounter> - (1, 3, EqualityComparer.Default, new TLruDateTimePolicy(TimeSpan.FromSeconds(1)), new NullHitCounter()); + private static readonly TemplateConcurrentLru, TLruDateTimePolicy, NoTelemetryPolicy> dateTimeTLru + = new TemplateConcurrentLru, TLruDateTimePolicy, NoTelemetryPolicy> + (1, 3, EqualityComparer.Default, new TLruDateTimePolicy(TimeSpan.FromSeconds(1)), new NoTelemetryPolicy()); - private static readonly TemplateConcurrentLru, TLruTicksPolicy, NullHitCounter> tickCountTLru - = new TemplateConcurrentLru, TLruTicksPolicy, NullHitCounter> - (1, 3, EqualityComparer.Default, new TLruTicksPolicy(TimeSpan.FromSeconds(1)), new NullHitCounter()); + private static readonly TemplateConcurrentLru, TLruTicksPolicy, NoTelemetryPolicy> tickCountTLru + = new TemplateConcurrentLru, TLruTicksPolicy, NoTelemetryPolicy> + (1, 3, EqualityComparer.Default, new TLruTicksPolicy(TimeSpan.FromSeconds(1)), new NoTelemetryPolicy()); - private static readonly TemplateConcurrentLru, TLruLongTicksPolicy, NullHitCounter> stopwatchTLru - = new TemplateConcurrentLru, TLruLongTicksPolicy, NullHitCounter> - (1, 3, EqualityComparer.Default, new TLruLongTicksPolicy(TimeSpan.FromSeconds(1)), new NullHitCounter()); + private static readonly TemplateConcurrentLru, TLruLongTicksPolicy, NoTelemetryPolicy> stopwatchTLru + = new TemplateConcurrentLru, TLruLongTicksPolicy, NoTelemetryPolicy> + (1, 3, EqualityComparer.Default, new TLruLongTicksPolicy(TimeSpan.FromSeconds(1)), new NoTelemetryPolicy()); [Benchmark(Baseline = true)] public void DateTimeUtcNow() diff --git a/BitFaster.Caching.UnitTests/Lru/HitCounterTests.cs b/BitFaster.Caching.UnitTests/Lru/HitCounterTests.cs index 811a99c4..19c8b2fd 100644 --- a/BitFaster.Caching.UnitTests/Lru/HitCounterTests.cs +++ b/BitFaster.Caching.UnitTests/Lru/HitCounterTests.cs @@ -12,7 +12,7 @@ public class HitCounterTests [Fact] public void WhenHitCountAndTotalCountAreEqualRatioIs1() { - HitCounter counter = new HitCounter(); + TelemetryPolicy counter = new TelemetryPolicy(); counter.IncrementHit(); @@ -22,7 +22,7 @@ public void WhenHitCountAndTotalCountAreEqualRatioIs1() [Fact] public void WhenHitCountIsEqualToMissCountRatioIsHalf() { - HitCounter counter = new HitCounter(); + TelemetryPolicy counter = new TelemetryPolicy(); counter.IncrementMiss(); counter.IncrementHit(); @@ -33,7 +33,7 @@ public void WhenHitCountIsEqualToMissCountRatioIsHalf() [Fact] public void WhenTotalCountIsZeroRatioReturnsZero() { - HitCounter counter = new HitCounter(); + TelemetryPolicy counter = new TelemetryPolicy(); counter.HitRatio.Should().Be(0.0); } diff --git a/BitFaster.Caching.UnitTests/Lru/NullHitCounterTests.cs b/BitFaster.Caching.UnitTests/Lru/NullHitCounterTests.cs index 259cb146..7137a9e9 100644 --- a/BitFaster.Caching.UnitTests/Lru/NullHitCounterTests.cs +++ b/BitFaster.Caching.UnitTests/Lru/NullHitCounterTests.cs @@ -9,7 +9,7 @@ namespace BitFaster.Caching.UnitTests.Lru { public class NullHitCounterTests { - private NullHitCounter counter = new NullHitCounter(); + private NoTelemetryPolicy counter = new NoTelemetryPolicy(); [Fact] public void HitRatioIsZero() diff --git a/BitFaster.Caching/Lru/ConcurrentLru.cs b/BitFaster.Caching/Lru/ConcurrentLru.cs index 5d40418c..eccf302d 100644 --- a/BitFaster.Caching/Lru/ConcurrentLru.cs +++ b/BitFaster.Caching/Lru/ConcurrentLru.cs @@ -7,7 +7,7 @@ namespace BitFaster.Caching.Lru { /// - public sealed class ConcurrentLru : TemplateConcurrentLru, LruPolicy, HitCounter> + public sealed class ConcurrentLru : TemplateConcurrentLru, LruPolicy, TelemetryPolicy> { /// /// Initializes a new instance of the ConcurrentLru class with the specified capacity that has the default @@ -15,7 +15,7 @@ public sealed class ConcurrentLru : TemplateConcurrentLru /// The maximum number of elements that the ConcurrentLru can contain. public ConcurrentLru(int capacity) - : base(Defaults.ConcurrencyLevel, capacity, EqualityComparer.Default, new LruPolicy(), new HitCounter()) + : base(Defaults.ConcurrencyLevel, capacity, EqualityComparer.Default, new LruPolicy(), new TelemetryPolicy()) { } @@ -27,7 +27,7 @@ public ConcurrentLru(int capacity) /// The maximum number of elements that the ConcurrentLru can contain. /// The IEqualityComparer implementation to use when comparing keys. public ConcurrentLru(int concurrencyLevel, int capacity, IEqualityComparer comparer) - : base(concurrencyLevel, capacity, comparer, new LruPolicy(), new HitCounter()) + : base(concurrencyLevel, capacity, comparer, new LruPolicy(), new TelemetryPolicy()) { } diff --git a/BitFaster.Caching/Lru/ConcurrentTLru.cs b/BitFaster.Caching/Lru/ConcurrentTLru.cs index 0c93d123..2c5c9030 100644 --- a/BitFaster.Caching/Lru/ConcurrentTLru.cs +++ b/BitFaster.Caching/Lru/ConcurrentTLru.cs @@ -7,7 +7,7 @@ namespace BitFaster.Caching.Lru { /// - public sealed class ConcurrentTLru : TemplateConcurrentLru, TLruLongTicksPolicy, HitCounter> + public sealed class ConcurrentTLru : TemplateConcurrentLru, TLruLongTicksPolicy, TelemetryPolicy> { /// /// Initializes a new instance of the ConcurrentTLru class with the specified capacity and time to live that has the default @@ -16,7 +16,7 @@ public sealed class ConcurrentTLru : TemplateConcurrentLruThe maximum number of elements that the ConcurrentTLru can contain. /// The time to live for cached values. public ConcurrentTLru(int capacity, TimeSpan timeToLive) - : base(Defaults.ConcurrencyLevel, capacity, EqualityComparer.Default, new TLruLongTicksPolicy(timeToLive), new HitCounter()) + : base(Defaults.ConcurrencyLevel, capacity, EqualityComparer.Default, new TLruLongTicksPolicy(timeToLive), new TelemetryPolicy()) { } @@ -29,7 +29,7 @@ public ConcurrentTLru(int capacity, TimeSpan timeToLive) /// The IEqualityComparer implementation to use when comparing keys. /// The time to live for cached values. public ConcurrentTLru(int concurrencyLevel, int capacity, IEqualityComparer comparer, TimeSpan timeToLive) - : base(concurrencyLevel, capacity, comparer, new TLruLongTicksPolicy(timeToLive), new HitCounter()) + : base(concurrencyLevel, capacity, comparer, new TLruLongTicksPolicy(timeToLive), new TelemetryPolicy()) { } diff --git a/BitFaster.Caching/Lru/FastConcurrentLru.cs b/BitFaster.Caching/Lru/FastConcurrentLru.cs index 49842be1..9ffc2cda 100644 --- a/BitFaster.Caching/Lru/FastConcurrentLru.cs +++ b/BitFaster.Caching/Lru/FastConcurrentLru.cs @@ -5,7 +5,7 @@ namespace BitFaster.Caching.Lru { /// - public sealed class FastConcurrentLru : TemplateConcurrentLru, LruPolicy, NullHitCounter> + public sealed class FastConcurrentLru : TemplateConcurrentLru, LruPolicy, NoTelemetryPolicy> { /// /// Initializes a new instance of the FastConcurrentLru class with the specified capacity that has the default @@ -13,7 +13,7 @@ public sealed class FastConcurrentLru : TemplateConcurrentLru /// The maximum number of elements that the FastConcurrentLru can contain. public FastConcurrentLru(int capacity) - : base(Defaults.ConcurrencyLevel, capacity, EqualityComparer.Default, new LruPolicy(), new NullHitCounter()) + : base(Defaults.ConcurrencyLevel, capacity, EqualityComparer.Default, new LruPolicy(), new NoTelemetryPolicy()) { } @@ -25,7 +25,7 @@ public FastConcurrentLru(int capacity) /// The maximum number of elements that the FastConcurrentLru can contain. /// The IEqualityComparer implementation to use when comparing keys. public FastConcurrentLru(int concurrencyLevel, int capacity, IEqualityComparer comparer) - : base(concurrencyLevel, capacity, comparer, new LruPolicy(), new NullHitCounter()) + : base(concurrencyLevel, capacity, comparer, new LruPolicy(), new NoTelemetryPolicy()) { } } diff --git a/BitFaster.Caching/Lru/FastConcurrentTLru.cs b/BitFaster.Caching/Lru/FastConcurrentTLru.cs index 44056c11..9b39683f 100644 --- a/BitFaster.Caching/Lru/FastConcurrentTLru.cs +++ b/BitFaster.Caching/Lru/FastConcurrentTLru.cs @@ -5,7 +5,7 @@ namespace BitFaster.Caching.Lru { /// - public sealed class FastConcurrentTLru : TemplateConcurrentLru, TLruLongTicksPolicy, NullHitCounter> + public sealed class FastConcurrentTLru : TemplateConcurrentLru, TLruLongTicksPolicy, NoTelemetryPolicy> { /// /// Initializes a new instance of the FastConcurrentTLru class with the specified capacity and time to live that has the default @@ -14,7 +14,7 @@ public sealed class FastConcurrentTLru : TemplateConcurrentLruThe maximum number of elements that the FastConcurrentTLru can contain. /// The time to live for cached values. public FastConcurrentTLru(int capacity, TimeSpan timeToLive) - : base(Defaults.ConcurrencyLevel, capacity, EqualityComparer.Default, new TLruLongTicksPolicy(timeToLive), new NullHitCounter()) + : base(Defaults.ConcurrencyLevel, capacity, EqualityComparer.Default, new TLruLongTicksPolicy(timeToLive), new NoTelemetryPolicy()) { } @@ -27,7 +27,7 @@ public FastConcurrentTLru(int capacity, TimeSpan timeToLive) /// The IEqualityComparer implementation to use when comparing keys. /// The time to live for cached values. public FastConcurrentTLru(int concurrencyLevel, int capacity, IEqualityComparer comparer, TimeSpan timeToLive) - : base(concurrencyLevel, capacity, comparer, new TLruLongTicksPolicy(timeToLive), new NullHitCounter()) + : base(concurrencyLevel, capacity, comparer, new TLruLongTicksPolicy(timeToLive), new NoTelemetryPolicy()) { } } diff --git a/BitFaster.Caching/Lru/IHitCounter.cs b/BitFaster.Caching/Lru/ITelemetryPolicy.cs similarity index 89% rename from BitFaster.Caching/Lru/IHitCounter.cs rename to BitFaster.Caching/Lru/ITelemetryPolicy.cs index ab61c42e..64246e73 100644 --- a/BitFaster.Caching/Lru/IHitCounter.cs +++ b/BitFaster.Caching/Lru/ITelemetryPolicy.cs @@ -6,7 +6,7 @@ namespace BitFaster.Caching.Lru { - public interface IHitCounter + public interface ITelemetryPolicy { void IncrementMiss(); diff --git a/BitFaster.Caching/Lru/NullHitCounter.cs b/BitFaster.Caching/Lru/NoTelemetryPolicy.cs similarity index 91% rename from BitFaster.Caching/Lru/NullHitCounter.cs rename to BitFaster.Caching/Lru/NoTelemetryPolicy.cs index 615389d3..2f6b2825 100644 --- a/BitFaster.Caching/Lru/NullHitCounter.cs +++ b/BitFaster.Caching/Lru/NoTelemetryPolicy.cs @@ -7,7 +7,7 @@ namespace BitFaster.Caching.Lru { - public struct NullHitCounter : IHitCounter + public struct NoTelemetryPolicy : ITelemetryPolicy { public double HitRatio => 0.0; diff --git a/BitFaster.Caching/Lru/HitCounter.cs b/BitFaster.Caching/Lru/TelemetryPolicy.cs similarity index 94% rename from BitFaster.Caching/Lru/HitCounter.cs rename to BitFaster.Caching/Lru/TelemetryPolicy.cs index 4617e2c2..20276533 100644 --- a/BitFaster.Caching/Lru/HitCounter.cs +++ b/BitFaster.Caching/Lru/TelemetryPolicy.cs @@ -8,7 +8,7 @@ namespace BitFaster.Caching.Lru { - public struct HitCounter : IHitCounter + public struct TelemetryPolicy : ITelemetryPolicy { private long hitCount; private long missCount; diff --git a/BitFaster.Caching/Lru/TemplateConcurrentLru.cs b/BitFaster.Caching/Lru/TemplateConcurrentLru.cs index f60c51eb..2e3428e2 100644 --- a/BitFaster.Caching/Lru/TemplateConcurrentLru.cs +++ b/BitFaster.Caching/Lru/TemplateConcurrentLru.cs @@ -30,7 +30,7 @@ namespace BitFaster.Caching.Lru public class TemplateConcurrentLru : ICache where I : LruItem where P : struct, IPolicy - where H : struct, IHitCounter + where H : struct, ITelemetryPolicy { private readonly ConcurrentDictionary dictionary; From 219f0bec9edb7442bbe113e8ab78c3669af61732 Mon Sep 17 00:00:00 2001 From: Alex Peck Date: Sat, 13 Nov 2021 18:42:37 -0800 Subject: [PATCH 09/13] default --- BitFaster.Caching.Benchmarks/Lru/TLruTimeBenchmark.cs | 6 +++--- BitFaster.Caching/Lru/ConcurrentLru.cs | 4 ++-- BitFaster.Caching/Lru/ConcurrentTLru.cs | 4 ++-- BitFaster.Caching/Lru/FastConcurrentLru.cs | 4 ++-- BitFaster.Caching/Lru/FastConcurrentTLru.cs | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/BitFaster.Caching.Benchmarks/Lru/TLruTimeBenchmark.cs b/BitFaster.Caching.Benchmarks/Lru/TLruTimeBenchmark.cs index 226e06f1..6fede73c 100644 --- a/BitFaster.Caching.Benchmarks/Lru/TLruTimeBenchmark.cs +++ b/BitFaster.Caching.Benchmarks/Lru/TLruTimeBenchmark.cs @@ -16,15 +16,15 @@ public class TLruTimeBenchmark { private static readonly TemplateConcurrentLru, TLruDateTimePolicy, NoTelemetryPolicy> dateTimeTLru = new TemplateConcurrentLru, TLruDateTimePolicy, NoTelemetryPolicy> - (1, 3, EqualityComparer.Default, new TLruDateTimePolicy(TimeSpan.FromSeconds(1)), new NoTelemetryPolicy()); + (1, 3, EqualityComparer.Default, new TLruDateTimePolicy(TimeSpan.FromSeconds(1)), default); private static readonly TemplateConcurrentLru, TLruTicksPolicy, NoTelemetryPolicy> tickCountTLru = new TemplateConcurrentLru, TLruTicksPolicy, NoTelemetryPolicy> - (1, 3, EqualityComparer.Default, new TLruTicksPolicy(TimeSpan.FromSeconds(1)), new NoTelemetryPolicy()); + (1, 3, EqualityComparer.Default, new TLruTicksPolicy(TimeSpan.FromSeconds(1)), default); private static readonly TemplateConcurrentLru, TLruLongTicksPolicy, NoTelemetryPolicy> stopwatchTLru = new TemplateConcurrentLru, TLruLongTicksPolicy, NoTelemetryPolicy> - (1, 3, EqualityComparer.Default, new TLruLongTicksPolicy(TimeSpan.FromSeconds(1)), new NoTelemetryPolicy()); + (1, 3, EqualityComparer.Default, new TLruLongTicksPolicy(TimeSpan.FromSeconds(1)), default); [Benchmark(Baseline = true)] public void DateTimeUtcNow() diff --git a/BitFaster.Caching/Lru/ConcurrentLru.cs b/BitFaster.Caching/Lru/ConcurrentLru.cs index eccf302d..93f3b409 100644 --- a/BitFaster.Caching/Lru/ConcurrentLru.cs +++ b/BitFaster.Caching/Lru/ConcurrentLru.cs @@ -15,7 +15,7 @@ public sealed class ConcurrentLru : TemplateConcurrentLru /// The maximum number of elements that the ConcurrentLru can contain. public ConcurrentLru(int capacity) - : base(Defaults.ConcurrencyLevel, capacity, EqualityComparer.Default, new LruPolicy(), new TelemetryPolicy()) + : base(Defaults.ConcurrencyLevel, capacity, EqualityComparer.Default, default, default) { } @@ -27,7 +27,7 @@ public ConcurrentLru(int capacity) /// The maximum number of elements that the ConcurrentLru can contain. /// The IEqualityComparer implementation to use when comparing keys. public ConcurrentLru(int concurrencyLevel, int capacity, IEqualityComparer comparer) - : base(concurrencyLevel, capacity, comparer, new LruPolicy(), new TelemetryPolicy()) + : base(concurrencyLevel, capacity, comparer, default, default) { } diff --git a/BitFaster.Caching/Lru/ConcurrentTLru.cs b/BitFaster.Caching/Lru/ConcurrentTLru.cs index 2c5c9030..0a0389e3 100644 --- a/BitFaster.Caching/Lru/ConcurrentTLru.cs +++ b/BitFaster.Caching/Lru/ConcurrentTLru.cs @@ -16,7 +16,7 @@ public sealed class ConcurrentTLru : TemplateConcurrentLruThe maximum number of elements that the ConcurrentTLru can contain. /// The time to live for cached values. public ConcurrentTLru(int capacity, TimeSpan timeToLive) - : base(Defaults.ConcurrencyLevel, capacity, EqualityComparer.Default, new TLruLongTicksPolicy(timeToLive), new TelemetryPolicy()) + : base(Defaults.ConcurrencyLevel, capacity, EqualityComparer.Default, new TLruLongTicksPolicy(timeToLive), default) { } @@ -29,7 +29,7 @@ public ConcurrentTLru(int capacity, TimeSpan timeToLive) /// The IEqualityComparer implementation to use when comparing keys. /// The time to live for cached values. public ConcurrentTLru(int concurrencyLevel, int capacity, IEqualityComparer comparer, TimeSpan timeToLive) - : base(concurrencyLevel, capacity, comparer, new TLruLongTicksPolicy(timeToLive), new TelemetryPolicy()) + : base(concurrencyLevel, capacity, comparer, new TLruLongTicksPolicy(timeToLive), default) { } diff --git a/BitFaster.Caching/Lru/FastConcurrentLru.cs b/BitFaster.Caching/Lru/FastConcurrentLru.cs index 9ffc2cda..8539b313 100644 --- a/BitFaster.Caching/Lru/FastConcurrentLru.cs +++ b/BitFaster.Caching/Lru/FastConcurrentLru.cs @@ -13,7 +13,7 @@ public sealed class FastConcurrentLru : TemplateConcurrentLru /// The maximum number of elements that the FastConcurrentLru can contain. public FastConcurrentLru(int capacity) - : base(Defaults.ConcurrencyLevel, capacity, EqualityComparer.Default, new LruPolicy(), new NoTelemetryPolicy()) + : base(Defaults.ConcurrencyLevel, capacity, EqualityComparer.Default, default, default) { } @@ -25,7 +25,7 @@ public FastConcurrentLru(int capacity) /// The maximum number of elements that the FastConcurrentLru can contain. /// The IEqualityComparer implementation to use when comparing keys. public FastConcurrentLru(int concurrencyLevel, int capacity, IEqualityComparer comparer) - : base(concurrencyLevel, capacity, comparer, new LruPolicy(), new NoTelemetryPolicy()) + : base(concurrencyLevel, capacity, comparer, default, default) { } } diff --git a/BitFaster.Caching/Lru/FastConcurrentTLru.cs b/BitFaster.Caching/Lru/FastConcurrentTLru.cs index 9b39683f..90bb70af 100644 --- a/BitFaster.Caching/Lru/FastConcurrentTLru.cs +++ b/BitFaster.Caching/Lru/FastConcurrentTLru.cs @@ -14,7 +14,7 @@ public sealed class FastConcurrentTLru : TemplateConcurrentLruThe maximum number of elements that the FastConcurrentTLru can contain. /// The time to live for cached values. public FastConcurrentTLru(int capacity, TimeSpan timeToLive) - : base(Defaults.ConcurrencyLevel, capacity, EqualityComparer.Default, new TLruLongTicksPolicy(timeToLive), new NoTelemetryPolicy()) + : base(Defaults.ConcurrencyLevel, capacity, EqualityComparer.Default, new TLruLongTicksPolicy(timeToLive), default) { } @@ -27,7 +27,7 @@ public FastConcurrentTLru(int capacity, TimeSpan timeToLive) /// The IEqualityComparer implementation to use when comparing keys. /// The time to live for cached values. public FastConcurrentTLru(int concurrencyLevel, int capacity, IEqualityComparer comparer, TimeSpan timeToLive) - : base(concurrencyLevel, capacity, comparer, new TLruLongTicksPolicy(timeToLive), new NoTelemetryPolicy()) + : base(concurrencyLevel, capacity, comparer, new TLruLongTicksPolicy(timeToLive), default) { } } From a8111ad024fb3b2b8d1b4b113fef35f589ad66e7 Mon Sep 17 00:00:00 2001 From: Alex Peck Date: Sun, 14 Nov 2021 12:07:06 -0800 Subject: [PATCH 10/13] item policy --- BitFaster.Caching/Disposer.cs | 2 +- BitFaster.Caching/Lru/ConcurrentLru.cs | 6 +-- BitFaster.Caching/Lru/ConcurrentTLru.cs | 6 +-- .../Lru/{IPolicy.cs => IItemPolicy.cs} | 2 +- BitFaster.Caching/Lru/LruPolicy.cs | 2 +- .../Lru/TemplateConcurrentLru.cs | 44 +++++++++---------- BitFaster.Caching/Lru/TlruDateTimePolicy.cs | 2 +- BitFaster.Caching/Lru/TlruLongTicksPolicy.cs | 2 +- BitFaster.Caching/Lru/TlruTicksPolicy.cs | 2 +- 9 files changed, 34 insertions(+), 34 deletions(-) rename BitFaster.Caching/Lru/{IPolicy.cs => IItemPolicy.cs} (84%) diff --git a/BitFaster.Caching/Disposer.cs b/BitFaster.Caching/Disposer.cs index 6cd42ce6..440cd198 100644 --- a/BitFaster.Caching/Disposer.cs +++ b/BitFaster.Caching/Disposer.cs @@ -8,7 +8,7 @@ namespace BitFaster.Caching { /// - /// A generic wrapper for object disposal. Enables JIT to inline/remove object disposal if statement reducing code size. + /// A generic wrapper for object disposal. /// /// The type of object to dispose public static class Disposer diff --git a/BitFaster.Caching/Lru/ConcurrentLru.cs b/BitFaster.Caching/Lru/ConcurrentLru.cs index 93f3b409..e0a5ccc7 100644 --- a/BitFaster.Caching/Lru/ConcurrentLru.cs +++ b/BitFaster.Caching/Lru/ConcurrentLru.cs @@ -34,15 +34,15 @@ public ConcurrentLru(int concurrencyLevel, int capacity, IEqualityComparer co /// /// Gets the ratio of hits to misses, where a value of 1 indicates 100% hits. /// - public double HitRatio => this.hitCounter.HitRatio; + public double HitRatio => this.telemetryPolicy.HitRatio; /// /// Occurs when an item is removed from the cache. /// public event EventHandler> ItemRemoved { - add { this.hitCounter.ItemRemoved += value; } - remove { this.hitCounter.ItemRemoved -= value; } + add { this.telemetryPolicy.ItemRemoved += value; } + remove { this.telemetryPolicy.ItemRemoved -= value; } } } } diff --git a/BitFaster.Caching/Lru/ConcurrentTLru.cs b/BitFaster.Caching/Lru/ConcurrentTLru.cs index 0a0389e3..1bca36bc 100644 --- a/BitFaster.Caching/Lru/ConcurrentTLru.cs +++ b/BitFaster.Caching/Lru/ConcurrentTLru.cs @@ -36,15 +36,15 @@ public ConcurrentTLru(int concurrencyLevel, int capacity, IEqualityComparer c /// /// Gets the ratio of hits to misses, where a value of 1 indicates 100% hits. /// - public double HitRatio => this.hitCounter.HitRatio; + public double HitRatio => this.telemetryPolicy.HitRatio; /// /// Occurs when an item is removed from the cache. /// public event EventHandler> ItemRemoved { - add { this.hitCounter.ItemRemoved += value; } - remove { this.hitCounter.ItemRemoved -= value; } + add { this.telemetryPolicy.ItemRemoved += value; } + remove { this.telemetryPolicy.ItemRemoved -= value; } } } } diff --git a/BitFaster.Caching/Lru/IPolicy.cs b/BitFaster.Caching/Lru/IItemPolicy.cs similarity index 84% rename from BitFaster.Caching/Lru/IPolicy.cs rename to BitFaster.Caching/Lru/IItemPolicy.cs index 8989bd72..65593239 100644 --- a/BitFaster.Caching/Lru/IPolicy.cs +++ b/BitFaster.Caching/Lru/IItemPolicy.cs @@ -6,7 +6,7 @@ namespace BitFaster.Caching.Lru { - public interface IPolicy where I : LruItem + public interface IItemPolicy where I : LruItem { I CreateItem(K key, V value); diff --git a/BitFaster.Caching/Lru/LruPolicy.cs b/BitFaster.Caching/Lru/LruPolicy.cs index 3e68020e..1fde7381 100644 --- a/BitFaster.Caching/Lru/LruPolicy.cs +++ b/BitFaster.Caching/Lru/LruPolicy.cs @@ -10,7 +10,7 @@ namespace BitFaster.Caching.Lru /// /// Discards the least recently used items first. /// - public readonly struct LruPolicy : IPolicy> + public readonly struct LruPolicy : IItemPolicy> { [MethodImpl(MethodImplOptions.AggressiveInlining)] public LruItem CreateItem(K key, V value) diff --git a/BitFaster.Caching/Lru/TemplateConcurrentLru.cs b/BitFaster.Caching/Lru/TemplateConcurrentLru.cs index 2e3428e2..e5043d4b 100644 --- a/BitFaster.Caching/Lru/TemplateConcurrentLru.cs +++ b/BitFaster.Caching/Lru/TemplateConcurrentLru.cs @@ -27,10 +27,10 @@ namespace BitFaster.Caching.Lru /// 5. When warm is full, warm tail is moved to warm head or cold depending on WasAccessed. /// 6. When cold is full, cold tail is moved to warm head or removed from dictionary on depending on WasAccessed. /// - public class TemplateConcurrentLru : ICache + public class TemplateConcurrentLru : ICache where I : LruItem - where P : struct, IPolicy - where H : struct, ITelemetryPolicy + where P : struct, IItemPolicy + where T : struct, ITelemetryPolicy { private readonly ConcurrentDictionary dictionary; @@ -47,18 +47,18 @@ public class TemplateConcurrentLru : ICache private readonly int warmCapacity; private readonly int coldCapacity; - private readonly P policy; + private readonly P itemPolicy; // Since H is a struct, making it readonly will force the runtime to make defensive copies // if mutate methods are called. Therefore, field must be mutable to maintain count. - protected H hitCounter; + protected T telemetryPolicy; public TemplateConcurrentLru( int concurrencyLevel, int capacity, IEqualityComparer comparer, P itemPolicy, - H hitCounter) + T telemetryPolicy) { if (capacity < 3) { @@ -82,9 +82,9 @@ public TemplateConcurrentLru( int dictionaryCapacity = this.hotCapacity + this.warmCapacity + this.coldCapacity + 1; this.dictionary = new ConcurrentDictionary(concurrencyLevel, dictionaryCapacity, comparer); - this.policy = itemPolicy; - this.hitCounter = hitCounter; - this.hitCounter.SetEventSource(this); + this.itemPolicy = itemPolicy; + this.telemetryPolicy = telemetryPolicy; + this.telemetryPolicy.SetEventSource(this); } // No lock count: https://arbel.net/2013/02/03/best-practices-for-using-concurrentdictionary/ @@ -105,7 +105,7 @@ public bool TryGet(K key, out V value) } value = default; - this.hitCounter.IncrementMiss(); + this.telemetryPolicy.IncrementMiss(); return false; } @@ -114,17 +114,17 @@ public bool TryGet(K key, out V value) [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool GetOrDiscard(I item, out V value) { - if (this.policy.ShouldDiscard(item)) + if (this.itemPolicy.ShouldDiscard(item)) { this.Move(item, ItemDestination.Remove); - this.hitCounter.IncrementMiss(); + this.telemetryPolicy.IncrementMiss(); value = default; return false; } value = item.Value; - this.policy.Touch(item); - this.hitCounter.IncrementHit(); + this.itemPolicy.Touch(item); + this.telemetryPolicy.IncrementHit(); return true; } @@ -140,7 +140,7 @@ public V GetOrAdd(K key, Func valueFactory) // The value factory may be called concurrently for the same key, but the first write to the dictionary wins. // This is identical logic in ConcurrentDictionary.GetOrAdd method. - var newItem = this.policy.CreateItem(key, valueFactory(key)); + var newItem = this.itemPolicy.CreateItem(key, valueFactory(key)); if (this.dictionary.TryAdd(key, newItem)) { @@ -166,7 +166,7 @@ public async Task GetOrAddAsync(K key, Func> valueFactory) // The value factory may be called concurrently for the same key, but the first write to the dictionary wins. // This is identical logic in ConcurrentDictionary.GetOrAdd method. - var newItem = this.policy.CreateItem(key, await valueFactory(key).ConfigureAwait(false)); + var newItem = this.itemPolicy.CreateItem(key, await valueFactory(key).ConfigureAwait(false)); if (this.dictionary.TryAdd(key, newItem)) { @@ -199,7 +199,7 @@ public bool TryRemove(K key) existing.WasAccessed = false; existing.WasRemoved = true; - this.hitCounter.OnItemRemoved(existing.Key, existing.Value, ItemRemovedReason.Removed); + this.telemetryPolicy.OnItemRemoved(existing.Key, existing.Value, ItemRemovedReason.Removed); // serialize dispose (common case dispose not thread safe) lock (existing) @@ -255,7 +255,7 @@ public void AddOrUpdate(K key, V value) } // then try add - var newItem = this.policy.CreateItem(key, value); + var newItem = this.itemPolicy.CreateItem(key, value); if (this.dictionary.TryAdd(key, newItem)) { @@ -325,7 +325,7 @@ private void CycleHotUnchecked() if (this.hotQueue.TryDequeue(out var item)) { - var where = this.policy.RouteHot(item); + var where = this.itemPolicy.RouteHot(item); this.Move(item, where); } else @@ -349,7 +349,7 @@ private void CycleWarmUnchecked() if (this.warmQueue.TryDequeue(out var item)) { - var where = this.policy.RouteWarm(item); + var where = this.itemPolicy.RouteWarm(item); // When the warm queue is full, we allow an overflow of 1 item before redirecting warm items to cold. // This only happens when hit rate is high, in which case we can consider all items relatively equal in @@ -384,7 +384,7 @@ private void CycleColdUnchecked() if (this.coldQueue.TryDequeue(out var item)) { - var where = this.policy.RouteCold(item); + var where = this.itemPolicy.RouteCold(item); if (where == ItemDestination.Warm && this.warmCount <= this.warmCapacity) { @@ -426,7 +426,7 @@ private void Move(I item, ItemDestination where) { item.WasRemoved = true; - this.hitCounter.OnItemRemoved(item.Key, item.Value, ItemRemovedReason.Evicted); + this.telemetryPolicy.OnItemRemoved(item.Key, item.Value, ItemRemovedReason.Evicted); lock (item) { diff --git a/BitFaster.Caching/Lru/TlruDateTimePolicy.cs b/BitFaster.Caching/Lru/TlruDateTimePolicy.cs index ecd7bd9c..88578818 100644 --- a/BitFaster.Caching/Lru/TlruDateTimePolicy.cs +++ b/BitFaster.Caching/Lru/TlruDateTimePolicy.cs @@ -11,7 +11,7 @@ namespace BitFaster.Caching.Lru /// Time aware Least Recently Used (TLRU) is a variant of LRU which discards the least /// recently used items first, and any item that has expired. /// - public readonly struct TLruDateTimePolicy : IPolicy> + public readonly struct TLruDateTimePolicy : IItemPolicy> { private readonly TimeSpan timeToLive; diff --git a/BitFaster.Caching/Lru/TlruLongTicksPolicy.cs b/BitFaster.Caching/Lru/TlruLongTicksPolicy.cs index 0cdbb34d..38620529 100644 --- a/BitFaster.Caching/Lru/TlruLongTicksPolicy.cs +++ b/BitFaster.Caching/Lru/TlruLongTicksPolicy.cs @@ -15,7 +15,7 @@ namespace BitFaster.Caching.Lru /// /// This class measures time using stopwatch. /// - public readonly struct TLruLongTicksPolicy : IPolicy> + public readonly struct TLruLongTicksPolicy : IItemPolicy> { private readonly long timeToLive; diff --git a/BitFaster.Caching/Lru/TlruTicksPolicy.cs b/BitFaster.Caching/Lru/TlruTicksPolicy.cs index c308d91d..f8805523 100644 --- a/BitFaster.Caching/Lru/TlruTicksPolicy.cs +++ b/BitFaster.Caching/Lru/TlruTicksPolicy.cs @@ -16,7 +16,7 @@ namespace BitFaster.Caching.Lru /// than DateTime.Now. However, if the process runs for longer than 24.8 days, the integer /// value will wrap and time measurement will become invalid. /// - public readonly struct TLruTicksPolicy : IPolicy> + public readonly struct TLruTicksPolicy : IItemPolicy> { private readonly int timeToLive; From 672456ee938c2abe37c89fddd229b8330db1dd38 Mon Sep 17 00:00:00 2001 From: Alex Peck Date: Sun, 14 Nov 2021 12:23:30 -0800 Subject: [PATCH 11/13] docs --- BitFaster.Caching/Lru/ItemRemovedEventArgs.cs | 20 +++++++++++++++++++ BitFaster.Caching/Lru/TelemetryPolicy.cs | 3 +-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/BitFaster.Caching/Lru/ItemRemovedEventArgs.cs b/BitFaster.Caching/Lru/ItemRemovedEventArgs.cs index 3e4acf2c..f67d6159 100644 --- a/BitFaster.Caching/Lru/ItemRemovedEventArgs.cs +++ b/BitFaster.Caching/Lru/ItemRemovedEventArgs.cs @@ -6,8 +6,19 @@ namespace BitFaster.Caching.Lru { + /// + /// Provides data for the ItemRemoved event. + /// + /// + /// public class ItemRemovedEventArgs : EventArgs { + /// + /// Initializes a new instance of the ItemRemovedEventArgs class using the specified key, value and reason. + /// + /// The key of the item that was removed from the cache. + /// The value of the item that was removed from the cache. + /// The reason the item was removed from the cache. public ItemRemovedEventArgs(K key, V value, ItemRemovedReason reason) { this.Key = key; @@ -15,10 +26,19 @@ public ItemRemovedEventArgs(K key, V value, ItemRemovedReason reason) this.Reason = reason; } + /// + /// Gets the key of the item that was removed from the cache. + /// public K Key { get; } + /// + /// Gets the value of the item that was removed from the cache. + /// public V Value { get; } + /// + /// Gets the reason the item was removed from the cache. + /// public ItemRemovedReason Reason { get; } } } diff --git a/BitFaster.Caching/Lru/TelemetryPolicy.cs b/BitFaster.Caching/Lru/TelemetryPolicy.cs index 20276533..17c3c06d 100644 --- a/BitFaster.Caching/Lru/TelemetryPolicy.cs +++ b/BitFaster.Caching/Lru/TelemetryPolicy.cs @@ -12,6 +12,7 @@ public struct TelemetryPolicy : ITelemetryPolicy { private long hitCount; private long missCount; + private object eventSource; public double HitRatio => Total == 0 ? 0 : (double)hitCount / (double)Total; @@ -19,8 +20,6 @@ public struct TelemetryPolicy : ITelemetryPolicy public EventHandler> ItemRemoved; - private object eventSource; - public void IncrementMiss() { Interlocked.Increment(ref this.missCount); From b46234ff055d07bb8cb554fcb924a2c934377a3f Mon Sep 17 00:00:00 2001 From: Alex Peck Date: Sun, 14 Nov 2021 12:43:06 -0800 Subject: [PATCH 12/13] tel pol tests --- .../Lru/HitCounterTests.cs | 41 ------------ ...nterTests.cs => NoTelemetryPolicyTests.cs} | 2 +- .../Lru/TelemetryPolicyTests.cs | 66 +++++++++++++++++++ 3 files changed, 67 insertions(+), 42 deletions(-) delete mode 100644 BitFaster.Caching.UnitTests/Lru/HitCounterTests.cs rename BitFaster.Caching.UnitTests/Lru/{NullHitCounterTests.cs => NoTelemetryPolicyTests.cs} (94%) create mode 100644 BitFaster.Caching.UnitTests/Lru/TelemetryPolicyTests.cs diff --git a/BitFaster.Caching.UnitTests/Lru/HitCounterTests.cs b/BitFaster.Caching.UnitTests/Lru/HitCounterTests.cs deleted file mode 100644 index 19c8b2fd..00000000 --- a/BitFaster.Caching.UnitTests/Lru/HitCounterTests.cs +++ /dev/null @@ -1,41 +0,0 @@ -using FluentAssertions; -using BitFaster.Caching.Lru; -using System; -using System.Collections.Generic; -using System.Text; -using Xunit; - -namespace BitFaster.Caching.UnitTests.Lru -{ - public class HitCounterTests - { - [Fact] - public void WhenHitCountAndTotalCountAreEqualRatioIs1() - { - TelemetryPolicy counter = new TelemetryPolicy(); - - counter.IncrementHit(); - - counter.HitRatio.Should().Be(1.0); - } - - [Fact] - public void WhenHitCountIsEqualToMissCountRatioIsHalf() - { - TelemetryPolicy counter = new TelemetryPolicy(); - - counter.IncrementMiss(); - counter.IncrementHit(); - - counter.HitRatio.Should().Be(0.5); - } - - [Fact] - public void WhenTotalCountIsZeroRatioReturnsZero() - { - TelemetryPolicy counter = new TelemetryPolicy(); - - counter.HitRatio.Should().Be(0.0); - } - } -} diff --git a/BitFaster.Caching.UnitTests/Lru/NullHitCounterTests.cs b/BitFaster.Caching.UnitTests/Lru/NoTelemetryPolicyTests.cs similarity index 94% rename from BitFaster.Caching.UnitTests/Lru/NullHitCounterTests.cs rename to BitFaster.Caching.UnitTests/Lru/NoTelemetryPolicyTests.cs index 7137a9e9..27814eab 100644 --- a/BitFaster.Caching.UnitTests/Lru/NullHitCounterTests.cs +++ b/BitFaster.Caching.UnitTests/Lru/NoTelemetryPolicyTests.cs @@ -7,7 +7,7 @@ namespace BitFaster.Caching.UnitTests.Lru { - public class NullHitCounterTests + public class NoTelemetryPolicyTests { private NoTelemetryPolicy counter = new NoTelemetryPolicy(); diff --git a/BitFaster.Caching.UnitTests/Lru/TelemetryPolicyTests.cs b/BitFaster.Caching.UnitTests/Lru/TelemetryPolicyTests.cs new file mode 100644 index 00000000..007231dc --- /dev/null +++ b/BitFaster.Caching.UnitTests/Lru/TelemetryPolicyTests.cs @@ -0,0 +1,66 @@ +using FluentAssertions; +using BitFaster.Caching.Lru; +using System; +using System.Collections.Generic; +using System.Text; +using Xunit; + +namespace BitFaster.Caching.UnitTests.Lru +{ + public class TelemetryPolicyTests + { + private TelemetryPolicy telemetryPolicy = default; + + [Fact] + public void WhenHitCountAndTotalCountAreEqualRatioIs1() + { + telemetryPolicy.IncrementHit(); + + telemetryPolicy.HitRatio.Should().Be(1.0); + } + + [Fact] + public void WhenHitCountIsEqualToMissCountRatioIsHalf() + { + telemetryPolicy.IncrementMiss(); + telemetryPolicy.IncrementHit(); + + telemetryPolicy.HitRatio.Should().Be(0.5); + } + + [Fact] + public void WhenTotalCountIsZeroRatioReturnsZero() + { + telemetryPolicy.HitRatio.Should().Be(0.0); + } + + [Fact] + public void WhenOnItemRemovedInvokedEventIsFired() + { + List> eventList = new(); + + telemetryPolicy.ItemRemoved += (source, args) => eventList.Add(args); + + telemetryPolicy.OnItemRemoved(1, 2, ItemRemovedReason.Evicted); + + eventList.Should().HaveCount(1); + eventList[0].Key.Should().Be(1); + eventList[0].Value.Should().Be(2); + eventList[0].Reason.Should().Be(ItemRemovedReason.Evicted); + } + + [Fact] + public void WhenEventSourceIsSetItemRemovedEventUsesSource() + { + List eventSourceList = new(); + + telemetryPolicy.ItemRemoved += (source, args) => eventSourceList.Add(source); + + telemetryPolicy.SetEventSource(this); + telemetryPolicy.OnItemRemoved(1, 2, ItemRemovedReason.Evicted); + + eventSourceList.Should().HaveCount(1); + eventSourceList[0].Should().Be(this); + } + } +} From 357c15dce1a52378926d1dd278059fd7449cf395 Mon Sep 17 00:00:00 2001 From: Alex Peck Date: Sun, 14 Nov 2021 12:49:13 -0800 Subject: [PATCH 13/13] docs --- BitFaster.Caching/Lru/ItemRemovedEventArgs.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BitFaster.Caching/Lru/ItemRemovedEventArgs.cs b/BitFaster.Caching/Lru/ItemRemovedEventArgs.cs index f67d6159..b1103ec1 100644 --- a/BitFaster.Caching/Lru/ItemRemovedEventArgs.cs +++ b/BitFaster.Caching/Lru/ItemRemovedEventArgs.cs @@ -9,8 +9,8 @@ namespace BitFaster.Caching.Lru /// /// Provides data for the ItemRemoved event. /// - /// - /// + /// The type of the removed item key. + /// The type of the removed item value. public class ItemRemovedEventArgs : EventArgs { ///