From 91e6a1809e937edc4499426dfd493f6a992f20b4 Mon Sep 17 00:00:00 2001 From: Alex Peck Date: Wed, 28 Sep 2022 19:31:10 -0700 Subject: [PATCH] memcache hr --- BitFaster.Caching.HitRateAnalysis/Analysis.cs | 5 + .../BitFaster.Caching.HitRateAnalysis.csproj | 1 + .../MemoryCacheAdaptor.cs | 139 ++++++++++++++++++ .../Zipfian/Runner.cs | 87 +++++++++++ 4 files changed, 232 insertions(+) create mode 100644 BitFaster.Caching.HitRateAnalysis/MemoryCacheAdaptor.cs diff --git a/BitFaster.Caching.HitRateAnalysis/Analysis.cs b/BitFaster.Caching.HitRateAnalysis/Analysis.cs index 86a0f42e..6c03c981 100644 --- a/BitFaster.Caching.HitRateAnalysis/Analysis.cs +++ b/BitFaster.Caching.HitRateAnalysis/Analysis.cs @@ -14,18 +14,22 @@ public class Analysis private readonly ConcurrentLru concurrentLru; private readonly ClassicLru classicLru; private readonly ConcurrentLfu concurrentLfu; + private readonly MemoryCacheAdaptor memoryCache; public Analysis(int cacheSize) { concurrentLru = new ConcurrentLru(1, cacheSize, EqualityComparer.Default); classicLru = new ClassicLru(1, cacheSize, EqualityComparer.Default); concurrentLfu = new ConcurrentLfu(1, cacheSize, new ForegroundScheduler(), EqualityComparer.Default); + memoryCache = new MemoryCacheAdaptor(cacheSize); } public int CacheSize => concurrentLru.Capacity; public double ClassicLruHitRate => classicLru.Metrics.Value.HitRatio * 100; + public double MemoryCacheHitRate => memoryCache.Metrics.Value.HitRatio * 100; + public double ConcurrentLruHitRate => concurrentLru.Metrics.Value.HitRatio * 100; public double ConcurrentLfuHitRate => concurrentLfu.Metrics.Value.HitRatio * 100; @@ -35,6 +39,7 @@ public void TestKey(K key) concurrentLru.GetOrAdd(key, u => 1); classicLru.GetOrAdd(key, u => 1); concurrentLfu.GetOrAdd(key, u => 1); + memoryCache.GetOrAdd(key, u => 1); } public static void WriteToFile(string path, IEnumerable> results) diff --git a/BitFaster.Caching.HitRateAnalysis/BitFaster.Caching.HitRateAnalysis.csproj b/BitFaster.Caching.HitRateAnalysis/BitFaster.Caching.HitRateAnalysis.csproj index 98c2c77c..c05b32cb 100644 --- a/BitFaster.Caching.HitRateAnalysis/BitFaster.Caching.HitRateAnalysis.csproj +++ b/BitFaster.Caching.HitRateAnalysis/BitFaster.Caching.HitRateAnalysis.csproj @@ -24,6 +24,7 @@ NU1701 + diff --git a/BitFaster.Caching.HitRateAnalysis/MemoryCacheAdaptor.cs b/BitFaster.Caching.HitRateAnalysis/MemoryCacheAdaptor.cs new file mode 100644 index 00000000..4c50870a --- /dev/null +++ b/BitFaster.Caching.HitRateAnalysis/MemoryCacheAdaptor.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.Caching.Memory; + +namespace BitFaster.Caching.HitRateAnalysis +{ + public class MemoryCacheAdaptor : ICache + { + MemoryCacheOptionsAccessor accessor; + MemoryCache exMemoryCache; + CachePolicy policy; + CacheMetrics metrics; + + public MemoryCacheAdaptor(int capacity) + { + accessor = new MemoryCacheOptionsAccessor(); + accessor.Value.SizeLimit = capacity; + + exMemoryCache = new MemoryCache(accessor); + policy = new CachePolicy(new Optional(new BoundedPolicy(capacity)), Optional.None()); + metrics = new CacheMetrics(); + } + + public int Count => throw new NotImplementedException(); + + public Optional Metrics => new Optional(this.metrics); + + public Optional> Events => throw new NotImplementedException(); + + public CachePolicy Policy => this.policy; + + public ICollection Keys => throw new NotImplementedException(); + + private static readonly MemoryCacheEntryOptions SizeOne = new MemoryCacheEntryOptions() { Size = 1 }; + + public void AddOrUpdate(K key, V value) + { + exMemoryCache.Set(key, value, SizeOne); + } + + public void Clear() + { + throw new NotImplementedException(); + } + + public IEnumerator> GetEnumerator() + { + throw new NotImplementedException(); + } + + public V GetOrAdd(K key, Func valueFactory) + { + if (!exMemoryCache.TryGetValue(key, out object result)) + { + using ICacheEntry entry = exMemoryCache.CreateEntry(key); + + result = valueFactory(key); + entry.Value = result; + entry.SetSize(1); + + this.metrics.requestMissCount++; + } + else + { + this.metrics.requestHitCount++; + } + + return (V)result; + } + + public bool TryGet(K key, out V value) + { + throw new NotImplementedException(); + } + + public bool TryRemove(K key) + { + throw new NotImplementedException(); + } + + public bool TryUpdate(K key, V value) + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + + private class BoundedPolicy : IBoundedPolicy + { + private int capacity; + + public BoundedPolicy(int capacity) + { + this.capacity = capacity; + } + + public int Capacity => this.capacity; + + public void Trim(int itemCount) + { + throw new NotImplementedException(); + } + } + + private class CacheMetrics : ICacheMetrics + { + public long requestHitCount; + public long requestMissCount; + + public double HitRatio => (double)requestHitCount / (double)Total; + + public long Total => requestHitCount + requestMissCount; + + public long Hits => requestHitCount; + + public long Misses => requestMissCount; + + public long Evicted => throw new NotImplementedException(); + + public long Updated => throw new NotImplementedException(); + } + } + + public class MemoryCacheOptionsAccessor + : Microsoft.Extensions.Options.IOptions + { + private readonly MemoryCacheOptions options = new MemoryCacheOptions(); + + public MemoryCacheOptions Value => this.options; + + } +} diff --git a/BitFaster.Caching.HitRateAnalysis/Zipfian/Runner.cs b/BitFaster.Caching.HitRateAnalysis/Zipfian/Runner.cs index b9acc04d..b60f7eb8 100644 --- a/BitFaster.Caching.HitRateAnalysis/Zipfian/Runner.cs +++ b/BitFaster.Caching.HitRateAnalysis/Zipfian/Runner.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using BitFaster.Caching.Lfu; using BitFaster.Caching.Lru; using BitFaster.Caching.ThroughputAnalysis; using MathNet.Numerics; @@ -74,9 +75,13 @@ public static void Run() var concurrentLru = new ConcurrentLru(1, cacheSize, EqualityComparer.Default); var classicLru = new ClassicLru(1, cacheSize, EqualityComparer.Default); + var memCache = new MemoryCacheAdaptor(cacheSize); + var concurrentLfu = new ConcurrentLfu(cacheSize); var concurrentLruScan = new ConcurrentLru(1, cacheSize, EqualityComparer.Default); var classicLruScan = new ClassicLru(1, cacheSize, EqualityComparer.Default); + var memCacheScan = new MemoryCacheAdaptor(cacheSize); + var concurrentLfuScan = new ConcurrentLfu(cacheSize); var d = a.s == 0.5 ? 0 : 1; @@ -88,6 +93,14 @@ public static void Run() lruSw.Stop(); Console.WriteLine($"concurrentLru size={cacheSize} took {lruSw.Elapsed}."); + var lfuSw = Stopwatch.StartNew(); + for (int i = 0; i < sampleCount; i++) + { + concurrentLfu.GetOrAdd(zipdfDistribution[d][i], func); + } + lfuSw.Stop(); + Console.WriteLine($"concurrentLfu size={cacheSize} took {lfuSw.Elapsed}."); + var clruSw = Stopwatch.StartNew(); for (int i = 0; i < sampleCount; i++) { @@ -96,6 +109,14 @@ public static void Run() clruSw.Stop(); Console.WriteLine($"classic lru size={cacheSize} took {clruSw.Elapsed}."); + var memSw = Stopwatch.StartNew(); + for (int i = 0; i < sampleCount; i++) + { + memCache.GetOrAdd(zipdfDistribution[d][i], func); + } + memSw.Stop(); + Console.WriteLine($"memcache size={cacheSize} took {memSw.Elapsed}."); + var lruSwScan = Stopwatch.StartNew(); for (int i = 0; i < sampleCount; i++) { @@ -105,6 +126,15 @@ public static void Run() lruSwScan.Stop(); Console.WriteLine($"concurrentLruScan lru size={cacheSize} took {lruSwScan.Elapsed}."); + var lfuSwScan = Stopwatch.StartNew(); + for (int i = 0; i < sampleCount; i++) + { + concurrentLfuScan.GetOrAdd(zipdfDistribution[d][i], func); + concurrentLfuScan.GetOrAdd(i % n, func); + } + lfuSwScan.Stop(); + Console.WriteLine($"concurrentLfuScan lru size={cacheSize} took {lfuSwScan.Elapsed}."); + var clruSwScan = Stopwatch.StartNew(); for (int i = 0; i < sampleCount; i++) { @@ -114,6 +144,15 @@ public static void Run() clruSwScan.Stop(); Console.WriteLine($"classicLruScan lru size={cacheSize} took {clruSwScan.Elapsed}."); + var memSwScan = Stopwatch.StartNew(); + for (int i = 0; i < sampleCount; i++) + { + memCacheScan.GetOrAdd(zipdfDistribution[d][i], func); + memCacheScan.GetOrAdd(i % n, func); + } + memSwScan.Stop(); + Console.WriteLine($"memcacheScan size={cacheSize} took {memSwScan.Elapsed}."); + results.Add(new AnalysisResult { Cache = "ClassicLru", @@ -126,6 +165,18 @@ public static void Run() Duration = clruSw.Elapsed, }); + results.Add(new AnalysisResult + { + Cache = "MemoryCache", + N = a.N, + s = a.s, + CacheSizePercent = a.CacheSizePercent * 100.0, + Samples = a.Samples, + IsScan = false, + HitRatio = memCache.Metrics.Value.HitRatio * 100.0, + Duration = memSw.Elapsed, + }); + results.Add(new AnalysisResult { Cache = "ConcurrentLru", @@ -138,6 +189,18 @@ public static void Run() Duration = lruSw.Elapsed, }); + results.Add(new AnalysisResult + { + Cache = "ConcurrentLfu", + N = a.N, + s = a.s, + CacheSizePercent = a.CacheSizePercent * 100.0, + Samples = a.Samples, + IsScan = false, + HitRatio = concurrentLfu.Metrics.Value.HitRatio * 100.0, + Duration = lfuSw.Elapsed, + }); + results.Add(new AnalysisResult { Cache = "ClassicLru", @@ -150,6 +213,18 @@ public static void Run() Duration = clruSwScan.Elapsed, }); + results.Add(new AnalysisResult + { + Cache = "MemoryCache", + N = a.N, + s = a.s, + CacheSizePercent = a.CacheSizePercent * 100.0, + Samples = a.Samples, + IsScan = true, + HitRatio = memCacheScan.Metrics.Value.HitRatio * 100.0, + Duration = memSwScan.Elapsed, + }); + results.Add(new AnalysisResult { Cache = "ConcurrentLru", @@ -161,6 +236,18 @@ public static void Run() HitRatio = concurrentLruScan.Metrics.Value.HitRatio * 100.0, Duration = lruSwScan.Elapsed, }); + + results.Add(new AnalysisResult + { + Cache = "ConcurrentLfu", + N = a.N, + s = a.s, + CacheSizePercent = a.CacheSizePercent * 100.0, + Samples = a.Samples, + IsScan = true, + HitRatio = concurrentLfuScan.Metrics.Value.HitRatio * 100.0, + Duration = lfuSwScan.Elapsed, + }); } results.WriteToConsole();