diff --git a/BitFaster.Caching.Benchmarks/Program.cs b/BitFaster.Caching.Benchmarks/Program.cs index 99e04292..aa66d4ed 100644 --- a/BitFaster.Caching.Benchmarks/Program.cs +++ b/BitFaster.Caching.Benchmarks/Program.cs @@ -15,7 +15,7 @@ class Program static void Main(string[] args) { var summary = BenchmarkRunner - .Run(ManualConfig.Create(DefaultConfig.Instance) + .Run(ManualConfig.Create(DefaultConfig.Instance) .AddJob(Job.RyuJitX64)); } } diff --git a/BitFaster.Caching.UnitTests/Lru/HitCounterTests.cs b/BitFaster.Caching.UnitTests/Lru/HitCounterTests.cs index a8205107..d973703e 100644 --- a/BitFaster.Caching.UnitTests/Lru/HitCounterTests.cs +++ b/BitFaster.Caching.UnitTests/Lru/HitCounterTests.cs @@ -13,20 +13,19 @@ public class HitCounterTests public void WhenHitCountAndTotalCountAreEqualRatioIs1() { HitCounter counter = new HitCounter(); - counter.IncrementTotalCount(); - counter.IncrementHitCount(); + + counter.IncrementHit(); counter.HitRatio.Should().Be(1.0); } [Fact] - public void WhenHitCountIsHalfTotalCountRatioIsHalf() + public void WhenHitCountIsEqualToMissCountRatioIsHalf() { HitCounter counter = new HitCounter(); - counter.IncrementTotalCount(); - counter.IncrementTotalCount(); - counter.IncrementHitCount(); + counter.IncrementMiss(); + counter.IncrementHit(); counter.HitRatio.Should().Be(0.5); } @@ -36,8 +35,6 @@ public void WhenTotalCountIsZeroRatioReturnsZero() { HitCounter counter = new HitCounter(); - counter.IncrementHitCount(); - counter.HitRatio.Should().Be(0.0); } } diff --git a/BitFaster.Caching.UnitTests/Lru/NullHitCounterTests.cs b/BitFaster.Caching.UnitTests/Lru/NullHitCounterTests.cs index 51e9835a..14372a82 100644 --- a/BitFaster.Caching.UnitTests/Lru/NullHitCounterTests.cs +++ b/BitFaster.Caching.UnitTests/Lru/NullHitCounterTests.cs @@ -20,13 +20,13 @@ public void HitRatioIsZero() [Fact] public void IncrementHitCountIsNoOp() { - counter.Invoking(c => c.IncrementHitCount()).Should().NotThrow(); + counter.Invoking(c => c.IncrementHit()).Should().NotThrow(); } [Fact] public void IncrementTotalCountIsNoOp() { - counter.Invoking(c => c.IncrementTotalCount()).Should().NotThrow(); + counter.Invoking(c => c.IncrementMiss()).Should().NotThrow(); } } } diff --git a/BitFaster.Caching/Lru/HitCounter.cs b/BitFaster.Caching/Lru/HitCounter.cs index 662bae6a..96553fdb 100644 --- a/BitFaster.Caching/Lru/HitCounter.cs +++ b/BitFaster.Caching/Lru/HitCounter.cs @@ -10,19 +10,21 @@ namespace BitFaster.Caching.Lru { public struct HitCounter : IHitCounter { - private long requestHitCount; - private long requestTotalCount; + private long hitCount; + private long missCount; - public double HitRatio => requestTotalCount == 0 ? 0 : (double)requestHitCount / (double)requestTotalCount; + public double HitRatio => Total == 0 ? 0 : (double)hitCount / (double)Total; - public void IncrementTotalCount() + public long Total => this.hitCount + this.missCount; + + public void IncrementMiss() { - Interlocked.Increment(ref this.requestTotalCount); + Interlocked.Increment(ref this.missCount); } - public void IncrementHitCount() + public void IncrementHit() { - Interlocked.Increment(ref this.requestHitCount); + Interlocked.Increment(ref this.hitCount); } } } diff --git a/BitFaster.Caching/Lru/IHitCounter.cs b/BitFaster.Caching/Lru/IHitCounter.cs index dab9fd0d..5dff4450 100644 --- a/BitFaster.Caching/Lru/IHitCounter.cs +++ b/BitFaster.Caching/Lru/IHitCounter.cs @@ -8,9 +8,9 @@ namespace BitFaster.Caching.Lru { public interface IHitCounter { - void IncrementTotalCount(); + void IncrementMiss(); - void IncrementHitCount(); + void IncrementHit(); double HitRatio { get; } } diff --git a/BitFaster.Caching/Lru/NullHitCounter.cs b/BitFaster.Caching/Lru/NullHitCounter.cs index 212853b0..431729e0 100644 --- a/BitFaster.Caching/Lru/NullHitCounter.cs +++ b/BitFaster.Caching/Lru/NullHitCounter.cs @@ -12,12 +12,12 @@ public struct NullHitCounter : IHitCounter public double HitRatio => 0.0; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void IncrementTotalCount() + public void IncrementMiss() { } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void IncrementHitCount() + public void IncrementHit() { } } diff --git a/BitFaster.Caching/Lru/TemplateConcurrentLru.cs b/BitFaster.Caching/Lru/TemplateConcurrentLru.cs index dc631b63..1aadbebb 100644 --- a/BitFaster.Caching/Lru/TemplateConcurrentLru.cs +++ b/BitFaster.Caching/Lru/TemplateConcurrentLru.cs @@ -94,8 +94,6 @@ public TemplateConcurrentLru( public bool TryGet(K key, out V value) { - this.hitCounter.IncrementTotalCount(); - I item; if (dictionary.TryGetValue(key, out item)) { @@ -108,11 +106,12 @@ public bool TryGet(K key, out V value) value = item.Value; this.policy.Touch(item); - this.hitCounter.IncrementHitCount(); + this.hitCounter.IncrementHit(); return true; } value = default(V); + this.hitCounter.IncrementMiss(); return false; } @@ -124,7 +123,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 to the ConcurrentDictionary.GetOrAdd method. + // This is identical logic in ConcurrentDictionary.GetOrAdd method. var newItem = this.policy.CreateItem(key, valueFactory(key)); if (this.dictionary.TryAdd(key, newItem)) @@ -146,7 +145,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 to the ConcurrentDictionary.GetOrAdd method. + // This is identical logic in ConcurrentDictionary.GetOrAdd method. var newItem = this.policy.CreateItem(key, await valueFactory(key).ConfigureAwait(false)); if (this.dictionary.TryAdd(key, newItem)) diff --git a/README.md b/README.md index 8f79fc63..5dddb134 100644 --- a/README.md +++ b/README.md @@ -43,15 +43,15 @@ Cache contains 6 items which are fetched repeatedly, no items are evicted. Repre FastConcurrentLru does not allocate and is approximately 10x faster than MemoryCache. -| Method | Mean | Error | StdDev | Ratio | Gen 0 | Allocated | -|----------------------------- |----------:|---------:|---------:|------:|-------:|----------:| -| ConcurrentDictionaryGetOrAdd | 18.72 ns | 0.289 ns | 0.641 ns | 1.00 | - | - | -| FastConcurrentLruGetOrAdd | 25.64 ns | 0.434 ns | 0.427 ns | 1.35 | - | - | -| ConcurrentLruGetOrAdd | 35.53 ns | 0.259 ns | 0.216 ns | 1.86 | - | - | -| FastConcurrentTLruGetOrAdd | 132.75 ns | 1.493 ns | 1.397 ns | 6.96 | - | - | -| ConcurrentTLruGetOrAdd | 144.87 ns | 2.179 ns | 1.819 ns | 7.59 | - | - | -| ClassicLruGetOrAdd | 75.67 ns | 1.513 ns | 1.554 ns | 3.99 | - | - | -| MemoryCacheGetStringKey | 309.14 ns | 2.155 ns | 1.910 ns | 16.17 | 0.0153 | 32 B | +| Method | Mean | Error | StdDev | Ratio | Gen 0 | Allocated | +|--------------------- |----------:|---------:|---------:|------:|-------:|----------:| +| ConcurrentDictionary | 15.83 ns | 0.242 ns | 0.215 ns | 1.00 | - | - | +| FastConcurrentLru | 20.42 ns | 0.319 ns | 0.283 ns | 1.29 | - | - | +| ConcurrentLru | 24.59 ns | 0.484 ns | 0.594 ns | 1.56 | - | - | +| FastConcurrentTLru | 110.76 ns | 0.664 ns | 0.518 ns | 6.98 | - | - | +| ConcurrentTLru | 114.99 ns | 1.652 ns | 1.465 ns | 7.27 | - | - | +| ClassicLru | 69.01 ns | 0.503 ns | 0.446 ns | 4.36 | - | - | +| MemoryCache | 257.83 ns | 4.786 ns | 4.700 ns | 16.30 | 0.0153 | 32 B | ### Mixed workload @@ -59,16 +59,15 @@ Tests 4 operations, 1 miss (adding the item), 2 hits then remove. This test needs to be improved to provoke queue cycling. - | Method | Mean | Error | StdDev | Ratio | Gen 0 | Allocated | |--------------------- |-----------:|---------:|---------:|------:|-------:|----------:| -| ConcurrentDictionary | 178.1 ns | 1.47 ns | 1.23 ns | 1.00 | 0.0381 | 80 B | -| FastConcurrentLru | 420.4 ns | 7.52 ns | 6.67 ns | 2.36 | 0.0534 | 112 B | -| ConcurrentLru | 423.7 ns | 3.17 ns | 2.64 ns | 2.38 | 0.0534 | 112 B | -| FastConcurrentTlru | 941.6 ns | 6.69 ns | 5.93 ns | 5.29 | 0.0572 | 120 B | -| ConcurrentTlru | 960.3 ns | 17.73 ns | 14.80 ns | 5.39 | 0.0572 | 120 B | -| ClassicLru | 363.5 ns | 3.65 ns | 3.23 ns | 2.04 | 0.0763 | 160 B | -| MemoryCache | 2,380.9 ns | 33.22 ns | 27.74 ns | 13.37 | 2.3460 | 4912 B | +| ConcurrentDictionary | 151.7 ns | 2.34 ns | 1.96 ns | 1.00 | 0.0381 | 80 B | +| FastConcurrentLru | 369.2 ns | 7.29 ns | 7.16 ns | 2.44 | 0.0534 | 112 B | +| ConcurrentLru | 373.6 ns | 2.97 ns | 2.64 ns | 2.46 | 0.0534 | 112 B | +| FastConcurrentTlru | 838.6 ns | 11.49 ns | 13.68 ns | 5.53 | 0.0572 | 120 B | +| ConcurrentTlru | 852.7 ns | 16.12 ns | 13.46 ns | 5.62 | 0.0572 | 120 B | +| ClassicLru | 347.3 ns | 2.67 ns | 2.08 ns | 2.29 | 0.0763 | 160 B | +| MemoryCache | 1,987.5 ns | 38.29 ns | 57.31 ns | 13.15 | 2.3460 | 4912 B | ### LruCycle2