diff --git a/benchmark/Prime.Extensions.Benchmarks.sh b/benchmark/Prime.Extensions.Benchmarks.sh new file mode 100644 index 0000000..0c3688c --- /dev/null +++ b/benchmark/Prime.Extensions.Benchmarks.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +rm -rf BenchmarkDotNet.Artifacts +rm -rf Prime.Extensions.Benchmarks/bin +rm -rf Prime.Extensions.Benchmarks/obj + +dotnet run --project Prime.Extensions.Benchmarks/ -c release + + + diff --git a/benchmark/Prime.Extensions.Benchmarks/FindIdsBenchmarks.cs b/benchmark/Prime.Extensions.Benchmarks/FindIdsBenchmarks.cs new file mode 100644 index 0000000..44b00e6 --- /dev/null +++ b/benchmark/Prime.Extensions.Benchmarks/FindIdsBenchmarks.cs @@ -0,0 +1,149 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using BenchmarkDotNet.Attributes; +using MongoDB.Driver; +using MongoDB.Prime.Extensions; +using Squadron; + +namespace Prime.Extensions.Benchmarks +{ + [RPlotExporter, CategoriesColumn, RankColumn, MeanColumn, MedianColumn, MemoryDiagnoser] + public class FindIdsBenchmarks + { + private static MongoReplicaSetResource _mongoRsResource; + private static IMongoCollection _barCollection; + private static IReadOnlyList _200BarsToFind; + private static IReadOnlyList _1000BarsToFind; + private static IReadOnlyList _5000BarsToFind; + private static IReadOnlyList _10000BarsToFind; + + [GlobalSetup] + public void GlobalSetup() + { + InitializeOnce().GetAwaiter().GetResult(); + } + + [Benchmark()] + public Task FindIdsClassic200() + { + return FindClassicAsync(_200BarsToFind); + } + + [Benchmark] + public Task FindIdsOptimized200() + { + return FindOptimizedAsync(_200BarsToFind); + } + + [Benchmark()] + public Task FindIdsClassic1000() + { + return FindClassicAsync(_1000BarsToFind); + } + + [Benchmark] + public Task FindIdsOptimized1000() + { + return FindOptimizedAsync(_1000BarsToFind); + } + + [Benchmark()] + public Task FindIdsClassic5000() + { + return FindClassicAsync(_5000BarsToFind); + } + + [Benchmark] + public Task FindIdsOptimized5000() + { + return FindOptimizedAsync(_5000BarsToFind); + } + + [Benchmark()] + public Task FindIdsClassic10000() + { + return FindClassicAsync(_10000BarsToFind); + } + + [Benchmark] + public Task FindIdsOptimized10000() + { + return FindOptimizedAsync(_10000BarsToFind); + } + + public async Task FindOptimizedAsync(IReadOnlyList barsToFind) + { + IDictionary result = await _barCollection + .FindIdsAsync(barsToFind, bar => bar.Id); + + if (result.Count != barsToFind.Count) + throw new Exception("WrongResult"); + } + + public async Task FindClassicAsync(IReadOnlyList barsToFind) + { + FilterDefinition filter = + Builders.Filter.In(b => b.Id, barsToFind); + + List bars = await _barCollection + .Find(filter) + .ToListAsync(); + + var result = bars.ToDictionary(t => t.Id); + + if (result.Count != barsToFind.Count) + throw new Exception("WrongResult"); + } + + private async Task InitializeOnce() + { + if (_10000BarsToFind == null) + { + _mongoRsResource = new MongoReplicaSetResource(); + + await _mongoRsResource.InitializeAsync(); + + IMongoDatabase mongoDatabase = _mongoRsResource.CreateDatabase(); + + _barCollection = mongoDatabase.GetCollection(); + + IReadOnlyList alldBars = CreateRandomBars(5_000_000); + + await _barCollection.InsertManyAsync(alldBars); + + _200BarsToFind = alldBars.Skip(500_000).Take(200).Select(bar => bar.Id).ToList(); + _1000BarsToFind = alldBars.Skip(500_000).Take(1000).Select(bar => bar.Id).ToList(); + _5000BarsToFind = alldBars.Skip(500_000).Take(5000).Select(bar => bar.Id).ToList(); + _10000BarsToFind = alldBars.Skip(500_000).Take(10000).Select(bar => bar.Id).ToList(); + } + } + + private static IReadOnlyList CreateRandomBars(int count) + { + return Enumerable + .Range(0, count) + .Select(number => new Bar( + Guid.NewGuid(), + $"BarName-Unique-{number}", + $"BarValue-{Guid.NewGuid()}")) + .ToList(); + } + + private class Bar + { + public Bar(Guid id, string name, string value) + { + Id = id; + Name = name; + Value = value; + } + + public Guid Id { get; } + + public string Name { get; } + public string Value { get; } + } + } +} diff --git a/benchmark/Prime.Extensions.Benchmarks/Prime.Extensions.Benchmarks.csproj b/benchmark/Prime.Extensions.Benchmarks/Prime.Extensions.Benchmarks.csproj new file mode 100644 index 0000000..895deb8 --- /dev/null +++ b/benchmark/Prime.Extensions.Benchmarks/Prime.Extensions.Benchmarks.csproj @@ -0,0 +1,20 @@ + + + + Exe + net5.0 + + + + + + + + + + + + + + + diff --git a/benchmark/Prime.Extensions.Benchmarks/Program.cs b/benchmark/Prime.Extensions.Benchmarks/Program.cs new file mode 100644 index 0000000..9af0db2 --- /dev/null +++ b/benchmark/Prime.Extensions.Benchmarks/Program.cs @@ -0,0 +1,15 @@ +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Running; + +namespace Prime.Extensions.Benchmarks +{ + public class Program + { + public static void Main(string[] args) + { + BenchmarkSwitcher + .FromAssembly(typeof(Program).Assembly) + .Run(args, new DebugInProcessConfig()); + } + } +} diff --git a/benchmark/Prime.Extensions.Benchmarks/reports/FindIdsBenchmark.Report.md b/benchmark/Prime.Extensions.Benchmarks/reports/FindIdsBenchmark.Report.md new file mode 100644 index 0000000..69d5d64 --- /dev/null +++ b/benchmark/Prime.Extensions.Benchmarks/reports/FindIdsBenchmark.Report.md @@ -0,0 +1,19 @@ +``` ini + +BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19042 +Intel Core i7-9750H CPU 2.60GHz, 1 CPU, 12 logical and 6 physical cores +.NET Core SDK=5.0.202 + [Host] : .NET Core 5.0.5 (CoreCLR 5.0.521.16609, CoreFX 5.0.521.16609), X64 + +``` + +| Method | Mean | Error | StdDev | Median | Rank | Gen 0 | Gen 1 | Gen 2 | Allocated | +|---------------------- |----------:|---------:|----------:|----------:|-----:|----------:|---------:|------:|------------:| +| FindIdsClassic200 | 10.65 ms | 0.480 ms | 1.394 ms | 10.67 ms | 1 | - | - | - | 322.7 KB | +| FindIdsOptimized200 | 10.55 ms | 0.321 ms | 0.935 ms | 10.43 ms | 1 | 46.8750 | - | - | 338.72 KB | +| FindIdsClassic1000 | 25.45 ms | 0.580 ms | 1.684 ms | 25.29 ms | 3 | 218.7500 | 62.5000 | - | 1372.31 KB | +| FindIdsOptimized1000 | 24.57 ms | 1.318 ms | 3.694 ms | 24.18 ms | 2 | 250.0000 | 93.7500 | - | 1531.93 KB | +| FindIdsClassic5000 | 141.15 ms | 8.385 ms | 24.058 ms | 139.35 ms | 5 | 1000.0000 | 500.0000 | - | 6877.5 KB | +| FindIdsOptimized5000 | 83.00 ms | 2.871 ms | 8.375 ms | 81.85 ms | 4 | 1142.8571 | 571.4286 | - | 7369.99 KB | +| FindIdsClassic10000 | 241.54 ms | 6.473 ms | 18.469 ms | 242.59 ms | 7 | 2000.0000 | 666.6667 | - | 13674.21 KB | +| FindIdsOptimized10000 | 156.01 ms | 6.863 ms | 19.910 ms | 154.24 ms | 6 | 2250.0000 | 750.0000 | - | 14622.44 KB | diff --git a/src/MongoDB.Extensions.sln b/src/MongoDB.Extensions.sln index 790b492..93069f7 100644 --- a/src/MongoDB.Extensions.sln +++ b/src/MongoDB.Extensions.sln @@ -11,9 +11,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Session", "Session\Session. EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Session.Tests", "Session.Tests\Session.Tests.csproj", "{7D953E6D-B34C-4724-8E4D-A968110CAC74}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Prime.Extensions", "Prime.Extensions\Prime.Extensions.csproj", "{284AF759-C10C-4C89-AFA3-F2D190115CC6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Prime.Extensions", "Prime.Extensions\Prime.Extensions.csproj", "{284AF759-C10C-4C89-AFA3-F2D190115CC6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Prime.Extensions.Tests", "Prime.Extensions.Tests\Prime.Extensions.Tests.csproj", "{74A9A535-5E20-4453-A670-2E37A279226C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Prime.Extensions.Tests", "Prime.Extensions.Tests\Prime.Extensions.Tests.csproj", "{74A9A535-5E20-4453-A670-2E37A279226C}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmark", "benchmark", "{73BCA034-E71E-462C-93E7-DF956BBC5E67}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Prime.Extensions.Benchmarks", "..\benchmark\Prime.Extensions.Benchmarks\Prime.Extensions.Benchmarks.csproj", "{960D6087-8C05-4B19-B7D6-78F92F7BAA3F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -45,10 +49,17 @@ Global {74A9A535-5E20-4453-A670-2E37A279226C}.Debug|Any CPU.Build.0 = Debug|Any CPU {74A9A535-5E20-4453-A670-2E37A279226C}.Release|Any CPU.ActiveCfg = Release|Any CPU {74A9A535-5E20-4453-A670-2E37A279226C}.Release|Any CPU.Build.0 = Release|Any CPU + {960D6087-8C05-4B19-B7D6-78F92F7BAA3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {960D6087-8C05-4B19-B7D6-78F92F7BAA3F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {960D6087-8C05-4B19-B7D6-78F92F7BAA3F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {960D6087-8C05-4B19-B7D6-78F92F7BAA3F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {960D6087-8C05-4B19-B7D6-78F92F7BAA3F} = {73BCA034-E71E-462C-93E7-DF956BBC5E67} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {1C05A190-7BC9-4DE5-A49D-D7557B01869D} EndGlobalSection diff --git a/src/Prime.Extensions/MongoCollectionFindExtensions.cs b/src/Prime.Extensions/MongoCollectionFindExtensions.cs index a059734..823a24b 100644 --- a/src/Prime.Extensions/MongoCollectionFindExtensions.cs +++ b/src/Prime.Extensions/MongoCollectionFindExtensions.cs @@ -24,6 +24,11 @@ public static async Task> FindIdsAsync(); + } + Func idSelectorFunc = idResultSelector.Compile(); int batchPartitionsCount =