diff --git a/BitFaster.Caching.Benchmarks/DisposerBench.cs b/BitFaster.Caching.Benchmarks/DisposerBench.cs index 1ef98c13..4daf4518 100644 --- a/BitFaster.Caching.Benchmarks/DisposerBench.cs +++ b/BitFaster.Caching.Benchmarks/DisposerBench.cs @@ -1,35 +1,35 @@ -using System; -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Runtime.CompilerServices; -using System.Text; -using BenchmarkDotNet.Attributes; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.Diagnostics.Runtime.Interop; - -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)] - [MemoryDiagnoser] - public class DisposerBench +using System.Text; +using BenchmarkDotNet.Attributes; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.Diagnostics.Runtime.Interop; + +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)] + [MemoryDiagnoser] + public class DisposerBench { - [Benchmark(Baseline = true)] - public void HandWritten() - { - for (int i = 0; i < 1000; i++) - { + [Benchmark(Baseline = true)] + public void HandWritten() + { + for (int i = 0; i < 1000; i++) + { NotDisposable notDisposable = new NotDisposable(); - Disposable disposable = new Disposable(); + Disposable disposable = new Disposable(); disposable.Dispose(); - } + } } - [Benchmark()] - public void NotOptimized() + [Benchmark()] + public void NotOptimized() { - for (int i = 0; i < 1000; i++) - { + for (int i = 0; i < 1000; i++) + { NotDisposable notDisposable = new NotDisposable(); Disposable disposable = new Disposable(); @@ -45,69 +45,69 @@ public void NotOptimized() } } - [Benchmark()] - public void GenericDisposerReadonlyProperty() + [Benchmark()] + public void GenericDisposerReadonlyProperty() { - for (int i = 0; i < 1000; i++) - { + for (int i = 0; i < 1000; i++) + { NotDisposable notDisposable = new NotDisposable(); - Disposable disposable = new Disposable(); + Disposable disposable = new Disposable(); Disposer.Dispose(disposable); Disposer.Dispose(notDisposable); } - } - - [Benchmark()] - public void GenericDisposerStdCheck() + } + + [Benchmark()] + public void GenericDisposerStdCheck() { - for (int i = 0; i < 1000; i++) - { + for (int i = 0; i < 1000; i++) + { NotDisposable notDisposable = new NotDisposable(); - Disposable disposable = new Disposable(); + Disposable disposable = new Disposable(); Disposer2.Dispose(disposable); Disposer2.Dispose(notDisposable); } - } - } - - public static class Disposer - { - // try using a static readonly field - private static readonly bool shouldDispose = typeof(IDisposable).IsAssignableFrom(typeof(T)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Dispose(T value) - { - if (shouldDispose) - { - ((IDisposable)value).Dispose(); - } - } - } - + } + } + + public static class Disposer + { + // try using a static readonly field + private static readonly bool shouldDispose = typeof(IDisposable).IsAssignableFrom(typeof(T)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Dispose(T value) + { + if (shouldDispose) + { + ((IDisposable)value).Dispose(); + } + } + } + public static class Disposer2 - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Dispose(T value) - { + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Dispose(T value) + { if (value is IDisposable d) { d.Dispose(); - } - } - } - - public class NotDisposable + } + } + } + + public class NotDisposable { } - public class Disposable : IDisposable - { - private bool isDisposed = false; - - public void Dispose() - { - if (!isDisposed) - this.isDisposed = true; - } - } -} + public class Disposable : IDisposable + { + private bool isDisposed = false; + + public void Dispose() + { + if (!isDisposed) + this.isDisposed = true; + } + } +} diff --git a/BitFaster.Caching.Benchmarks/Lru/LruCycleBench.cs b/BitFaster.Caching.Benchmarks/Lru/LruCycleBench.cs index bacb3189..1afb3e01 100644 --- a/BitFaster.Caching.Benchmarks/Lru/LruCycleBench.cs +++ b/BitFaster.Caching.Benchmarks/Lru/LruCycleBench.cs @@ -15,13 +15,14 @@ namespace BitFaster.Caching.Benchmarks.Lru // DefaultJob : .NET 6.0.0 (6.0.21.52210), X64 RyuJIT - //| Method | Mean | Error | StdDev | Code Size | Gen 0 | Allocated | - //|------------------- |---------:|---------:|---------:|----------:|-------:|----------:| - //| FastConcurrentLru | 22.61 us | 0.125 us | 0.110 us | 0 KB | 2.1362 | 9 KB | - //| ConcurrentLru | 24.39 us | 0.389 us | 0.364 us | 0 KB | 2.1362 | 9 KB | - //| FastConcurrentTLru | 31.28 us | 0.067 us | 0.062 us | 1 KB | 2.3193 | 10 KB | - //| ConcurrentTLru | 31.75 us | 0.074 us | 0.062 us | 1 KB | 2.3193 | 10 KB | - [DisassemblyDiagnoser(printSource: true)] + //| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Code Size | Allocated | + //|------------------- |---------:|---------:|---------:|------:|--------:|-------:|----------:|----------:| + //| FastConcurrentLru | 23.25 us | 0.128 us | 0.114 us | 1.00 | 0.00 | 2.1362 | 5 KB | 9 KB | + //| ConcurrentLru | 23.78 us | 0.116 us | 0.097 us | 1.02 | 0.01 | 2.1362 | 5 KB | 9 KB | + //| FastConcurrentTLru | 32.17 us | 0.463 us | 0.433 us | 1.38 | 0.02 | 2.3193 | 6 KB | 10 KB | + //| ConcurrentTLru | 32.52 us | 0.386 us | 0.361 us | 1.40 | 0.02 | 2.3193 | 6 KB | 10 KB | + //| ClassicLru | 16.29 us | 0.195 us | 0.163 us | 0.70 | 0.01 | 3.2959 | 5 KB | 14 KB | + [DisassemblyDiagnoser(printSource: true, maxDepth: 5)] [MemoryDiagnoser] public class LruCycleBench { @@ -31,7 +32,7 @@ public class LruCycleBench private static readonly FastConcurrentLru fastConcurrentLru = new(8, 9, EqualityComparer.Default); private static readonly FastConcurrentTLru fastConcurrentTLru = new(8, 9, EqualityComparer.Default, TimeSpan.FromMinutes(1)); - [Benchmark()] + [Benchmark(Baseline = true)] public void FastConcurrentLru() { Func func = x => x; @@ -66,5 +67,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 e844bf82..88c97aed 100644 --- a/BitFaster.Caching.Benchmarks/Lru/LruJustGetOrAdd.cs +++ b/BitFaster.Caching.Benchmarks/Lru/LruJustGetOrAdd.cs @@ -21,15 +21,15 @@ namespace BitFaster.Caching.Benchmarks //| Method | Mean | Error | StdDev | Ratio | RatioSD | Code Size | Gen 0 | Allocated | //|------------------------- |-----------:|----------:|----------:|------:|--------:|----------:|-------:|----------:| - //| ConcurrentDictionary | 7.761 ns | 0.0393 ns | 0.0307 ns | 1.00 | 0.00 | 340 B | - | - | - //| FastConcurrentLru | 9.559 ns | 0.0625 ns | 0.0585 ns | 1.23 | 0.01 | 427 B | - | - | - //| ConcurrentLru | 13.580 ns | 0.0531 ns | 0.0443 ns | 1.75 | 0.01 | 449 B | - | - | - //| FastConcurrentTLru | 27.109 ns | 0.1041 ns | 0.0813 ns | 3.49 | 0.02 | 613 B | - | - | - //| ConcurrentTLru | 29.622 ns | 0.2369 ns | 0.2216 ns | 3.81 | 0.03 | 684 B | - | - | - //| ClassicLru | 48.060 ns | 0.2447 ns | 0.2169 ns | 6.19 | 0.03 | 738 B | - | - | - //| RuntimeMemoryCacheGet | 106.117 ns | 0.4677 ns | 0.4375 ns | 13.69 | 0.08 | 49 B | 0.0074 | 32 B | - //| ExtensionsMemoryCacheGet | 92.386 ns | 0.4539 ns | 0.4023 ns | 11.91 | 0.07 | 78 B | 0.0055 | 24 B | - [DisassemblyDiagnoser(printSource: true)] + //| ConcurrentDictionary | 7.868 ns | 0.0543 ns | 0.0481 ns | 1.00 | 0.00 | 1,523 B | - | - | + //| FastConcurrentLru | 10.340 ns | 0.0496 ns | 0.0464 ns | 1.31 | 0.01 | 2,185 B | - | - | + //| ConcurrentLru | 13.739 ns | 0.0979 ns | 0.0916 ns | 1.75 | 0.01 | 2,207 B | - | - | + //| FastConcurrentTLru | 25.820 ns | 0.0933 ns | 0.0729 ns | 3.28 | 0.02 | 2,371 B | - | - | + //| ConcurrentTLru | 29.732 ns | 0.1387 ns | 0.1229 ns | 3.78 | 0.03 | 2,442 B | - | - | + //| 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 | + [DisassemblyDiagnoser(printSource: true, maxDepth: 5)] [MemoryDiagnoser] public class LruJustGetOrAdd { diff --git a/BitFaster.Caching.Benchmarks/Lru/LruJustTryGet.cs b/BitFaster.Caching.Benchmarks/Lru/LruJustTryGet.cs index 61884bb3..13a243a3 100644 --- a/BitFaster.Caching.Benchmarks/Lru/LruJustTryGet.cs +++ b/BitFaster.Caching.Benchmarks/Lru/LruJustTryGet.cs @@ -16,10 +16,10 @@ namespace BitFaster.Caching.Benchmarks.Lru //| Method | Mean | Error | StdDev | Ratio | RatioSD | Code Size | Allocated | //|--------------------- |----------:|----------:|----------:|------:|--------:|----------:|----------:| - //| ConcurrentDictionary | 4.421 ns | 0.0295 ns | 0.0276 ns | 1.00 | 0.00 | 364 B | - | - //| FastConcurrentLru | 7.645 ns | 0.0339 ns | 0.0300 ns | 1.73 | 0.02 | 339 B | - | - //| FastConcurrentTLru | 26.139 ns | 0.0741 ns | 0.0619 ns | 5.92 | 0.04 | 437 B | - | - [DisassemblyDiagnoser(printSource: true)] + //| 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 | - | + [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 cbd8d66a..0406621b 100644 --- a/BitFaster.Caching.Benchmarks/Lru/LruMultiGet.cs +++ b/BitFaster.Caching.Benchmarks/Lru/LruMultiGet.cs @@ -12,7 +12,23 @@ namespace BitFaster.Caching.Benchmarks.Lru { - [DisassemblyDiagnoser(printSource: true)] + //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 | 8.389 ns | 0.0233 ns | 0.0206 ns | 1.00 | 0.00 | 1,544 B | - | - | + //| FastConcurrentLru | 10.637 ns | 0.0808 ns | 0.0755 ns | 1.27 | 0.01 | 3,149 B | - | - | + //| ConcurrentLru | 13.977 ns | 0.0674 ns | 0.0526 ns | 1.67 | 0.01 | 3,171 B | - | - | + //| FastConcurrentTLru | 27.107 ns | 0.0810 ns | 0.0632 ns | 3.23 | 0.01 | 3,468 B | - | - | + //| 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 | + [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 f4ff4a10..8aa300d9 100644 --- a/BitFaster.Caching.Benchmarks/Lru/LruZipDistribution.cs +++ b/BitFaster.Caching.Benchmarks/Lru/LruZipDistribution.cs @@ -17,12 +17,12 @@ namespace BitFaster.Caching.Benchmarks.Lru //| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Code Size | Allocated | //|------------------- |---------:|--------:|--------:|------:|--------:|-------:|----------:|----------:| - //| ClassicLru | 108.4 ns | 0.26 ns | 0.20 ns | 1.00 | 0.00 | 0.0154 | 799 B | 67 B | - //| FastConcurrentLru | 123.1 ns | 0.97 ns | 0.86 ns | 1.14 | 0.01 | 0.0093 | 488 B | 41 B | - //| ConcurrentLru | 128.7 ns | 2.12 ns | 1.98 ns | 1.19 | 0.02 | 0.0093 | 510 B | 40 B | - //| FastConcurrentTLru | 166.1 ns | 0.99 ns | 0.83 ns | 1.53 | 0.01 | 0.0100 | 674 B | 43 B | - //| ConcurrentTLru | 172.2 ns | 0.52 ns | 0.46 ns | 1.59 | 0.00 | 0.0103 | 745 B | 45 B | - [DisassemblyDiagnoser(printSource: true)] + //| ClassicLru | 111.3 ns | 1.33 ns | 1.11 ns | 1.00 | 0.00 | 0.0148 | 4,108 B | 64 B | + //| FastConcurrentLru | 121.6 ns | 1.45 ns | 1.21 ns | 1.09 | 0.01 | 0.0090 | 5,085 B | 39 B | + //| 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 | + [DisassemblyDiagnoser(printSource: true, maxDepth: 5)] [MemoryDiagnoser] public class LruZipDistribution {