Skip to content

Commit f3f7050

Browse files
authored
Inline policy/move logic (#54)
* inline * fix readme
1 parent 8571643 commit f3f7050

File tree

2 files changed

+58
-38
lines changed

2 files changed

+58
-38
lines changed

BitFaster.Caching/Lru/TemplateConcurrentLru.cs

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Concurrent;
33
using System.Collections.Generic;
44
using System.Linq;
5+
using System.Runtime.CompilerServices;
56
using System.Text;
67
using System.Threading;
78
using System.Threading.Tasks;
@@ -99,24 +100,33 @@ public bool TryGet(K key, out V value)
99100
I item;
100101
if (dictionary.TryGetValue(key, out item))
101102
{
102-
if (this.policy.ShouldDiscard(item))
103-
{
104-
this.Move(item, ItemDestination.Remove);
105-
value = default(V);
106-
return false;
107-
}
108-
109-
value = item.Value;
110-
this.policy.Touch(item);
111-
this.hitCounter.IncrementHit();
112-
return true;
103+
return GetOrDiscard(item, out value);
113104
}
114105

115106
value = default(V);
116107
this.hitCounter.IncrementMiss();
117108
return false;
118109
}
119110

111+
// AggressiveInlining forces the JIT to inline policy.ShouldDiscard(). For LRU policy
112+
// the first branch is completely eliminated due to JIT time constant propogation.
113+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
114+
private bool GetOrDiscard(I item, out V value)
115+
{
116+
if (this.policy.ShouldDiscard(item))
117+
{
118+
this.Move(item, ItemDestination.Remove);
119+
this.hitCounter.IncrementMiss();
120+
value = default(V);
121+
return false;
122+
}
123+
124+
value = item.Value;
125+
this.policy.Touch(item);
126+
this.hitCounter.IncrementHit();
127+
return true;
128+
}
129+
120130
///<inheritdoc/>
121131
public V GetOrAdd(K key, Func<K, V> valueFactory)
122132
{
@@ -303,6 +313,7 @@ private void CycleCold()
303313
}
304314
}
305315

