Skip to content

Commit 9f1abbd

Browse files
authored
count once (#24)
1 parent 498359d commit 9f1abbd

File tree

8 files changed

+41
-44
lines changed

8 files changed

+41
-44
lines changed

BitFaster.Caching.Benchmarks/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class Program
1515
static void Main(string[] args)
1616
{
1717
var summary = BenchmarkRunner
18-
.Run<LruCycle2>(ManualConfig.Create(DefaultConfig.Instance)
18+
.Run<MissHitHitRemove>(ManualConfig.Create(DefaultConfig.Instance)
1919
.AddJob(Job.RyuJitX64));
2020
}
2121
}

BitFaster.Caching.UnitTests/Lru/HitCounterTests.cs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,19 @@ public class HitCounterTests
1313
public void WhenHitCountAndTotalCountAreEqualRatioIs1()
1414
{
1515
HitCounter counter = new HitCounter();
16-
counter.IncrementTotalCount();
17-
counter.IncrementHitCount();
16+
17+
counter.IncrementHit();
1818

1919
counter.HitRatio.Should().Be(1.0);
2020
}
2121

2222
[Fact]
23-
public void WhenHitCountIsHalfTotalCountRatioIsHalf()
23+
public void WhenHitCountIsEqualToMissCountRatioIsHalf()
2424
{
2525
HitCounter counter = new HitCounter();
2626

27-
counter.IncrementTotalCount();
28-
counter.IncrementTotalCount();
29-
counter.IncrementHitCount();
27+
counter.IncrementMiss();
28+
counter.IncrementHit();
3029

3130
counter.HitRatio.Should().Be(0.5);
3231
}
@@ -36,8 +35,6 @@ public void WhenTotalCountIsZeroRatioReturnsZero()
3635
{
3736
HitCounter counter = new HitCounter();
3837

39-
counter.IncrementHitCount();
40-
4138
counter.HitRatio.Should().Be(0.0);
4239
}
4340
}

BitFaster.Caching.UnitTests/Lru/NullHitCounterTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ public void HitRatioIsZero()
2020
[Fact]
2121
public void IncrementHitCountIsNoOp()
2222
{
23-
counter.Invoking(c => c.IncrementHitCount()).Should().NotThrow();
23+
counter.Invoking(c => c.IncrementHit()).Should().NotThrow();
2424
}
2525

2626
[Fact]
2727
public void IncrementTotalCountIsNoOp()
2828
{
29-
counter.Invoking(c => c.IncrementTotalCount()).Should().NotThrow();
29+
counter.Invoking(c => c.IncrementMiss()).Should().NotThrow();
3030
}
3131
}
3232
}

BitFaster.Caching/Lru/HitCounter.cs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,21 @@ namespace BitFaster.Caching.Lru
1010
{
1111
public struct HitCounter : IHitCounter
1212
{
13-
private long requestHitCount;
14-
private long requestTotalCount;
13+
private long hitCount;
14+
private long missCount;
1515

16-
public double HitRatio => requestTotalCount == 0 ? 0 : (double)requestHitCount / (double)requestTotalCount;
16+
public double HitRatio => Total == 0 ? 0 : (double)hitCount / (double)Total;
1717

18-
public void IncrementTotalCount()
18+
public long Total => this.hitCount + this.missCount;
19+
20+
public void IncrementMiss()
1921
{
20-
Interlocked.Increment(ref this.requestTotalCount);
22+
Interlocked.Increment(ref this.missCount);
2123
}
2224

23-
public void IncrementHitCount()
25+
public void IncrementHit()
2426
{
25-
Interlocked.Increment(ref this.requestHitCount);
27+
Interlocked.Increment(ref this.hitCount);
2628
}
2729
}
2830
}

BitFaster.Caching/Lru/IHitCounter.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ namespace BitFaster.Caching.Lru
88
{
99
public interface IHitCounter
1010
{
11-
void IncrementTotalCount();
11+
void IncrementMiss();
1212

13-
void IncrementHitCount();
13+
void IncrementHit();
1414

1515
double HitRatio { get; }
1616
}

BitFaster.Caching/Lru/NullHitCounter.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@ public struct NullHitCounter : IHitCounter
1212
public double HitRatio => 0.0;
1313

1414
[MethodImpl(MethodImplOptions.AggressiveInlining)]
15-
public void IncrementTotalCount()
15+
public void IncrementMiss()
1616
{
1717
}
1818

1919
[MethodImpl(MethodImplOptions.AggressiveInlining)]
20-
public void IncrementHitCount()
20+
public void IncrementHit()
2121
{
2222
}
2323
}

BitFaster.Caching/Lru/TemplateConcurrentLru.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,6 @@ public TemplateConcurrentLru(
9494

9595
public bool TryGet(K key, out V value)
9696
{
97-
this.hitCounter.IncrementTotalCount();
98-
9997
I item;
10098
if (dictionary.TryGetValue(key, out item))
10199
{
@@ -108,11 +106,12 @@ public bool TryGet(K key, out V value)
108106

109107
value = item.Value;
110108
this.policy.Touch(item);
111-
this.hitCounter.IncrementHitCount();
109+
this.hitCounter.IncrementHit();
112110
return true;
113111
}
114112

115113
value = default(V);
114+
this.hitCounter.IncrementMiss();
116115
return false;
117116
}
118117

@@ -124,7 +123,7 @@ public V GetOrAdd(K key, Func<K, V> valueFactory)
124123
}
125124

126125
// The value factory may be called concurrently for the same key, but the first write to the dictionary wins.
127-
// This is identical logic to the ConcurrentDictionary.GetOrAdd method.
126+
// This is identical logic in ConcurrentDictionary.GetOrAdd method.
128127
var newItem = this.policy.CreateItem(key, valueFactory(key));
129128

