diff --git a/BitFaster.Caching.HitRateAnalysis/Glimpse/Analysis.cs b/BitFaster.Caching.HitRateAnalysis/Glimpse/Analysis.cs new file mode 100644 index 00000000..44d9a61f --- /dev/null +++ b/BitFaster.Caching.HitRateAnalysis/Glimpse/Analysis.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using BitFaster.Caching.Lru; +using CsvHelper; + +namespace BitFaster.Caching.HitRateAnalysis.Glimpse +{ + public class Analysis + { + private readonly ConcurrentLru concurrentLru; + private readonly ClassicLru classicLru; + + public Analysis(int cacheSize) + { + this.concurrentLru = new ConcurrentLru(1, cacheSize, EqualityComparer.Default); + this.classicLru = new ClassicLru(1, cacheSize, EqualityComparer.Default); + } + + public int CacheSize => this.concurrentLru.Capacity; + + public double ConcurrentLruHitRate => this.concurrentLru.HitRatio * 100; + + public double ClassicLruHitRate => this.classicLru.HitRatio * 100; + + public void TestKey(long key) + { + this.concurrentLru.GetOrAdd(key, u => 1); + this.classicLru.GetOrAdd(key, u => 1); + } + + public void Compare() + { + Console.WriteLine($"Size {this.concurrentLru.Capacity} Classic HitRate {FormatHits(this.classicLru.HitRatio)} Concurrent HitRate {FormatHits(this.concurrentLru.HitRatio)}"); + } + + private static string FormatHits(double hitRate) + { + return string.Format("{0:N2}%", hitRate * 100.0); + } + + public static void WriteToFile(string path, IEnumerable results) + { + using (var writer = new StreamWriter(path)) + using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture)) + { + csv.WriteRecords(results); + } + } + } +} diff --git a/BitFaster.Caching.HitRateAnalysis/Glimpse/DataFile.cs b/BitFaster.Caching.HitRateAnalysis/Glimpse/DataFile.cs new file mode 100644 index 00000000..d67d14bd --- /dev/null +++ b/BitFaster.Caching.HitRateAnalysis/Glimpse/DataFile.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; + +namespace BitFaster.Caching.HitRateAnalysis.Glimpse +{ + // TODO: dedupe + public class DataFile + { + private static readonly Uri Uri = new Uri("https://github.com/ben-manes/caffeine/raw/master/simulator/src/main/resources/com/github/benmanes/caffeine/cache/simulator/parser/lirs/gli.trace.gz"); + private static readonly string FilePath = "gli.trace"; + + public static async Task DownloadIfNotExistsAsync() + { + var zipped = FilePath + ".gz"; + + if (!File.Exists(zipped)) + { + Console.WriteLine($"Downloading {Uri}..."); + HttpClient client = new HttpClient(); + var response = await client.GetAsync(Uri); + using (var fs = new FileStream(zipped, FileMode.CreateNew)) + { + await response.Content.CopyToAsync(fs); + } + } + + if (!File.Exists(FilePath)) + { + Console.WriteLine($"Decompressing {Uri}..."); + + using FileStream originalFileStream = new FileInfo(zipped).OpenRead(); + using var decompressedFileStream = File.Create(FilePath); + using var decompressionStream = new GZipStream(originalFileStream, CompressionMode.Decompress); + + decompressionStream.CopyTo(decompressedFileStream); + } + } + + public static IEnumerable EnumerateFileData() + { + // File data is like this: + //0 + //1 + //2 + //3 + //4 + //5 + //6 + + using StreamReader sr = new StreamReader(FilePath); + + while (sr.Peek() >= 0) + { + var line = sr.ReadLine(); + + if (long.TryParse(line, out var value)) + { + yield return value; + } + } + } + } +} diff --git a/BitFaster.Caching.HitRateAnalysis/Glimpse/Runner.cs b/BitFaster.Caching.HitRateAnalysis/Glimpse/Runner.cs new file mode 100644 index 00000000..106379a7 --- /dev/null +++ b/BitFaster.Caching.HitRateAnalysis/Glimpse/Runner.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BitFaster.Caching.HitRateAnalysis.Glimpse +{ + public class Runner + { + public static async Task Run() + { + int[] cacheSizes = { 250, 500, 750, 1000, 1250, 1500, 1750, 2000 }; + var analysis = cacheSizes.Select(s => new Analysis(s)).ToList(); + + await DataFile.DownloadIfNotExistsAsync(); + + Console.WriteLine("Running..."); + int count = 0; + var sw = Stopwatch.StartNew(); + + foreach (var key in DataFile.EnumerateFileData()) + { + foreach (var a in analysis) + { + a.TestKey(key); + } + + if (count++ % 100000 == 0) + { + Console.WriteLine($"Processed {count} keys..."); + } + } + + Console.WriteLine($"Tested {count} keys in {sw.Elapsed}"); + + foreach (var a in analysis) + { + a.Compare(); + } + + Analysis.WriteToFile("results.glimpse.csv", analysis); + } + } +} diff --git a/BitFaster.Caching.HitRateAnalysis/Program.cs b/BitFaster.Caching.HitRateAnalysis/Program.cs index c46dcabe..be098525 100644 --- a/BitFaster.Caching.HitRateAnalysis/Program.cs +++ b/BitFaster.Caching.HitRateAnalysis/Program.cs @@ -3,6 +3,7 @@ var menu = new EasyConsole.Menu() .Add("Zipf", () => BitFaster.Caching.HitRateAnalysis.Zipfian.Runner.Run()) - .Add("Wikibench", () => BitFaster.Caching.HitRateAnalysis.Wikibench.Runner.Run().Wait()); + .Add("Wikibench", () => BitFaster.Caching.HitRateAnalysis.Wikibench.Runner.Run().Wait()) + .Add("Glimpse", () => BitFaster.Caching.HitRateAnalysis.Glimpse.Runner.Run().Wait()); menu.Display();