316+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
306317
private void Move(I item, ItemDestination where)
307318
{
308319
item.WasAccessed = false;

README.md

Lines changed: 36 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -172,11 +172,11 @@ Cache size = *N* / 10 (so we can cache 10% of the total set). ConcurrentLru has
172172

173173
| Method | Mean | Error | StdDev | Ratio | RatioSD |
174174
|------------------- |---------:|--------:|--------:|------:|--------:|
175-
| ClassicLru | 157.3 ns | 1.67 ns | 1.48 ns | 1.00 | 0.00 |
176-
| FastConcurrentLru | 165.4 ns | 1.17 ns | 1.04 ns | 1.05 | 0.01 |
177-
| ConcurrentLru | 176.1 ns | 1.22 ns | 1.08 ns | 1.12 | 0.01 |
178-
| FastConcurrentTLru | 247.9 ns | 3.58 ns | 2.80 ns | 1.58 | 0.02 |
179-
| ConcurrentTLru | 259.0 ns | 3.61 ns | 3.20 ns | 1.65 | 0.03 |
175+
| ClassicLru | 175.7 ns | 2.75 ns | 2.43 ns | 1.00 | 0.00 |
176+
| FastConcurrentLru | 180.2 ns | 2.55 ns | 2.26 ns | 1.03 | 0.02 |
177+
| ConcurrentLru | 189.1 ns | 3.14 ns | 2.94 ns | 1.08 | 0.03 |
178+
| FastConcurrentTLru | 261.4 ns | 4.53 ns | 4.01 ns | 1.49 | 0.04 |
179+
| ConcurrentTLru | 266.1 ns | 3.96 ns | 3.51 ns | 1.51 | 0.03 |
180180

181181
### Raw Lookup speed
182182

@@ -188,16 +188,16 @@ In this test the same items are fetched repeatedly, no items are evicted. Repres
188188

189189
FastConcurrentLru does not allocate and is approximately 10x faster than System.Runtime.Caching.MemoryCache or the newer Microsoft.Extensions.Caching.Memory.MemoryCache.
190190

191-
| Method | Mean | Error | StdDev | Ratio | Gen 0 | Allocated |
192-
|---------------------- |----------:|---------:|---------:|------:|-------:|----------:|
193-
| ConcurrentDictionary | 16.88 ns | 0.276 ns | 0.245 ns | 1.00 | - | - |
194-
| FastConcurrentLru | 23.27 ns | 0.491 ns | 0.565 ns | 1.38 | - | - |
195-
| ConcurrentLru | 26.77 ns | 0.512 ns | 0.666 ns | 1.60 | - | - |
196-
| FastConcurrentTLru | 54.35 ns | 0.650 ns | 0.576 ns | 3.22 | - | - |
197-
| ConcurrentTLru | 60.10 ns | 1.024 ns | 1.501 ns | 3.53 | - | - |
198-
| ClassicLru | 68.04 ns | 1.400 ns | 2.221 ns | 4.12 | - | - |
199-
| RuntimeMemoryCache | 280.16 ns | 5.607 ns | 7.486 ns | 16.59 | 0.0153 | 32 B |
200-
| ExtensionsMemoryCache | 342.72 ns | 3.729 ns | 3.114 ns | 20.29 | 0.0114 | 24 B |
191+
| Method | Mean | Error | StdDev | Ratio | Gen 0 | Allocated |
192+
|------------------------- |----------:|---------:|---------:|------:|-------:|----------:|
193+
| ConcurrentDictionary | 16.76 ns | 0.322 ns | 0.285 ns | 1.00 | - | - |
194+
| FastConcurrentLru | 18.94 ns | 0.249 ns | 0.220 ns | 1.13 | - | - |
195+
| ConcurrentLru | 21.46 ns | 0.204 ns | 0.191 ns | 1.28 | - | - |
196+
| FastConcurrentTLru | 41.57 ns | 0.450 ns | 0.376 ns | 2.48 | - | - |
197+
| ConcurrentTLru | 43.95 ns | 0.588 ns | 0.521 ns | 2.62 | - | - |
198+
| ClassicLru | 67.62 ns | 0.901 ns | 0.799 ns | 4.03 | - | - |
199+
| RuntimeMemoryCacheGet | 279.70 ns | 3.825 ns | 3.578 ns | 16.70 | 0.0153 | 32 B |
200+
| ExtensionsMemoryCacheGet | 341.67 ns | 6.617 ns | 6.499 ns | 20.35 | 0.0114 | 24 B |
201201

202202

203203
## ConcurrentLru Throughput
@@ -222,23 +222,32 @@ public bool TryGet(K key, out V value)
222222
I item;
223223
if (dictionary.TryGetValue(key, out item))
224224
{
225-
if (this.policy.ShouldDiscard(item)) // 1
226-
{
227-
this.Move(item, ItemDestination.Remove);
228-
value = default(V);
229-
return false;
230-
}
231-
232-
value = item.Value;
233-
this.policy.Touch(item);
234-
this.hitCounter.IncrementHit(); // 2
235-
return true;
225+
return GetOrDiscard(item, out value);
236226
}
237227

238228
value = default(V);
239-
this.hitCounter.IncrementMiss(); // 2
229+
this.hitCounter.IncrementMiss();
240230
return false;
241231
}
232+
233+
// AggressiveInlining forces the JIT to inline policy.ShouldDiscard(). For LRU policy
234+
// the first branch is completely eliminated due to JIT time constant propogation.
235+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
236+
private bool GetOrDiscard(I item, out V value)
237+
{
238+
if (this.policy.ShouldDiscard(item)) // 1
239+
{
240+
this.Move(item, ItemDestination.Remove);
241+
this.hitCounter.IncrementMiss(); // 2
242+
value = default(V);
243+
return false;
244+
}
245+
246+
value = item.Value;
247+
this.policy.Touch(item); // 1
248+
this.hitCounter.IncrementHit(); // 2
249+
return true;
250+
}
242251
```
243252

244253
### FastConcurrentLru (LruPolicy & NullHitCounter)

0 commit comments

Comments
 (0)