130129
if (this.dictionary.TryAdd(key, newItem))
@@ -146,7 +145,7 @@ public async Task<V> GetOrAddAsync(K key, Func<K, Task<V>> valueFactory)
146145
}
147146

148147
// The value factory may be called concurrently for the same key, but the first write to the dictionary wins.
149-
// This is identical logic to the ConcurrentDictionary.GetOrAdd method.
148+
// This is identical logic in ConcurrentDictionary.GetOrAdd method.
150149
var newItem = this.policy.CreateItem(key, await valueFactory(key).ConfigureAwait(false));
151150

152151
if (this.dictionary.TryAdd(key, newItem))

README.md

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -43,32 +43,31 @@ Cache contains 6 items which are fetched repeatedly, no items are evicted. Repre
4343

4444
FastConcurrentLru does not allocate and is approximately 10x faster than MemoryCache.
4545

46-
| Method | Mean | Error | StdDev | Ratio | Gen 0 | Allocated |
47-
|----------------------------- |----------:|---------:|---------:|------:|-------:|----------:|
48-
| ConcurrentDictionaryGetOrAdd | 18.72 ns | 0.289 ns | 0.641 ns | 1.00 | - | - |
49-
| FastConcurrentLruGetOrAdd | 25.64 ns | 0.434 ns | 0.427 ns | 1.35 | - | - |
50-
| ConcurrentLruGetOrAdd | 35.53 ns | 0.259 ns | 0.216 ns | 1.86 | - | - |
51-
| FastConcurrentTLruGetOrAdd | 132.75 ns | 1.493 ns | 1.397 ns | 6.96 | - | - |
52-
| ConcurrentTLruGetOrAdd | 144.87 ns | 2.179 ns | 1.819 ns | 7.59 | - | - |
53-
| ClassicLruGetOrAdd | 75.67 ns | 1.513 ns | 1.554 ns | 3.99 | - | - |
54-
| MemoryCacheGetStringKey | 309.14 ns | 2.155 ns | 1.910 ns | 16.17 | 0.0153 | 32 B |
46+
| Method | Mean | Error | StdDev | Ratio | Gen 0 | Allocated |
47+
|--------------------- |----------:|---------:|---------:|------:|-------:|----------:|
48+
| ConcurrentDictionary | 15.83 ns | 0.242 ns | 0.215 ns | 1.00 | - | - |
49+
| FastConcurrentLru | 20.42 ns | 0.319 ns | 0.283 ns | 1.29 | - | - |
50+
| ConcurrentLru | 24.59 ns | 0.484 ns | 0.594 ns | 1.56 | - | - |
51+
| FastConcurrentTLru | 110.76 ns | 0.664 ns | 0.518 ns | 6.98 | - | - |
52+
| ConcurrentTLru | 114.99 ns | 1.652 ns | 1.465 ns | 7.27 | - | - |
53+
| ClassicLru | 69.01 ns | 0.503 ns | 0.446 ns | 4.36 | - | - |
54+
| MemoryCache | 257.83 ns | 4.786 ns | 4.700 ns | 16.30 | 0.0153 | 32 B |
5555

5656
### Mixed workload
5757

5858
Tests 4 operations, 1 miss (adding the item), 2 hits then remove.
5959

6060
This test needs to be improved to provoke queue cycling.
6161

62-
6362
| Method | Mean | Error | StdDev | Ratio | Gen 0 | Allocated |
6463
|--------------------- |-----------:|---------:|---------:|------:|-------:|----------:|
65-
| ConcurrentDictionary | 178.1 ns | 1.47 ns | 1.23 ns | 1.00 | 0.0381 | 80 B |
66-
| FastConcurrentLru | 420.4 ns | 7.52 ns | 6.67 ns | 2.36 | 0.0534 | 112 B |
67-
| ConcurrentLru | 423.7 ns | 3.17 ns | 2.64 ns | 2.38 | 0.0534 | 112 B |
68-
| FastConcurrentTlru | 941.6 ns | 6.69 ns | 5.93 ns | 5.29 | 0.0572 | 120 B |
69-
| ConcurrentTlru | 960.3 ns | 17.73 ns | 14.80 ns | 5.39 | 0.0572 | 120 B |
70-
| ClassicLru | 363.5 ns | 3.65 ns | 3.23 ns | 2.04 | 0.0763 | 160 B |
71-
| MemoryCache | 2,380.9 ns | 33.22 ns | 27.74 ns | 13.37 | 2.3460 | 4912 B |
64+
| ConcurrentDictionary | 151.7 ns | 2.34 ns | 1.96 ns | 1.00 | 0.0381 | 80 B |
65+
| FastConcurrentLru | 369.2 ns | 7.29 ns | 7.16 ns | 2.44 | 0.0534 | 112 B |
66+
| ConcurrentLru | 373.6 ns | 2.97 ns | 2.64 ns | 2.46 | 0.0534 | 112 B |
67+
| FastConcurrentTlru | 838.6 ns | 11.49 ns | 13.68 ns | 5.53 | 0.0572 | 120 B |
68+
| ConcurrentTlru | 852.7 ns | 16.12 ns | 13.46 ns | 5.62 | 0.0572 | 120 B |
69+
| ClassicLru | 347.3 ns | 2.67 ns | 2.08 ns | 2.29 | 0.0763 | 160 B |
70+
| MemoryCache | 1,987.5 ns | 38.29 ns | 57.31 ns | 13.15 | 2.3460 | 4912 B |
7271

7372

7473
### LruCycle2

0 commit comments

Comments
 (0)