Skip to content

Commit 743ea48

Browse files
authored
Update README.md
1 parent 6c0e7b9 commit 743ea48

File tree

1 file changed

+0
-213
lines changed

1 file changed

+0
-213
lines changed

README.md

Lines changed: 0 additions & 213 deletions
Original file line numberDiff line numberDiff line change
@@ -211,216 +211,3 @@ In this test, we generate 2000 samples of 500 keys with a Zipfian distribution (
211211
This test was run on a Standard D16s v3 Azure VM (16 cpus), with .NET Core 3.1.
212212

213213
![image](https://user-images.githubusercontent.com/12851828/86203563-2f941880-bb1a-11ea-8d6a-70ece91b4362.png)
214-
215-
## Meta-programming using structs and JIT value type optimization
216-
217-
TemplateConcurrentLru features injectable behaviors defined as structs. Structs are subject to special JIT optimizations, and the .NET JIT compiler can inline, eliminate dead code and propogate JIT time constants based on structs. Using this technique, the TemplateConcurrentLru can be customized to support LRU and TLRU policies without compromising execution speed.
218-
219-
### Example: TemplateConcurrentLru.TryGet
220-
221-
This is the source code for the TryGet method. It calls into two value type generic type arguments: policy (1) and hitcounter (2).
222-
223-
```csharp
224-
public bool TryGet(K key, out V value)
225-
{
226-
I item;
227-
if (dictionary.TryGetValue(key, out item))
228-
{
229-
return GetOrDiscard(item, out value);
230-
}
231-
232-
value = default(V);
233-
this.hitCounter.IncrementMiss();
234-
return false;
235-
}
236-
237-
// AggressiveInlining forces the JIT to inline policy.ShouldDiscard(). For LRU policy
238-
// the first branch is completely eliminated due to JIT time constant propogation.
239-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
240-
private bool GetOrDiscard(I item, out V value)
241-
{
242-
if (this.policy.ShouldDiscard(item)) // 1
243-
{
244-
this.Move(item, ItemDestination.Remove);
245-
this.hitCounter.IncrementMiss(); // 2
246-
value = default(V);
247-
return false;
248-
}
249-
250-
value = item.Value;
251-
this.policy.Touch(item); // 1
252-
this.hitCounter.IncrementHit(); // 2
253-
return true;
254-
}
255-
```
256-
257-
### FastConcurrentLru (LruPolicy & NullHitCounter)
258-
259-
The LruPolicy used by FastConcurrentLru/ConcurrentLru is hardcoded to never discard items.
260-
261-
```csharp
262-
public readonly struct LruPolicy<K, V> : IPolicy<K, V, LruItem<K, V>>
263-
{
264-
...
265-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
266-
public bool ShouldDiscard(LruItem<K, V> item)
267-
{
268-
return false;
269-
}
270-
...
271-
}
272-
```
273-
274-
The branch and enclosed code for ShouldDiscard are completely eliminated by JIT (1). Since the below code is from FastConcurrentLru, the hit counting calls (2) are also eliminated. The assembly code for the FastConcurrentLru.TryGet method with LruPolicy and NullHitCounter is 76 bytes:
275-
276-
```assembly
277-
; BitFaster.Caching.Lru.TemplateConcurrentLru`5[[System.Int32, System.Private.CoreLib],[System.Int32, System.Private.CoreLib],[System.__Canon, System.Private.CoreLib],[BitFaster.Caching.Lru.LruPolicy`2[[System.Int32, System.Private.CoreLib],[System.Int32, System.Private.CoreLib]], BitFaster.Caching],[BitFaster.Caching.Lru.NullHitCounter, BitFaster.Caching]].TryGet(Int32, Int32 ByRef)
278-
push rsi
279-
sub rsp,30
280-
xor eax,eax
281-
mov [rsp+28],rax
282-
mov rsi,r8
283-
mov rcx,[rcx+8]
284-
lea r8,[rsp+28]
285-
cmp [rcx],ecx
286-
call qword ptr [7FFED15190A0]
287-
test eax,eax
288-
je short M01_L00
289-
mov rax,[rsp+28]
290-
mov eax,[rax+0C]
291-
mov [rsi],eax
292-
mov rax,[rsp+28]
293-
mov byte ptr [rax+10],1
294-
mov eax,1
295-
add rsp,30
296-
pop rsi
297-
ret
298-
M01_L00:
299-
xor eax,eax
300-
mov [rsi],eax
301-
add rsp,30
302-
pop rsi
303-
ret
304-
; Total bytes of code 76
305-
```
306-
307-
### FastConcurrentTLru (TLruLongTicksPolicy & NullHitCounter)
308-
309-
The struct TLruLongTicksPolicy used in FastConcurrentTLru can expire items.
310-
311-
```csharp
312-
public readonly struct TLruLongTicksPolicy<K, V> : IPolicy<K, V, LongTickCountLruItem<K, V>>
313-
{
314-
...
315-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
316-
public bool ShouldDiscard(LongTickCountLruItem<K, V> item)
317-
{
318-
if (Stopwatch.GetTimestamp() - item.TickCount > this.timeToLive)
319-
{
320-
return true;
321-
}
322-
323-
return false;
324-
}
325-
...
326-
}
327-
```
328-
329-
As a result, the JITted code now includes branch 2 and the assembly code grows considerably to 312 bytes.
330-
331-
```assembly
332-
; BitFaster.Caching.Lru.TemplateConcurrentLru`5[[System.Int32, System.Private.CoreLib],[System.Int32, System.Private.CoreLib],[System.__Canon, System.Private.CoreLib],[BitFaster.Caching.Lru.TLruLongTicksPolicy`2[[System.Int32, System.Private.CoreLib],[System.Int32, System.Private.CoreLib]], BitFaster.Caching],[BitFaster.Caching.Lru.NullHitCounter, BitFaster.Caching]].TryGet(Int32, Int32 ByRef)
333-
push rbp
334-
push r15
335-
push r14
336-
push r13
337-
push r12
338-
push rdi
339-
push rsi
340-
push rbx
341-
sub rsp,88
342-
lea rbp,[rsp+0C0]
343-
xor ebx,ebx
344-
mov [rbp+0FFC0],rbx
345-
mov [rbp+0FFB8],rbx
346-
mov [rbp+20],r8
347-
mov rsi,rcx
348-
mov ebx,edx
349-
lea rcx,[rbp+0FF70]
350-
mov rdx,r10
351-
call CORINFO_HELP_INIT_PINVOKE_FRAME
352-
mov r14,rax
353-
mov rcx,rsp
354-
mov [rbp+0FF90],rcx
355-
mov rcx,rbp
356-
mov [rbp+0FFA0],rcx
357-
mov rcx,[rsi+8]
358-
lea r8,[rbp+0FFC0]
359-
mov edx,ebx
360-
cmp [rcx],ecx
361-
call qword ptr [7FFED15290A0]
362-
test eax,eax
363-
je near ptr M01_L03
364-
mov [rbp+10],rsi
365-
mov rbx,[rsi+40]
366-
mov r15,[rbp+0FFC0]
367-
mov [rbp+0FFB0],r15
368-
lea rcx,[rbp+0FFB8]
369-
xor r11d,r11d
370-
mov rax,offset MD_Interop+Kernel32.QueryPerformanceCounter(Int64*)
371-
mov [rbp+0FF80],rax
372-
lea rax,[M01_L00]
373-
mov [rbp+0FF98],rax
374-
lea rax,[rbp+0FF70]
375-
mov [r14+10],rax
376-
mov byte ptr [r14+0C],0
377-
call qword ptr [7FFED151D7D0]
378-
M01_L00:
379-
mov byte ptr [r14+0C],1
380-
cmp dword ptr [7FFED1524BD8],0
381-
je short M01_L01
382-
call qword ptr [7FFED1528278]
383-
M01_L01:
384-
mov rcx,[rbp+0FF78]
385-
mov [r14+10],rcx
386-
mov rcx,[rbp+0FFB8]
387-
mov r15,[rbp+0FFB0]
388-
sub rcx,[r15+18]
389-
cmp rcx,rbx
390-
jle short M01_L02
391-
mov rcx,[rbp+10]
392-
mov rdx,[rbp+0FFC0]
393-
mov r8d,2
394-
call BitFaster.Caching.Lru.TemplateConcurrentLru`5[[System.Int32, System.Private.CoreLib],[System.Int32, System.Private.CoreLib],[System.__Canon, System.Private.CoreLib],[BitFaster.Caching.Lru.TLruLongTicksPolicy`2[[System.Int32, System.Private.CoreLib],[System.Int32, System.Private.CoreLib]], BitFaster.Caching],[BitFaster.Caching.Lru.NullHitCounter, BitFaster.Caching]].Move(System.__Canon, BitFaster.Caching.Lru.ItemDestination)
395-
xor eax,eax
396-
mov rdi,[rbp+20]
397-
mov [rdi],eax
398-
jmp short M01_L04
399-
M01_L02:
400-
mov rax,[rbp+0FFC0]
401-
mov eax,[rax+0C]
402-
mov rdi,[rbp+20]
403-
mov [rdi],eax
404-
mov rax,[rbp+0FFC0]
405-
mov byte ptr [rax+10],1
406-
mov eax,1
407-
jmp short M01_L04
408-
M01_L03:
409-
xor eax,eax
410-
mov rdi,[rbp+20]
411-
mov [rdi],eax
412-
M01_L04:
413-
movzx eax,al
414-
mov byte ptr [r14+0C],1
415-
lea rsp,[rbp+0FFC8]
416-
pop rbx
417-
pop rsi
418-
pop rdi
419-
pop r12
420-
pop r13
421-
pop r14
422-
pop r15
423-
pop rbp
424-
ret
425-
; Total bytes of code 312
426-
```

0 commit comments

Comments
 (0)