diff --git a/README.md b/README.md index 2c3bf86..8650821 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,13 @@ ReindexerNet is a .NET binding(builtin & builtinserver) and connector(Grpc, ~~OpenApi~~) for embeddable in-memory document db [Reindexer](https://github.com/Restream/reindexer). We are using ReindexerNET in production environments for a long time, and even if all unit tests are passed, we don't encourge you to use in a prod environment without testing. So please test in your environment before using. -If you have any questions about Reindexer, please use [main page](https://github.com/Restream/reindexer) of Reindexer. Feel free to report issues and contribute about **ReindexerNet**. You can check [change logs here](CHANGELOG.md). +If you have any questions about Reindexer, please use [main page](https://github.com/Restream/reindexer) of Reindexer. Feel free to report issues and contribute about **ReindexerNet**. You can check the [change logs here](CHANGELOG.md). + +## Contents +- [Sample Usage](#sample-usage) +- [Packages](#packages) +- [Performance](#performance) +- [Roadmap](#to-do-list) ## Sample Usage: - Add one of these client packages according to your use case @@ -24,24 +30,24 @@ If you have any questions about Reindexer, please use [main page](https://github - [![Osx Nuget](https://img.shields.io/nuget/v/ReindexerNet.Embedded.Native.Osx-x64?label=Embedded.Native.Osx&color=1182c2&style=flat-square&logo=nuget)](https://www.nuget.org/packages/ReindexerNet.Embedded.Native.Osx-x64) - [![Linux Nuget](https://img.shields.io/nuget/v/ReindexerNet.Embedded.Native.Linux-x64?label=Embedded.Native.Linux&color=1182c2&style=flat-square&logo=nuget)](https://www.nuget.org/packages/ReindexerNet.Embedded.Native.Linux-x64) - [![AlpineLinux Nuget](https://img.shields.io/nuget/v/ReindexerNet.Embedded.Native.AlpineLinux-x64?label=Embedded.Native.AlpineLinux&color=1182c2&style=flat-square&logo=nuget)](https://www.nuget.org/packages/ReindexerNet.Embedded.Native.AlpineLinux-x64) - - Or you can add all native packages like this: + - Or you can use conditon to add native packages that are only needed according to the operating system: ```xml ``` @@ -166,8 +172,8 @@ This package contains Grpc client to use Reindexer server over grpc protocol. It ### ReindexerNet.Core [![Core Nuget](https://img.shields.io/nuget/v/ReindexerNet.Core?label=Core&color=1182c2&style=flat-square&logo=nuget)](https://www.nuget.org/packages/ReindexerNet.Core) This package contains base types and common models for Reindexer and .net packages. You can use the models in this package as OpenApi/Rest models. Every model in this package has `DataContract` and `JsonPropertyName` attributes to support valid json serialization for Reindexer rest api. - -## ReindexerNet.Embedded Benchmarks and Comparations +## Performance +## ReindexerNet.Embedded Benchmarks and Comparisons ``` ReindexerNet v0.4.1 (Reindexer v3.20) Cachalot v2.0.8 @@ -176,227 +182,203 @@ Realm.NET v11.6.1 BenchmarkDotNet v0.13.11, Windows 10 (10.0.19045.3636/22H2/2022Update) Intel Core i7-8700K CPU 3.70GHz (Coffee Lake), 1 CPU, 12 logical and 6 physical cores -.NET SDK 8.0.100-rc.2.23502.2 - [Host] : .NET 7.0.13 (7.0.1323.51816), X64 RyuJIT AVX2 +.NET SDK 8.0.100 + [Host] : .NET 8.0.0 (8.0.23.53103), X64 RyuJIT AVX2 ``` ### Insert Benchmarks > #### Insert (Without controlling existance) - +```markdown +| Method | N | Mean | Error | Gen0 | Gen1 | Gen2 | Allocated | +|------------------- |------- |------------:|------:|-------------:|------------:|----------:|------------:| +| Cachalot | 10000 | 503.0 ms | NA | 30000.0000 | 9000.0000 | - | 211.52 MB | +| CachalotCompressed | 10000 | 1,361.1 ms | NA | 563000.0000 | 35000.0000 | - | 3408.5 MB | +| CachalotOnlyMemory | 10000 | 520.3 ms | NA | 23000.0000 | 8000.0000 | 2000.0000 | 138.14 MB | +| LiteDb | 10000 | 1,018.3 ms | NA | 302000.0000 | 1000.0000 | - | 1829.46 MB | +| LiteDbMemory | 10000 | 978.7 ms | NA | 304000.0000 | 1000.0000 | - | 1919.56 MB | +| Realm | 10000 | 500.5 ms | NA | 7000.0000 | 2000.0000 | 1000.0000 | 43.29 MB | +| ReindexerNet | 10000 | 215.8 ms | NA | 1000.0000 | 1000.0000 | - | 9 MB | +| ReindexerNetDense | 10000 | 215.1 ms | NA | 1000.0000 | 1000.0000 | - | 9 MB | +| Cachalot | 100000 | 3,574.3 ms | NA | 293000.0000 | 88000.0000 | - | 2157.84 MB | +| CachalotCompressed | 100000 | 11,675.4 ms | NA | 5506000.0000 | 315000.0000 | - | 33376.07 MB | +| CachalotOnlyMemory | 100000 | 2,587.6 ms | NA | 211000.0000 | 56000.0000 | 1000.0000 | 1373.43 MB | +| LiteDb | 100000 | 13,296.9 ms | NA | 3836000.0000 | 14000.0000 | 3000.0000 | 23072.51 MB | +| LiteDbMemory | 100000 | 12,576.0 ms | NA | 3769000.0000 | 16000.0000 | 4000.0000 | 23563.28 MB | +| Realm | 100000 | 3,121.4 ms | NA | 73000.0000 | 49000.0000 | 1000.0000 | 432.62 MB | +| ReindexerNet | 100000 | 1,970.3 ms | NA | 16000.0000 | 2000.0000 | 1000.0000 | 94.52 MB | +| ReindexerNetDense | 100000 | 1,824.3 ms | NA | 16000.0000 | 2000.0000 | 1000.0000 | 94.52 MB | +``` > #### Upsert (Update if exists, otherwise insert) +``` +| Method | N | Mean | Error | Gen0 | Gen1 | Allocated | +|------------------- |------- |------------:|------:|-------------:|------------:|------------:| +| Cachalot | 10000 | 1,707.0 ms | NA | 106000.0000 | 10000.0000 | 658.26 MB | +| CachalotCompressed | 10000 | 2,853.8 ms | NA | 671000.0000 | 32000.0000 | 4059.77 MB | +| CachalotOnlyMemory | 10000 | 315.8 ms | NA | 20000.0000 | 5000.0000 | 125.98 MB | +| LiteDb | 10000 | 306.9 ms | NA | 54000.0000 | - | 327.39 MB | +| LiteDbMemory | 10000 | 213.3 ms | NA | 53000.0000 | - | 320.88 MB | +| Realm | 10000 | 218.5 ms | NA | 7000.0000 | 6000.0000 | 43.31 MB | +| ReindexerNet | 10000 | 182.2 ms | NA | 1000.0000 | - | 9 MB | +| ReindexerNetDense | 10000 | 183.0 ms | NA | 1000.0000 | - | 9 MB | +| Cachalot | 100000 | 5,693.4 ms | NA | 465000.0000 | 90000.0000 | 2979 MB | +| CachalotCompressed | 100000 | 14,753.4 ms | NA | 5712000.0000 | 303000.0000 | 34521.09 MB | +| CachalotOnlyMemory | 100000 | 3,495.1 ms | NA | 207000.0000 | 51000.0000 | 1258.28 MB | +| LiteDb | 100000 | 2,760.3 ms | NA | 701000.0000 | 14000.0000 | 4196.2 MB | +| LiteDbMemory | 100000 | 2,582.1 ms | NA | 633000.0000 | 14000.0000 | 3793.94 MB | +| Realm | 100000 | 2,061.4 ms | NA | 72000.0000 | 51000.0000 | 432.61 MB | +| ReindexerNet | 100000 | 1,531.4 ms | NA | 15000.0000 | - | 99 MB | +| ReindexerNetDense | 100000 | 1,497.3 ms | NA | 15000.0000 | - | 96.62 MB | +``` ### Select Benchmarks -> #### Single Primary Key(Guid) in a loop -> ``` -> foreach (id in ids) -> { -> WHERE Id = id -> } -> ``` +#### Single Primary Key(Guid) in a loop +``` +foreach (id in ids) +{ + WHERE Id = id +} -| Method | Categories | N | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated | -|---------------------------------------------- |---------------------------------------------- |----- |-----------------:|---------------:|---------------:|------------:|-----------:|----------:|--------------:| -| Cachalot_SelectSinglePK | SelectSinglePK,Cachalot | 1000 | 282,785.050 us | 4,029.8328 us | 623.6211 us | 30500.0000 | 4000.0000 | - | 187511.56 KB | -| | | | | | | | | | | -| CachalotCompressed_SelectSinglePK | SelectSinglePK,CachalotCompressed | 1000 | 348,094.320 us | 31,687.8269 us | 8,229.2280 us | 35000.0000 | 5000.0000 | - | 220151.91 KB | -| | | | | | | | | | | -| CachalotMemory_SelectSinglePK | SelectSinglePK,CachalotMemory | 1000 | 295,441.588 us | 18,397.1767 us | 2,846.9835 us | 30500.0000 | 4000.0000 | - | 187511.17 KB | -| | | | | | | | | | | -| LiteDb_SelectSinglePK | SelectSinglePK,LiteDb | 1000 | 257,152.880 us | 91,902.9020 us | 23,866.8918 us | 35000.0000 | 5000.0000 | - | 216416.3 KB | -| | | | | | | | | | | -| LiteDbMemory_SelectSinglePK | SelectSinglePK,LiteDbMemory | 1000 | 258,560.020 us | 20,879.9150 us | 5,422.4476 us | 35500.0000 | 4500.0000 | 500.0000 | 215364.41 KB | -| | | | | | | | | | | -| Realm_SelectSinglePK | SelectSinglePK,Realm | 1000 | 2,481.698 us | 572.0812 us | 148.5677 us | 82.0313 | 78.1250 | - | 516.25 KB | -| | | | | | | | | | | -| ReindexerNet_SelectSinglePK | SelectSinglePK,ReindexerNet | 1000 | 3,781.301 us | 100.0469 us | 15.4824 us | 125.0000 | 31.2500 | 3.9063 | 814.7 KB | -| | | | | | | | | | | -| ReindexerNetSpanJson_SelectSinglePK | SelectSinglePK,ReindexerNetSpanJson | 1000 | 3,865.852 us | 69.9667 us | 10.8274 us | 121.0938 | 27.3438 | - | 805.36 KB | -| | | | | | | | | | | -| ReindexerNetSql_SelectSinglePK | SelectSinglePK,ReindexerNetSql | 1000 | 118,826.668 us | 12,686.3854 us | 3,294.6140 us | 6800.0000 | 3600.0000 | 600.0000 | 38843.18 KB | - - -> #### Multiple Primary Key(Guid) at once -> ``` -> WHERE Id IN (id_1,id_2,id_3,...,id_N) -> ``` -> | Method | Categories | N | Mean | Allocated | Error | StdDev | Median | Gen0 | Gen1 | Gen2 | -> |-------------------------------------------- |-------------------------------------------- |----- |--------------:|-------------:|--------------:|--------------:|--------------:|-----------:|-----------:|----------:| -| Cachalot_SelectMultiplePK | SelectMultiplePK,Cachalot | 1000 | 278,948.588 us | 3,533.1024 us | 546.7515 us | 28000.0000 | 4500.0000 | - | 187879.49 KB | -| | | | | | | | | | | -| CachalotCompressed_SelectMultiplePK | SelectMultiplePK,CachalotCompressed | 1000 | 327,700.900 us | 2,259.7422 us | 586.8479 us | 34000.0000 | 4500.0000 | - | 217708.79 KB | -| | | | | | | | | | | -| CachalotMemory_SelectMultiplePK | SelectMultiplePK,CachalotMemory | 1000 | 317,860.340 us | 17,609.6170 us | 4,573.1616 us | 29000.0000 | 6000.0000 | 1000.0000 | 187881.41 KB | -| | | | | | | | | | | -| LiteDb_SelectMultiplePK | SelectMultiplePK,LiteDb | 1000 | 218,759.327 us | 9,240.6277 us | 2,399.7617 us | 31666.6667 | 5000.0000 | 666.6667 | 190726.18 KB | -| | | | | | | | | | | -| LiteDbMemory_SelectMultiplePK | SelectMultiplePK,LiteDbMemory | 1000 | 232,426.967 us | 40,450.5974 us | 10,504.8917 us | 32000.0000 | 5000.0000 | 666.6667 | 192243.95 KB | -| | | | | | | | | | | -| Realm_SelectMultiplePK | SelectMultiplePK,Realm | 1000 | 10,461.847 us | 416.8394 us | 64.5064 us | 62.5000 | 46.8750 | - | 521.59 KB | -| | | | | | | | | | | -| ReindexerNet_SelectMultiplePK | SelectMultiplePK,ReindexerNet | 1000 | 290.542 us | 5.0227 us | 1.3044 us | 8.7891 | 0.4883 | - | 55.64 KB | -| | | | | | | | | | | -| ReindexerNetSpanJson_SelectMultiplePK | SelectMultiplePK,ReindexerNetSpanJson | 1000 | 292.723 us | 6.2390 us | 1.6203 us | 8.7891 | 0.4883 | - | 55.64 KB | -| | | | | | | | | | | -| ReindexerNetSql_SelectMultiplePK | SelectMultiplePK,ReindexerNetSql | 1000 | 103,247.260 us | 5,065.8543 us | 1,315.5863 us | 6833.3333 | 3666.6667 | 666.6667 | 38479.89 KB | - - -> #### Single Hash in a loop -> ``` -> foreach (value in values) -> { -> WHERE StringProperty = value -> } -> ``` -> | Method | Categories | N | Mean | Allocated | Error | StdDev | Median | Gen0 | Gen1 | Gen2 | -> |-------------------------------------------- |-------------------------------------------- |----- |--------------:|-------------:|--------------:|--------------:|--------------:|-----------:|-----------:|----------:| -| Cachalot_SelectSingleHash | SelectSingleHash,Cachalot | 1000 | 495,281.120 us | 7,065.3711 us | 1,834.8544 us | 33000.0000 | 4000.0000 | - | 203251.58 KB | -| | | | | | | | | | | -| CachalotCompressed_SelectSingleHash | SelectSingleHash,CachalotCompressed | 1000 | 499,396.525 us | 11,637.6496 us | 1,800.9392 us | 33000.0000 | 4000.0000 | - | 203259.39 KB | -| | | | | | | | | | | -| CachalotMemory_SelectSingleHash | SelectSingleHash,CachalotMemory | 1000 | 494,665.950 us | 4,578.2394 us | 708.4876 us | 33000.0000 | 4000.0000 | - | 202883.13 KB | -| | | | | | | | | | | -| LiteDb_SelectSingleHash | SelectSingleHash,LiteDb | 1000 | 258,610.400 us | 8,568.2965 us | 2,225.1594 us | 36000.0000 | 4500.0000 | 500.0000 | 218752.75 KB | -| | | | | | | | | | | -| LiteDbMemory_SelectSingleHash | SelectSingleHash,LiteDbMemory | 1000 | 256,300.775 us | 8,398.4181 us | 1,299.6645 us | 35500.0000 | 4500.0000 | 500.0000 | 217346.21 KB | -| | | | | | | | | | | -| Realm_SelectSingleHash | SelectSingleHash,Realm | 1000 | 136,388.090 us | 4,077.2886 us | 1,058.8589 us | 1250.0000 | 1000.0000 | - | 8911.91 KB | -| | | | | | | | | | | -| ReindexerNet_SelectSingleHash | SelectSingleHash,ReindexerNet | 1000 | 112,529.608 us | 13,604.9193 us | 3,533.1543 us | 6800.0000 | 3600.0000 | 600.0000 | 38930.61 KB | -| | | | | | | | | | | -| ReindexerNetSpanJson_SelectSingleHash | SelectSingleHash,ReindexerNetSpanJson | 1000 | 82,073.017 us | 7,637.4217 us | 1,983.4142 us | 4000.0000 | 2166.6667 | 500.0000 | 22327.14 KB | -| | | | | | | | | | | -| ReindexerNetSql_SelectSingleHash | SelectSingleHash,ReindexerNetSql | 1000 | 119,879.556 us | 15,616.2359 us | 4,055.4869 us | 6800.0000 | 3600.0000 | 600.0000 | 38834.99 KB | - - -> #### Single Hash in a Parallel loop -> ``` -> Parallel.Foreach(values, value => -> { -> WHERE StringProperty = value -> }); -> ``` -> | Method | Categories | N | Mean | Allocated | Error | StdDev | Median | Gen0 | Gen1 | Gen2 | -> |-------------------------------------------- |-------------------------------------------- |----- |--------------:|-------------:|--------------:|--------------:|--------------:|-----------:|-----------:|----------:| -| Cachalot_SelectSingleHashParallel | SelectSingleHashParallel,Cachalot | 1000 | 223,647.250 us | 11,039.6038 us | 1,708.3909 us | 35000.0000 | 7000.0000 | - | 203285.11 KB | -| | | | | | | | | | | -| CachalotCompressed_SelectSingleHashParallel | SelectSingleHashParallel,CachalotCompressed | 1000 | 220,399.560 us | 14,033.4679 us | 3,644.4471 us | 35333.3333 | 7333.3333 | - | 203269.54 KB | -| | | | | | | | | | | -| CachalotMemory_SelectSingleHashParallel | SelectSingleHashParallel,CachalotMemory | 1000 | 263,773.960 us | 27,919.2992 us | 7,250.5533 us | 35000.0000 | 8000.0000 | - | 202945.35 KB | -| | | | | | | | | | | -| LiteDb_SelectSingleHashParallel | SelectSingleHashParallel,LiteDb | 1000 | 221,579.473 us | 19,197.2353 us | 4,985.4610 us | 37000.0000 | 7666.6667 | 333.3333 | 216827.26 KB | -| | | | | | | | | | | -| LiteDbMemory_SelectSingleHashParallel | SelectSingleHashParallel,LiteDbMemory | 1000 | 228,644.927 us | 29,638.3253 us | 7,696.9790 us | 37000.0000 | 7666.6667 | 333.3333 | 217929.17 KB | -| | | | | | | | | | | -| Realm_SelectSingleHashParallel | SelectSingleHashParallel,Realm | 1000 | 49,549.095 us | 16,808.3345 us | 2,601.1084 us | 2300.0000 | 500.0000 | - | 13985.24 KB | -| | | | | | | | | | | -| ReindexerNet_SelectSingleHashParallel | SelectSingleHashParallel,ReindexerNet | 1000 | 55,591.460 us | 9,269.8340 us | 2,407.3465 us | 7222.2222 | 3222.2222 | 222.2222 | 39038.02 KB | -| | | | | | | | | | | -| ReindexerNetSpanJson_SelectSingleHashParallel | SelectSingleHashParallel,ReindexerNetSpanJson | 1000 | 39,962.429 us | 5,217.6222 us | 807.4328 us | 4000.0000 | 2000.0000 | 71.4286 | 22432.14 KB | -| | | | | | | | | | | -| ReindexerNetSql_SelectSingleHashParallel | SelectSingleHashParallel,ReindexerNetSql | 1000 | 59,198.408 us | 8,624.5871 us | 1,334.6644 us | 7222.2222 | 3222.2222 | 222.2222 | 38848.56 KB | - - -> #### Multiple Hash at once -> ``` -> WHERE StringProperty IN ('abc', 'def', .... ) -> ``` -> | Method | Categories | N | Mean | Allocated | Error | StdDev | Median | Gen0 | Gen1 | Gen2 | -> |-------------------------------------------- |-------------------------------------------- |----- |--------------:|-------------:|--------------:|--------------:|--------------:|-----------:|-----------:|----------:| -| Cachalot_SelectMultipleHash | SelectMultipleHash,Cachalot | 1000 | 1,484,532.475 us | 37,109.4888 us | 5,742.7345 us | 152000.0000 | 22000.0000 | - | 997263.42 KB | -| | | | | | | | | | | -| CachalotCompressed_SelectMultipleHash | SelectMultipleHash,CachalotCompressed | 1000 | 1,832,603.760 us | 11,512.5847 us | 2,989.7817 us | 186000.0000 | 26000.0000 | - | 1206270.39 KB | -| | | | | | | | | | | -| CachalotMemory_SelectMultipleHash | SelectMultipleHash,CachalotMemory | 1000 | 313,764.380 us | 11,475.7239 us | 2,980.2091 us | 29000.0000 | 6000.0000 | 1000.0000 | 187772.65 KB | -| | | | | | | | | | | -| LiteDb_SelectMultipleHash | SelectMultipleHash,LiteDb | 1000 | 226,051.750 us | 5,536.0051 us | 856.7029 us | 31666.6667 | 5000.0000 | 666.6667 | 191589.16 KB | -| | | | | | | | | | | -| LiteDbMemory_SelectMultipleHash | SelectMultipleHash,LiteDbMemory | 1000 | 227,047.733 us | 11,887.6348 us | 3,087.1810 us | 31666.6667 | 5333.3333 | 666.6667 | 191670.53 KB | -| | | | | | | | | | | -| Realm_SelectMultipleHash | SelectMultipleHash,Realm | 1000 | 11,876.545 us | 951.3057 us | 247.0511 us | 62.5000 | 46.8750 | - | 464.74 KB | -| | | | | | | | | | | -| ReindexerNet_SelectMultipleHash | SelectMultipleHash,ReindexerNet | 1000 | 102,228.168 us | 9,141.4401 us | 2,374.0030 us | 6800.0000 | 3600.0000 | 600.0000 | 38203.88 KB | -| | | | | | | | | | | -| ReindexerNetSpanJson_SelectMultipleHash | SelectMultipleHash,ReindexerNetSpanJson | 1000 | 70,505.400 us | 5,345.3941 us | 827.2056 us | 4000.0000 | 2285.7143 | 571.4286 | 21600.45 KB | -| | | | | | | | | | | -| ReindexerNetSql_SelectMultipleHash | SelectMultipleHash,ReindexerNetSql | 1000 | 103,781.868 us | 2,847.8748 us | 739.5840 us | 6800.0000 | 3600.0000 | 600.0000 | 38296.22 KB | - - -> #### Range Filtering -> ``` -> WHERE IntProperty < i -> WHERE IntProperty >= i -> ``` -> | Method | Categories | N | Mean | Allocated | Error | StdDev | Median | Gen0 | Gen1 | Gen2 | -> |-------------------------------------------- |-------------------------------------------- |----- |--------------:|-------------:|--------------:|--------------:|--------------:|-----------:|-----------:|----------:| -| Cachalot_SelectRange | SelectRange,Cachalot | 1000 | 2,688,260.175 us | 85,898.9574 us | 13,292.9587 us | 276000.0000 | 40000.0000 | - | 1802101.88 KB | -| | | | | | | | | | | -| CachalotCompressed_SelectRange | SelectRange,CachalotCompressed | 1000 | 3,099,263.400 us | 67,706.5567 us | 10,477.6646 us | 310000.0000 | 45000.0000 | - | 2011108.75 KB | -| | | | | | | | | | | -| CachalotMemory_SelectRange | SelectRange,CachalotMemory | 1000 | 313,817.340 us | 24,892.1160 us | 6,464.4035 us | 28000.0000 | 6000.0000 | 1000.0000 | 185407.89 KB | -| | | | | | | | | | | -| LiteDb_SelectRange | SelectRange,LiteDb | 1000 | 215,661.108 us | 4,626.9026 us | 716.0183 us | 30333.3333 | 4666.6667 | 666.6667 | 182618.11 KB | -| | | | | | | | | | | -| LiteDbMemory_SelectRange | SelectRange,LiteDbMemory | 1000 | 221,388.847 us | 6,773.8405 us | 1,759.1449 us | 30333.3333 | 4666.6667 | 666.6667 | 182624.05 KB | -| | | | | | | | | | | -| Realm_SelectRange | SelectRange,Realm | 1000 | 1,573.357 us | 450.8583 us | 117.0865 us | 46.8750 | 44.9219 | - | 295.97 KB | -| | | | | | | | | | | -| ReindexerNet_SelectRange | SelectRange,ReindexerNet | 1000 | 106,644.072 us | 12,920.3608 us | 3,355.3767 us | 6800.0000 | 3600.0000 | 600.0000 | 38196.92 KB | -| | | | | | | | | | | -| ReindexerNetSpanJson_SelectRange | SelectRange,ReindexerNetSpanJson | 1000 | 69,709.810 us | 7,478.6567 us | 1,942.1834 us | 4125.0000 | 2375.0000 | 625.0000 | 21593.97 KB | -| | | | | | | | | | | -| ReindexerNetSql_SelectRange | SelectRange,ReindexerNetSql | 1000 | 102,760.600 us | 6,674.4567 us | 1,733.3352 us | 6800.0000 | 3600.0000 | 600.0000 | 38196.14 KB | - -> #### Array Column Filtering(Single Item IN) -> ``` -> WHERE N IN Integer_Array -> WHERE 'N' IN String_Array -> ``` -> | Method | Categories | N | Mean | Allocated | Error | StdDev | Median | Gen0 | Gen1 | Gen2 | -> |-------------------------------------------- |-------------------------------------------- |----- |--------------:|-------------:|--------------:|--------------:|--------------:|-----------:|-----------:|----------:| -| Cachalot_SelectArraySingle | SelectArraySingle,Cachalot | 1000 | 424.875 us | 32.1625 us | 8.3525 us | 9.2773 | 4.3945 | - | 58.58 KB | -| | | | | | | | | | | -| CachalotCompressed_SelectArraySingle | SelectArraySingle,CachalotCompressed | 1000 | 422.408 us | 3.3954 us | 0.8818 us | 9.2773 | 4.3945 | - | 58.8 KB | -| | | | | | | | | | | -| CachalotMemory_SelectArraySingle | SelectArraySingle,CachalotMemory | 1000 | 437.087 us | 15.0301 us | 2.3259 us | 9.2773 | 4.3945 | - | 58.58 KB | -| | | | | | | | | | | -| LiteDb_SelectArraySingle | SelectArraySingle,LiteDb | 1000 | 115.684 us | 2.0888 us | 0.5425 us | 17.7002 | 0.7324 | - | 108.7 KB | -| | | | | | | | | | | -| LiteDbMemory_SelectArraySingle | SelectArraySingle,LiteDbMemory | 1000 | 110.742 us | 2.0057 us | 0.5209 us | 15.9912 | 0.4883 | - | 98 KB | -| | | | | | | | | | | -| Realm_SelectArraySingle | SelectArraySingle,Realm | 1000 | 25,987.981 us | 450.1629 us | 116.9059 us | - | - | - | 3.16 KB | -| | | | | | | | | | | -| ReindexerNet_SelectArraySingle | SelectArraySingle,ReindexerNet | 1000 | 7.537 us | 0.2348 us | 0.0363 us | 0.2670 | 0.0076 | 0.0076 | 1.74 KB | -| | | | | | | | | | | -| ReindexerNetSpanJson_SelectArraySingle | SelectArraySingle,ReindexerNetSpanJson | 1000 | 7.823 us | 0.7236 us | 0.1879 us | 0.2594 | - | - | 1.74 KB | -| | | | | | | | | | | -| ReindexerNetSql_SelectArraySingle | SelectArraySingle,ReindexerNetSql | 1000 | 11.432 us | 0.3705 us | 0.0962 us | 0.1678 | - | - | 1.23 KB | - - -> #### Array Column Filtering(Contains, All) -> ``` -> WHERE Integer_Array CONTAINS (1,2,3,....,N) -> WHERE String_Array CONTAINS ('abc', 'def', .... ) -> WHERE Integer_Array ALL (1,2,3,....,N) -> WHERE Integer_Array ALL (1,2,3,....,N) -> ``` -> | Method | Categories | N | Mean | Allocated | Error | StdDev | Median | Gen0 | Gen1 | Gen2 | -> |-------------------------------------------- |-------------------------------------------- |----- |--------------:|-------------:|--------------:|--------------:|--------------:|-----------:|-----------:|----------:| -| Cachalot_SelectArrayMultiple | SelectArrayMultiple,Cachalot | 1000 | 652,729.720 us | 66,142.3826 us | 17,176.9667 us | 58000.0000 | 9000.0000 | - | 392384.68 KB | -| | | | | | | | | | | -| CachalotCompressed_SelectArrayMultiple | SelectArrayMultiple,CachalotCompressed | 1000 | 1,318,515.020 us | 12,199.7214 us | 3,168.2289 us | 129000.0000 | 19000.0000 | - | 825509.77 KB | -| | | | | | | | | | | -| CachalotMemory_SelectArrayMultiple | SelectArrayMultiple,CachalotMemory | 1000 | 648,278.780 us | 31,402.6038 us | 8,155.1565 us | 59000.0000 | 10000.0000 | 1000.0000 | 392384.44 KB | -| | | | | | | | | | | -| LiteDb_SelectArrayMultiple | SelectArrayMultiple,LiteDb | 1000 | 1,221,826.140 us | 12,537.1964 us | 3,255.8701 us | 122000.0000 | 26000.0000 | 1000.0000 | 745676.34 KB | -| | | | | | | | | | | -| LiteDbMemory_SelectArrayMultiple | SelectArrayMultiple,LiteDbMemory | 1000 | 1,216,860.860 us | 31,751.1468 us | 8,245.6720 us | 122000.0000 | 28000.0000 | 1000.0000 | 745677.55 KB | -| | | | | | | | | | | -| Realm_SelectArrayMultiple | SelectArrayMultiple,Realm | 1000 | 480,289.680 us | 10,716.3749 us | 2,783.0085 us | - | - | - | 589.57 KB | -| | | | | | | | | | | -| ReindexerNet_SelectArrayMultiple | SelectArrayMultiple,ReindexerNet | 1000 | 204,457.533 us | 21,413.5619 us | 5,561.0340 us | 13666.6667 | 7333.3333 | 1000.0000 | 78959.62 KB | -| | | | | | | | | | | -| ReindexerNetSpanJson_SelectArrayMultiple | SelectArrayMultiple,ReindexerNetSpanJson | 1000 | 135,851.400 us | 16,115.7813 us | 4,185.2172 us | 8500.0000 | 4750.0000 | 1250.0000 | 44808.57 KB | -| | | | | | | | | | | -| ReindexerNetSql_SelectArrayMultiple | SelectArrayMultiple,ReindexerNetSql | 1000 | 206,962.613 us | 14,102.3376 us | 3,662.3323 us | 13666.6667 | 7333.3333 | 1000.0000 | 78961.6 KB | +| Method | N | Mean | Error | StdDev | Min | Max | Gen0 | Gen1 | Gen2 | Allocated | +|--------------------- |----- |-----------:|----------:|----------:|-----------:|-----------:|-----------:|----------:|----------:|-------------:| +| ReindexerNet | 1000 | 3.772 ms | 0.0608 ms | 0.0569 ms | 3.675 ms | 3.909 ms | 140.6250 | 19.5313 | 3.9063 | 916.25 KB | +| ReindexerNetSpanJson | 1000 | 4.087 ms | 0.0577 ms | 0.0540 ms | 3.963 ms | 4.184 ms | 132.8125 | 15.6250 | - | 880.35 KB | +| ReindexerNetSql | 1000 | 106.503 ms | 2.1241 ms | 4.9651 ms | 97.281 ms | 117.538 ms | 7333.3333 | 4000.0000 | 1000.0000 | 39063.15 KB | +| CachalotMemory | 1000 | 206.542 ms | 2.2395 ms | 2.0949 ms | 201.875 ms | 209.943 ms | 30000.0000 | 5000.0000 | - | 187603.98 KB | +| Cachalot | 1000 | 208.367 ms | 1.5068 ms | 1.2582 ms | 206.252 ms | 210.109 ms | 30333.3333 | 4000.0000 | - | 187603.27 KB | +| LiteDbMemory | 1000 | 210.757 ms | 3.0700 ms | 2.8717 ms | 206.822 ms | 216.022 ms | 36000.0000 | 6000.0000 | 1000.0000 | 215746.8 KB | +| LiteDb | 1000 | 212.069 ms | 3.6957 ms | 6.5691 ms | 197.385 ms | 225.860 ms | 36000.0000 | 6000.0000 | 1000.0000 | 215787.06 KB | +| Realm | 1000 | 233.696 ms | 3.1694 ms | 2.9647 ms | 228.573 ms | 239.720 ms | 4333.3333 | 2666.6667 | 666.6667 | 23001.83 KB | +| CachalotCompressed | 1000 | 251.389 ms | 1.9690 ms | 1.7455 ms | 248.861 ms | 254.736 ms | 35500.0000 | 4500.0000 | - | 220243.17 KB | +``` + +#### Multiple Primary Key(Guid) at once +``` +WHERE Id IN (id_1,id_2,id_3,...,id_N) + +| Method | N | Mean | Error | StdDev | Median | Min | Max | Gen0 | Gen1 | Gen2 | Allocated | +|--------------------- |----- |-------------:|------------:|------------:|-------------:|-------------:|-------------:|-----------:|----------:|----------:|-------------:| +| ReindexerNet | 1000 | 280.5 us | 1.20 us | 1.06 us | 280.6 us | 278.9 us | 282.8 us | 6.3477 | - | - | 40.91 KB | +| ReindexerNetSpanJson | 1000 | 285.1 us | 5.63 us | 8.07 us | 280.7 us | 278.0 us | 302.9 us | 6.3477 | - | - | 39.96 KB | +| ReindexerNetSql | 1000 | 106,486.3 us | 2,129.47 us | 3,438.69 us | 105,796.1 us | 98,176.9 us | 113,709.0 us | 8400.0000 | 4800.0000 | 1200.0000 | 44307.83 KB | +| LiteDb | 1000 | 185,631.6 us | 2,317.46 us | 2,167.75 us | 186,081.7 us | 181,218.5 us | 188,559.2 us | 32500.0000 | 5000.0000 | 500.0000 | 197863.84 KB | +| LiteDbMemory | 1000 | 201,107.3 us | 3,993.21 us | 9,870.23 us | 196,599.5 us | 187,358.6 us | 226,621.6 us | 33000.0000 | 6000.0000 | 1000.0000 | 197536.98 KB | +| CachalotMemory | 1000 | 208,060.8 us | 1,865.30 us | 1,653.54 us | 207,961.0 us | 205,400.9 us | 211,116.9 us | 28666.6667 | 4333.3333 | - | 193851.34 KB | +| Cachalot | 1000 | 227,040.3 us | 3,343.96 us | 3,127.94 us | 226,465.9 us | 223,353.5 us | 235,004.1 us | 28000.0000 | 5000.0000 | - | 193853.33 KB | +| Realm | 1000 | 243,557.0 us | 1,778.28 us | 1,576.39 us | 243,424.7 us | 241,051.8 us | 246,540.9 us | 4333.3333 | 2333.3333 | 666.6667 | 23013.14 KB | +| CachalotCompressed | 1000 | 251,255.9 us | 1,220.44 us | 952.84 us | 251,492.5 us | 249,395.1 us | 252,855.4 us | 35000.0000 | 5000.0000 | - | 223683.38 KB | +``` + + +#### Single Hash in a loop +``` +foreach (value in values) +{ + WHERE StringProperty = value +} + +| Method | N | Mean | Error | StdDev | Min | Max | Gen0 | Gen1 | Gen2 | Allocated | +|--------------------- |----- |----------:|---------:|---------:|----------:|----------:|-----------:|----------:|----------:|----------:| +| ReindexerNetSpanJson | 1000 | 76.73 ms | 1.531 ms | 2.195 ms | 72.30 ms | 80.41 ms | 4285.7143 | 2428.5714 | 714.2857 | 22.02 MB | +| ReindexerNet | 1000 | 99.67 ms | 1.593 ms | 2.181 ms | 96.82 ms | 104.71 ms | 7333.3333 | 4000.0000 | 1000.0000 | 38.23 MB | +| ReindexerNetSql | 1000 | 109.15 ms | 2.178 ms | 4.499 ms | 99.27 ms | 118.38 ms | 7333.3333 | 4000.0000 | 1000.0000 | 38.14 MB | +| LiteDb | 1000 | 219.26 ms | 4.327 ms | 6.476 ms | 211.27 ms | 230.07 ms | 36000.0000 | 6000.0000 | 1000.0000 | 211.92 MB | +| LiteDbMemory | 1000 | 225.70 ms | 4.345 ms | 6.764 ms | 212.11 ms | 236.48 ms | 36000.0000 | 6000.0000 | 1000.0000 | 213.21 MB | +| Realm | 1000 | 371.69 ms | 3.742 ms | 3.500 ms | 366.67 ms | 378.11 ms | 5000.0000 | 2000.0000 | - | 30.75 MB | +| CachalotMemory | 1000 | 395.27 ms | 3.091 ms | 2.891 ms | 391.74 ms | 402.18 ms | 33000.0000 | 5000.0000 | - | 197.73 MB | +| Cachalot | 1000 | 396.81 ms | 3.622 ms | 3.211 ms | 392.42 ms | 401.60 ms | 33000.0000 | 4000.0000 | - | 198.07 MB | +| CachalotCompressed | 1000 | 400.09 ms | 5.197 ms | 4.607 ms | 394.77 ms | 410.26 ms | 33000.0000 | 4000.0000 | - | 198.08 MB | +``` + +#### Single Hash in a Parallel loop +``` +Parallel.Foreach(values, value => +{ + WHERE StringProperty = value +}); + +| Method | N | Mean | Error | StdDev | Min | Max | Gen0 | Gen1 | Gen2 | Allocated | +|--------------------- |----- |----------:|---------:|----------:|----------:|----------:|-----------:|----------:|---------:|----------:| +| ReindexerNetSpanJson | 1000 | 40.75 ms | 0.808 ms | 1.415 ms | 38.07 ms | 43.88 ms | 4076.9231 | 2076.9231 | 153.8462 | 22.12 MB | +| ReindexerNet | 1000 | 54.56 ms | 1.067 ms | 1.979 ms | 49.93 ms | 58.52 ms | 7300.0000 | 3200.0000 | 300.0000 | 38.34 MB | +| ReindexerNetSql | 1000 | 58.70 ms | 1.157 ms | 1.421 ms | 54.43 ms | 60.77 ms | 7300.0000 | 3200.0000 | 300.0000 | 38.15 MB | +| Realm | 1000 | 98.26 ms | 1.563 ms | 1.462 ms | 94.69 ms | 100.04 ms | 6600.0000 | 3400.0000 | 400.0000 | 36.01 MB | +| CachalotMemory | 1000 | 200.90 ms | 3.963 ms | 9.342 ms | 183.63 ms | 228.97 ms | 35000.0000 | 7000.0000 | - | 197.77 MB | +| CachalotCompressed | 1000 | 209.76 ms | 4.179 ms | 10.328 ms | 189.66 ms | 228.95 ms | 35000.0000 | 7500.0000 | - | 198.15 MB | +| Cachalot | 1000 | 214.24 ms | 4.240 ms | 9.570 ms | 192.58 ms | 230.81 ms | 35000.0000 | 8000.0000 | - | 198.14 MB | +| LiteDbMemory | 1000 | 225.41 ms | 4.471 ms | 10.363 ms | 206.92 ms | 247.77 ms | 37000.0000 | 8500.0000 | 500.0000 | 211.58 MB | +| LiteDb | 1000 | 242.61 ms | 5.148 ms | 15.178 ms | 206.78 ms | 265.36 ms | 37000.0000 | 8333.3333 | 333.3333 | 210.86 MB | +``` + +#### Multiple Hash at once +``` +WHERE StringProperty IN ('abc', 'def', .... ) + +| Method | N | Mean | Error | StdDev | Min | Max | Gen0 | Gen1 | Gen2 | Allocated | +|--------------------- |----- |----------:|---------:|---------:|----------:|----------:|-----------:|-----------:|----------:|----------:| +| ReindexerNetSpanJson | 1000 | 76.59 ms | 1.484 ms | 1.388 ms | 75.02 ms | 80.02 ms | 5285.7143 | 3000.0000 | 857.1429 | 26.94 MB | +| ReindexerNetSql | 1000 | 108.02 ms | 2.122 ms | 3.486 ms | 101.71 ms | 114.66 ms | 8400.0000 | 4800.0000 | 1200.0000 | 43.22 MB | +| ReindexerNet | 1000 | 110.42 ms | 2.192 ms | 3.722 ms | 104.36 ms | 116.44 ms | 8400.0000 | 4800.0000 | 1200.0000 | 43.16 MB | +| LiteDbMemory | 1000 | 193.75 ms | 2.587 ms | 2.420 ms | 191.26 ms | 199.34 ms | 33000.0000 | 6000.0000 | 1000.0000 | 193.29 MB | +| LiteDb | 1000 | 197.36 ms | 2.779 ms | 3.985 ms | 192.03 ms | 208.02 ms | 33000.0000 | 6000.0000 | 1000.0000 | 193.13 MB | +| CachalotMemory | 1000 | 204.27 ms | 2.363 ms | 2.210 ms | 200.86 ms | 208.97 ms | 28000.0000 | 5000.0000 | - | 189.21 MB | +| Cachalot | 1000 | 212.28 ms | 3.811 ms | 5.217 ms | 205.63 ms | 226.48 ms | 28000.0000 | 5000.0000 | - | 189.21 MB | +| Realm | 1000 | 243.08 ms | 3.056 ms | 2.859 ms | 238.90 ms | 248.40 ms | 4333.3333 | 2333.3333 | 666.6667 | 22.42 MB | +| CachalotCompressed | 1000 | 454.42 ms | 3.513 ms | 2.743 ms | 450.31 ms | 458.75 ms | 64000.0000 | 10000.0000 | - | 399.26 MB | +``` + +#### Range Filtering +``` +WHERE IntProperty < i +WHERE IntProperty >= i + +| Method | N | Mean | Error | StdDev | Min | Max | Gen0 | Gen1 | Gen2 | Allocated | +|--------------------- |----- |------------:|----------:|----------:|------------:|------------:|------------:|-----------:|----------:|----------:| +| ReindexerNetSpanJson | 1000 | 68.98 ms | 1.234 ms | 1.884 ms | 65.60 ms | 72.79 ms | 4250.0000 | 2500.0000 | 750.0000 | 21.18 MB | +| ReindexerNet | 1000 | 91.01 ms | 1.781 ms | 2.316 ms | 86.20 ms | 94.46 ms | 7166.6667 | 4000.0000 | 1000.0000 | 37.39 MB | +| ReindexerNetSql | 1000 | 94.10 ms | 0.945 ms | 0.838 ms | 93.12 ms | 96.12 ms | 7166.6667 | 4000.0000 | 1000.0000 | 37.39 MB | +| LiteDbMemory | 1000 | 166.43 ms | 3.151 ms | 4.207 ms | 162.31 ms | 177.94 ms | 29000.0000 | 4000.0000 | - | 178.44 MB | +| LiteDb | 1000 | 183.24 ms | 3.307 ms | 3.094 ms | 178.14 ms | 186.81 ms | 30500.0000 | 5000.0000 | 1000.0000 | 178.44 MB | +| CachalotMemory | 1000 | 206.58 ms | 1.655 ms | 1.548 ms | 203.80 ms | 208.95 ms | 28000.0000 | 5000.0000 | - | 181.15 MB | +| Realm | 1000 | 228.51 ms | 4.063 ms | 3.800 ms | 223.23 ms | 233.08 ms | 4000.0000 | 2000.0000 | 500.0000 | 22.25 MB | +| Cachalot | 1000 | 2,125.57 ms | 41.912 ms | 86.555 ms | 1,989.19 ms | 2,358.20 ms | 276000.0000 | 40000.0000 | - | 1742.7 MB | +| CachalotCompressed | 1000 | 2,267.47 ms | 35.480 ms | 59.279 ms | 2,215.05 ms | 2,421.11 ms | 310000.0000 | 45000.0000 | - | 1946.9 MB | +``` + +#### Array Column Filtering(Single Item IN) +``` +WHERE N IN Integer_Array +WHERE 'N' IN String_Array + +| Method | N | Mean | Error | StdDev | Min | Max | Gen0 | Gen1 | Gen2 | Allocated | +|--------------------- |----- |--------------:|------------:|------------:|--------------:|--------------:|--------:|-------:|-------:|----------:| +| ReindexerNet | 1000 | 7.426 μs | 0.0897 μs | 0.0796 μs | 7.340 μs | 7.562 μs | 0.2975 | 0.0076 | 0.0076 | 1.95 KB | +| ReindexerNetSpanJson | 1000 | 7.459 μs | 0.1014 μs | 0.0847 μs | 7.282 μs | 7.571 μs | 0.2975 | 0.0076 | 0.0076 | 1.95 KB | +| ReindexerNetSql | 1000 | 11.941 μs | 0.2384 μs | 0.3183 μs | 11.546 μs | 12.623 μs | 0.1984 | - | - | 1.44 KB | +| LiteDb | 1000 | 91.456 μs | 1.8236 μs | 2.6731 μs | 88.298 μs | 97.401 μs | 16.1133 | 0.4883 | - | 98.88 KB | +| LiteDbMemory | 1000 | 95.710 μs | 1.8860 μs | 3.0987 μs | 90.051 μs | 101.158 μs | 17.0898 | 0.4883 | - | 107.25 KB | +| CachalotMemory | 1000 | 375.573 μs | 2.6608 μs | 2.2219 μs | 372.386 μs | 379.561 μs | 8.7891 | 3.9063 | - | 57.74 KB | +| Cachalot | 1000 | 377.761 μs | 4.7644 μs | 3.9785 μs | 372.897 μs | 387.478 μs | 9.2773 | 4.3945 | - | 57.45 KB | +| CachalotCompressed | 1000 | 383.748 μs | 7.3570 μs | 8.1773 μs | 374.281 μs | 405.420 μs | 9.2773 | 4.3945 | - | 57.5 KB | +| Realm | 1000 | 26,092.364 μs | 350.8481 μs | 311.0177 μs | 25,577.228 μs | 26,581.688 μs | - | - | - | 3.35 KB | +``` +#### Array Column Filtering(Contains, All) +``` +WHERE Integer_Array CONTAINS (1,2,3,....,N) +WHERE String_Array CONTAINS ('abc', 'def', .... ) +WHERE Integer_Array ALL (1,2,3,....,N) +WHERE Integer_Array ALL (1,2,3,....,N) + +| Method | N | Mean | Error | StdDev | Median | Min | Max | Gen0 | Gen1 | Gen2 | Allocated | +|--------------------- |----- |-----------:|---------:|---------:|---------:|---------:|-----------:|------------:|-----------:|----------:|----------:| +| ReindexerNetSpanJson | 1000 | 134.1 ms | 2.59 ms | 2.66 ms | 133.6 ms | 129.9 ms | 139.3 ms | 8250.0000 | 4500.0000 | 1000.0000 | 43.95 MB | +| ReindexerNet | 1000 | 178.5 ms | 3.56 ms | 6.68 ms | 181.8 ms | 166.6 ms | 187.0 ms | 13000.0000 | 7000.0000 | 1000.0000 | 77.3 MB | +| ReindexerNetSql | 1000 | 186.7 ms | 2.89 ms | 2.71 ms | 186.9 ms | 182.4 ms | 190.5 ms | 13500.0000 | 7000.0000 | 1000.0000 | 77.3 MB | +| Cachalot | 1000 | 464.7 ms | 9.26 ms | 9.10 ms | 462.2 ms | 451.8 ms | 489.3 ms | 58000.0000 | 9000.0000 | - | 383.3 MB | +| CachalotMemory | 1000 | 475.2 ms | 5.93 ms | 4.63 ms | 475.5 ms | 468.9 ms | 481.5 ms | 58000.0000 | 9000.0000 | - | 383.3 MB | +| LiteDbMemory | 1000 | 832.3 ms | 8.96 ms | 7.48 ms | 834.3 ms | 816.2 ms | 841.8 ms | 122000.0000 | 20000.0000 | 1000.0000 | 728.39 MB | +| LiteDb | 1000 | 868.9 ms | 7.69 ms | 6.82 ms | 870.3 ms | 855.7 ms | 878.2 ms | 123000.0000 | 29000.0000 | 2000.0000 | 728.39 MB | +| Realm | 1000 | 945.7 ms | 9.82 ms | 9.19 ms | 946.8 ms | 923.2 ms | 958.6 ms | 7000.0000 | 3000.0000 | - | 44.5 MB | +| CachalotCompressed | 1000 | 1,000.4 ms | 11.46 ms | 10.16 ms | 998.4 ms | 982.3 ms | 1,021.5 ms | 129000.0000 | 19000.0000 | - | 806.47 MB | +``` ## To Do List diff --git a/Tests/ReindexerNet.EmbeddedBenchmarks/BenchmarkEntity.cs b/Tests/ReindexerNet.EmbeddedBenchmarks/BenchmarkEntity.cs index cdc4b94..9fceb83 100644 --- a/Tests/ReindexerNet.EmbeddedBenchmarks/BenchmarkEntity.cs +++ b/Tests/ReindexerNet.EmbeddedBenchmarks/BenchmarkEntity.cs @@ -2,6 +2,7 @@ using Realms; using Realms.Schema; using Realms.Weaving; +using System.Linq.Expressions; using System.Runtime.Serialization; using System.Text.Json.Serialization; @@ -32,49 +33,65 @@ public sealed class BenchmarkEntity [ServerSideValue(Client.Core.IndexType.Dictionary)] [DataMember(Order = 5)] - public string[] StrArray { get; set; } + public string[] StrArray { get; set; } + + public BenchmarkEntity PreventLazy() + => new BenchmarkEntity + { + Id = Id, + IntArray = IntArray.ToArray(), + StrArray = StrArray.ToArray(), + CreateDate = CreateDate, + IntProperty = IntProperty, + StringProperty = StringProperty, + }; } -public sealed partial class BenchmarkRealmEntity: IRealmObject -{ - [PrimaryKey] +public sealed partial class BenchmarkRealmEntity : IRealmObject +{ + [PrimaryKey] public Guid Id { get; set; } - [Indexed(Realms.IndexType.General)] + [Indexed(Realms.IndexType.General)] public int? IntProperty { get; set; } - [Indexed(Realms.IndexType.General)] + [Indexed(Realms.IndexType.General)] public string? StringProperty { get; set; } - [Indexed(Realms.IndexType.General)] + [Indexed(Realms.IndexType.General)] public DateTimeOffset? CreateDate { get; set; } - + public ISet IntArray { get; } public ISet StrArray { get; } public static implicit operator BenchmarkEntity(BenchmarkRealmEntity realmEntity) { - return new BenchmarkEntity{ + return new BenchmarkEntity + { Id = realmEntity.Id, IntProperty = realmEntity.IntProperty, StringProperty = realmEntity.StringProperty, CreateDate = realmEntity.CreateDate, - IntArray = realmEntity.IntArray.ToArray(), - StrArray = realmEntity.StrArray.ToArray() - }; + IntArray = [.. realmEntity.IntArray], + StrArray = [.. realmEntity.StrArray] + }; } public static implicit operator BenchmarkRealmEntity(BenchmarkEntity realmEntity) { - var entity = new BenchmarkRealmEntity{ + var entity = new BenchmarkRealmEntity + { Id = realmEntity.Id, IntProperty = realmEntity.IntProperty, StringProperty = realmEntity.StringProperty, CreateDate = realmEntity.CreateDate, - }; + }; entity.IntArray.UnionWith(realmEntity.IntArray); entity.StrArray.UnionWith(realmEntity.StrArray); return entity; } + + public BenchmarkEntity PreventLazy() + => (BenchmarkEntity)this; } \ No newline at end of file diff --git a/Tests/ReindexerNet.EmbeddedBenchmarks/BenchmarkHelper.cs b/Tests/ReindexerNet.EmbeddedBenchmarks/BenchmarkHelper.cs new file mode 100644 index 0000000..49263e2 --- /dev/null +++ b/Tests/ReindexerNet.EmbeddedBenchmarks/BenchmarkHelper.cs @@ -0,0 +1,27 @@ +using ReindexerNet; +using ReindexerNetBenchmark.EmbeddedBenchmarks; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ReindexerNetBenchmark; + +internal static class BenchmarkHelper +{ + public static object CaptureResult(this BenchmarkEntity entity) + => entity.PreventLazy(); + + public static object CaptureResult(this BenchmarkRealmEntity entity) + => entity.PreventLazy(); + + public static object CaptureResult(this IEnumerable entites) + => entites.Select(e => e.PreventLazy()).ToList(); + + public static object CaptureResult(this IEnumerable entites) + => entites.Select(e => e.PreventLazy()).ToList(); + + public static object CaptureResult(this QueryItemsOf result) + => result.Items.Select(e => e.PreventLazy()).ToList(); +} diff --git a/Tests/ReindexerNet.EmbeddedBenchmarks/InsertBenchmark.cs b/Tests/ReindexerNet.EmbeddedBenchmarks/InsertBenchmark.cs index 3e2da94..2dbf055 100644 --- a/Tests/ReindexerNet.EmbeddedBenchmarks/InsertBenchmark.cs +++ b/Tests/ReindexerNet.EmbeddedBenchmarks/InsertBenchmark.cs @@ -17,20 +17,20 @@ namespace ReindexerNetBenchmark.EmbeddedBenchmarks; //[Config(typeof(AntiVirusFriendlyConfig))] [SimpleJob(launchCount: 0, warmupCount: 0, iterationCount: 1)] [MemoryDiagnoser()] -[CategoriesColumn] [CustomCategoryDiscoverer] -[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory, BenchmarkLogicalGroupRule.ByParams)] +[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByParams, BenchmarkLogicalGroupRule.ByCategory)] [PlainExporter] +[Orderer(BenchmarkDotNet.Order.SummaryOrderPolicy.FastestToSlowest)] public class InsertBenchmark { - private class CustomCategoryDiscoverer : DefaultCategoryDiscoverer + protected class CustomCategoryDiscoverer : DefaultCategoryDiscoverer { public override string[] GetCategories(MethodInfo method) => method.Name.Split('_').Reverse().ToArray(); } [AttributeUsage(AttributeTargets.Class)] - private class CustomCategoryDiscovererAttribute : Attribute, IConfigSource + protected class CustomCategoryDiscovererAttribute : Attribute, IConfigSource { public CustomCategoryDiscovererAttribute() { @@ -41,23 +41,23 @@ public CustomCategoryDiscovererAttribute() public IConfig Config { get; } } - private string _dataPath; - private BenchmarkEntity[] _data; + protected string _dataPath; + protected BenchmarkEntity[] _data; [Params(10_000, 100_000)] public int N; - private ReindexerEmbedded? _rxClient; - private ReindexerEmbedded? _rxClientDense; - private LiteDatabase _liteDb; - private LiteDatabase _liteDbMemory; - private ILiteCollection _liteColl; - private ILiteCollection _liteCollMemory; - private Server.Server _caServer; - private Connector? _caConnector; - private Connector? _caConnectorCompressed; - private Connector _caMemoryConnector; - private Realm _realm; + protected ReindexerEmbedded? _rxClient; + protected ReindexerEmbedded? _rxClientDense; + protected LiteDatabase _liteDb; + protected LiteDatabase _liteDbMemory; + protected ILiteCollection _liteColl; + protected ILiteCollection _liteCollMemory; + protected Server.Server _caServer; + protected Connector? _caConnector; + protected Connector? _caConnectorCompressed; + protected Connector _caMemoryConnector; + protected Realm _realm; public void Setup() { @@ -71,8 +71,8 @@ public void Setup() IntProperty = i, StringProperty = "ÇŞĞÜÖİöçşğüı", CreateDate = DateTime.UtcNow, - IntArray = new[] { 123, 124, 456, 456, 6777, 3123, 123123, 333 }, - StrArray = new[] { "", "Abc", "Def", "FFF", "GGG", "HHH", "HGFDF", "asd" } + IntArray = [ 123, 124, 456, 456, 6777, 3123, 123123, 333 ], + StrArray = [ "", "Abc", "Def", "FFF", "GGG", "HHH", "HGFDF", "asd" ] }; } Console.WriteLine(_dataPath); @@ -83,19 +83,8 @@ public void Cleanup() Directory.Delete(_dataPath, true); } - [IterationSetup(Targets = new[] { nameof(ReindexerNet_Insert) })] - public void ReindexerNetInsertSetup() - { - ReindexerNetSetup(); - } - [IterationSetup(Targets = new[] { nameof(ReindexerNet_Upsert) })] - public void ReindexerNetUpsertSetup() - { - ReindexerNetSetup(); - ReindexerNet_Insert(); - } - - public void ReindexerNetSetup() + [IterationSetup(Targets = new[] { nameof(ReindexerNet) })] + public virtual void ReindexerNetSetup() { Setup(); var rxDbPath = Path.Combine(_dataPath, "ReindexerEmbedded"); @@ -114,26 +103,15 @@ public void ReindexerNetSetup() } - [IterationCleanup(Targets = new[] { nameof(ReindexerNet_Insert), nameof(ReindexerNet_Upsert) })] + [IterationCleanup(Targets = new[] { nameof(ReindexerNet) })] public void ReindexerNetClean() { _rxClient!.Dispose(); Cleanup(); } - [IterationSetup(Targets = new[] { nameof(ReindexerNetDense_Insert) })] - public void ReindexerNetDenseInsertSetup() - { - ReindexerNetDenseSetup(); - } - [IterationSetup(Targets = new[] { nameof(ReindexerNetDense_Upsert) })] - public void ReindexerNetDenseUpsertSetup() - { - ReindexerNetDenseSetup(); - ReindexerNetDense_Insert(); - } - - public void ReindexerNetDenseSetup() + [IterationSetup(Targets = new[] { nameof(ReindexerNetDense) })] + public virtual void ReindexerNetDenseSetup() { Setup(); var rxDensedbPath = Path.Combine(_dataPath, "ReindexerEmbeddedDense"); @@ -151,26 +129,15 @@ public void ReindexerNetDenseSetup() _rxClientDense.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.StrArray), IndexType = IndexType.Hash, FieldType = FieldType.String, IsDense = true, IsArray = true }); } - [IterationCleanup(Targets = new[] { nameof(ReindexerNetDense_Insert), nameof(ReindexerNetDense_Upsert) })] + [IterationCleanup(Targets = new[] { nameof(ReindexerNetDense) })] public void ReindexerNetDenseClean() { _rxClientDense!.Dispose(); Cleanup(); } - [IterationSetup(Targets = new[] { nameof(Cachalot_Insert) })] - public void CachalotInsertSetup() - { - CachalotSetup(); - } - [IterationSetup(Targets = new[] { nameof(Cachalot_Upsert) })] - public void CachalotUpsertSetup() - { - CachalotSetup(); - Cachalot_Insert(); - } - - public void CachalotSetup() + [IterationSetup(Targets = new[] { nameof(Cachalot) })] + public virtual void CachalotSetup() { Setup(); Directory.CreateDirectory(Path.Combine(_dataPath, "Cachalot")); @@ -181,7 +148,7 @@ public void CachalotSetup() _caConnector.GetCollectionSchema("BenchmarkEntity").UseCompression = false; } - [IterationCleanup(Targets = new[] { nameof(Cachalot_Insert), nameof(Cachalot_Upsert) })] + [IterationCleanup(Targets = new[] { nameof(Cachalot) })] public void CachalotClean() { _caConnector!.Dispose(); @@ -189,19 +156,8 @@ public void CachalotClean() Cleanup(); } - [IterationSetup(Targets = new[] { nameof(CachalotCompressed_Insert) })] - public void CachalotCompressedInsertSetup() - { - CachalotCompressedSetup(); - } - [IterationSetup(Targets = new[] { nameof(CachalotCompressed_Upsert) })] - public void CachalotCompressedUpsertSetup() - { - CachalotCompressedSetup(); - CachalotCompressed_Insert(); - } - - public void CachalotCompressedSetup() + [IterationSetup(Targets = new[] { nameof(CachalotCompressed) })] + public virtual void CachalotCompressedSetup() { Setup(); Directory.CreateDirectory(Path.Combine(_dataPath, "CachalotCompressed")); @@ -212,7 +168,7 @@ public void CachalotCompressedSetup() _caConnectorCompressed.GetCollectionSchema("BenchmarkEntity").UseCompression = true; } - [IterationCleanup(Targets = new[] { nameof(CachalotCompressed_Insert), nameof(CachalotCompressed_Upsert) })] + [IterationCleanup(Targets = new[] { nameof(CachalotCompressed) })] public void CachalotCompressedClean() { _caConnectorCompressed!.Dispose(); @@ -220,19 +176,8 @@ public void CachalotCompressedClean() Cleanup(); } - [IterationSetup(Targets = new[] { nameof(CachalotOnlyMemory_Insert) })] - public void CachalotOnlyMemoryInsertSetup() - { - CachalotOnlyMemorySetup(); - } - [IterationSetup(Targets = new[] { nameof(CachalotOnlyMemory_Upsert) })] - public void CachalotOnlyMemoryUpsertSetup() - { - CachalotOnlyMemorySetup(); - CachalotOnlyMemory_Insert(); - } - - public void CachalotOnlyMemorySetup() + [IterationSetup(Targets = new[] { nameof(CachalotOnlyMemory) })] + public virtual void CachalotOnlyMemorySetup() { Setup(); _caServer = new Server.Server(new NodeConfig { DataPath = Path.Combine(_dataPath, "CachalotOnlyMemory"), IsPersistent = false, ClusterName = "CachalotOnlyMemory" }); @@ -240,7 +185,7 @@ public void CachalotOnlyMemorySetup() _caMemoryConnector.DeclareCollection("BenchmarkEntity"); } - [IterationCleanup(Targets = new[] { nameof(CachalotOnlyMemory_Insert), nameof(CachalotOnlyMemory_Upsert) })] + [IterationCleanup(Targets = new[] { nameof(CachalotOnlyMemory) })] public void CachalotOnlyMemoryClean() { _caMemoryConnector!.Dispose(); @@ -248,18 +193,8 @@ public void CachalotOnlyMemoryClean() Cleanup(); } - [IterationSetup(Targets = new[] { nameof(LiteDb_Insert) })] - public void LiteDbInsertSetup() - { - LiteDbSetup(); - } - [IterationSetup(Targets = new[] { nameof(LiteDb_Upsert) })] - public void LiteDbUpsertSetup() - { - LiteDbSetup(); - LiteDb_Insert(); - } - public void LiteDbSetup() + [IterationSetup(Targets = new[] { nameof(LiteDb) })] + public virtual void LiteDbSetup() { Setup(); _liteDb = new LiteDatabase(Path.Combine(_dataPath, "LiteDB")); @@ -272,25 +207,15 @@ public void LiteDbSetup() _liteColl.EnsureIndex(e => e.StrArray); } - [IterationCleanup(Targets = new[] { nameof(LiteDb_Insert), nameof(LiteDb_Upsert) })] + [IterationCleanup(Targets = new[] { nameof(LiteDb) })] public void LiteDbClean() { _liteDb!.Dispose(); Cleanup(); } - [IterationSetup(Targets = new[] { nameof(LiteDbMemory_Insert) })] - public void LiteDbMemoryInsertSetup() - { - LiteDbMemorySetup(); - } - [IterationSetup(Targets = new[] { nameof(LiteDbMemory_Upsert) })] - public void LiteDbMemoryUpsertSetup() - { - LiteDbMemorySetup(); - LiteDbMemory_Insert(); - } - public void LiteDbMemorySetup() + [IterationSetup(Targets = new[] { nameof(LiteDbMemory) })] + public virtual void LiteDbMemorySetup() { Setup(); _liteDbMemory = new LiteDatabase(":memory:"); @@ -303,159 +228,79 @@ public void LiteDbMemorySetup() _liteCollMemory.EnsureIndex(e => e.StrArray); } - [IterationCleanup(Targets = new[] { nameof(LiteDbMemory_Insert), nameof(LiteDbMemory_Upsert) })] + [IterationCleanup(Targets = new[] { nameof(LiteDbMemory) })] public void LiteDbMemoryClean() { _liteDbMemory!.Dispose(); Cleanup(); } - [IterationSetup(Targets = new[] { nameof(Realm_Insert) })] - public void RealmInsertSetup() - { - RealmSetup(); - } - [IterationSetup(Targets = new[] { nameof(Realm_Upsert) })] - public void RealmUpsertSetup() - { - RealmSetup(); - Realm_Insert(); - } - public void RealmSetup() + [IterationSetup(Targets = new[] { nameof(Realm) })] + public virtual void RealmSetup() { Setup(); - _realm = Realm.GetInstance(new RealmConfiguration(Path.Combine(_dataPath, "Realm"))); + _realm = Realms.Realm.GetInstance(new RealmConfiguration(Path.Combine(_dataPath, "Realm"))); } - [IterationCleanup(Targets = new[] { nameof(Realm_Insert), nameof(Realm_Upsert) })] + [IterationCleanup(Targets = new[] { nameof(Realm) })] public void RealmClean() { _realm.Dispose(); - Realm.DeleteRealm(new RealmConfiguration(Path.Combine(_dataPath, "Realm"))); + Realms.Realm.DeleteRealm(new RealmConfiguration(Path.Combine(_dataPath, "Realm"))); Cleanup(); } - [BenchmarkCategory("Insert")] [Benchmark] - public void ReindexerNet_Insert() + public virtual void ReindexerNet() { _rxClient!.Insert("Entities", _data); } - [BenchmarkCategory("Insert")] [Benchmark] - public void ReindexerNetDense_Insert() + public virtual void ReindexerNetDense() { _rxClientDense!.Insert("Entities", _data); } - [BenchmarkCategory("Insert")] [Benchmark] - public void Cachalot_Insert() + public virtual void Cachalot() { var entities = _caConnector!.DataSource("BenchmarkEntity"); entities.PutMany(_data); } - [BenchmarkCategory("Insert")] [Benchmark] - public void CachalotCompressed_Insert() + public virtual void CachalotCompressed() { var entities = _caConnectorCompressed!.DataSource("BenchmarkEntity"); entities.PutMany(_data); } - [BenchmarkCategory("Insert")] [Benchmark] - public void CachalotOnlyMemory_Insert() + public virtual void CachalotOnlyMemory() { var entities = _caMemoryConnector!.DataSource("BenchmarkEntity"); entities.PutMany(_data); } - [BenchmarkCategory("Insert")] [Benchmark] - public void LiteDb_Insert() + public virtual void LiteDb() { _liteColl.InsertBulk(_data); } - [BenchmarkCategory("Insert")] [Benchmark] - public void LiteDbMemory_Insert() + public virtual void LiteDbMemory() { _liteCollMemory.InsertBulk(_data); } - [BenchmarkCategory("Insert")] [Benchmark] - public void Realm_Insert() + public virtual void Realm() { _realm.Write(() => { _realm.Add(_data.Select(e => (BenchmarkRealmEntity)e), update: false); }); } - - [BenchmarkCategory("Upsert")] - [Benchmark] - public void ReindexerNet_Upsert() - { - _rxClient!.Upsert("Entities", _data); - } - - [BenchmarkCategory("Upsert")] - [Benchmark] - public void ReindexerNetDense_Upsert() - { - _rxClientDense!.Upsert("Entities", _data); - } - - [BenchmarkCategory("Upsert")] - [Benchmark] - public void Cachalot_Upsert() - { - var entities = _caConnector!.DataSource("BenchmarkEntity"); - entities.PutMany(_data); - } - - [BenchmarkCategory("Upsert")] - [Benchmark] - public void CachalotCompressed_Upsert() - { - var entities = _caConnectorCompressed!.DataSource("BenchmarkEntity"); - entities.PutMany(_data); - } - - [BenchmarkCategory("Upsert")] - [Benchmark] - public void CachalotOnlyMemory_Upsert() - { - var entities = _caMemoryConnector!.DataSource("BenchmarkEntity"); - entities.PutMany(_data); - } - - [BenchmarkCategory("Upsert")] - [Benchmark] - public void LiteDb_Upsert() - { - _liteColl.Upsert(_data); - } - - [BenchmarkCategory("Upsert")] - [Benchmark] - public void LiteDbMemory_Upsert() - { - _liteCollMemory.Upsert(_data); - } - - [BenchmarkCategory("Upsert")] - [Benchmark] - public void Realm_Upsert() - { - _realm.Write(() => - { - _realm.Add(_data.Select(e => (BenchmarkRealmEntity)e), update: true); - }); - } } \ No newline at end of file diff --git a/Tests/ReindexerNet.EmbeddedBenchmarks/Program.cs b/Tests/ReindexerNet.EmbeddedBenchmarks/Program.cs index 47abe78..34d5062 100644 --- a/Tests/ReindexerNet.EmbeddedBenchmarks/Program.cs +++ b/Tests/ReindexerNet.EmbeddedBenchmarks/Program.cs @@ -12,7 +12,17 @@ namespace ReindexerNetBenchmark.EmbeddedBenchmarks; public class Program { - public static void Main(string[] args) => BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); + public static void Main(string[] args) + { +#if DEBUG + var b = new SelectSinglePK{ N = 1000 }; + b.RealmSetup(); + b.Realm(); + b.RealmClean(); +#else + BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); +#endif + } } public class AntiVirusFriendlyConfig : ManualConfig diff --git a/Tests/ReindexerNet.EmbeddedBenchmarks/ReindexerNet.EmbeddedBenchmarks.csproj b/Tests/ReindexerNet.EmbeddedBenchmarks/ReindexerNet.EmbeddedBenchmarks.csproj index 65c8a24..a24148d 100644 --- a/Tests/ReindexerNet.EmbeddedBenchmarks/ReindexerNet.EmbeddedBenchmarks.csproj +++ b/Tests/ReindexerNet.EmbeddedBenchmarks/ReindexerNet.EmbeddedBenchmarks.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net8.0 enable enable ReindexerNetBenchmark diff --git a/Tests/ReindexerNet.EmbeddedBenchmarks/SelectArrayMultiple.cs b/Tests/ReindexerNet.EmbeddedBenchmarks/SelectArrayMultiple.cs new file mode 100644 index 0000000..5af5438 --- /dev/null +++ b/Tests/ReindexerNet.EmbeddedBenchmarks/SelectArrayMultiple.cs @@ -0,0 +1,130 @@ +using BenchmarkDotNet.Attributes; +using ReindexerNetBenchmark.EmbeddedBenchmarks; +using ReindexerNet; +using Realms; + +namespace ReindexerNetBenchmark; + +public class SelectArrayMultiple : SelectBenchmarkBase +{ + [Benchmark] + public IList ReindexerNet() + { + var result = new List + { + RxClient.Execute("Entities", q => q.WhereInt32("IntArray", Condition.SET, SearchItemsInt)).CaptureResult(), + RxClient.Execute("Entities", q => q.WhereString("StrArray", Condition.SET, SearchItemsStr)).CaptureResult(), + RxClient.Execute("Entities", q => q.WhereInt32("IntArray", Condition.ALLSET, SearchItemsInt)).CaptureResult(), + RxClient.Execute("Entities", q => q.WhereString("StrArray", Condition.ALLSET, SearchItemsStr)).CaptureResult() + }; + return result; + } + + [Benchmark] + public IList ReindexerNetSpanJson() + { + var result = new List + { + RxClientSpanJson.Execute("Entities", q => q.WhereInt32("IntArray", Condition.SET, SearchItemsInt)).CaptureResult(), + RxClientSpanJson.Execute("Entities", q => q.WhereString("StrArray", Condition.SET, SearchItemsStr)).CaptureResult(), + RxClientSpanJson.Execute("Entities", q => q.WhereInt32("IntArray", Condition.ALLSET, SearchItemsInt)).CaptureResult(), + RxClientSpanJson.Execute("Entities", q => q.WhereString("StrArray", Condition.ALLSET, SearchItemsStr)).CaptureResult() + }; + return result; + } + + [Benchmark] + public IList ReindexerNetSql() + { + var result = new List + { + RxClientSql.ExecuteSql($"SELECT * FROM Entities WHERE IntArray IN ({SearchItemsIntJoined})").CaptureResult(), + RxClientSql.ExecuteSql($"SELECT * FROM Entities WHERE StrArray IN ({SearchItemsStrJoined})").CaptureResult(), + RxClientSql.ExecuteSql($"SELECT * FROM Entities WHERE IntArray ALLSET ({SearchItemsIntJoined})").CaptureResult(), + RxClientSql.ExecuteSql($"SELECT * FROM Entities WHERE StrArray ALLSET ({SearchItemsStrJoined})").CaptureResult() + }; + return result; + } + + [Benchmark] + public IList Cachalot() + { + var result = new List + { + CaDS.Where(IntAnyQuery).AsEnumerable().CaptureResult(), + CaDS.Where(StrAnyQuery).AsEnumerable().CaptureResult(), + CaDS.Where(IntAllQuery).AsEnumerable().CaptureResult(), + CaDS.Where(StrAllQuery).AsEnumerable().CaptureResult() + }; + return result; + } + + [Benchmark] + public IList CachalotMemory() + { + var result = new List + { + CaDSMemory.Where(IntAnyQuery).AsEnumerable().CaptureResult(), + CaDSMemory.Where(StrAnyQuery).AsEnumerable().CaptureResult(), + CaDSMemory.Where(IntAllQuery).AsEnumerable().CaptureResult(), + CaDSMemory.Where(StrAllQuery).AsEnumerable().CaptureResult() + }; + return result; + } + + [Benchmark] + public IList CachalotCompressed() + { + var result = new List + { + CaDSCompressed.Where(IntAnyQuery).AsEnumerable().CaptureResult(), + CaDSCompressed.Where(StrAnyQuery).AsEnumerable().CaptureResult(), + CaDSCompressed.Where(IntAllQuery).AsEnumerable().CaptureResult(), + CaDSCompressed.Where(StrAllQuery).AsEnumerable().CaptureResult() + }; + return result; + } + + [Benchmark] + public IList LiteDb() + { + var result = new List + { + LiteColl.Query().Where(IntAnyQuery).ToEnumerable().CaptureResult(), + LiteColl.Query().Where(StrAnyQuery).ToEnumerable().CaptureResult(), + LiteColl.Query().Where(IntAllQuery).ToEnumerable().CaptureResult(), + LiteColl.Query().Where(StrAllQuery).ToEnumerable().CaptureResult() + }; + return result; + } + + [Benchmark] + public IList LiteDbMemory() + { + var result = new List + { + LiteCollMemory.Query().Where(IntAnyQuery).ToEnumerable().CaptureResult(), + LiteCollMemory.Query().Where(StrAnyQuery).ToEnumerable().CaptureResult(), + LiteCollMemory.Query().Where(IntAllQuery).ToEnumerable().CaptureResult(), + LiteCollMemory.Query().Where(StrAllQuery).ToEnumerable().CaptureResult() + }; + return result; + } + + [Benchmark] + public IList Realm() + { + + + + var result = new List + { + RealmCli.All().Filter($"ANY IntArray.@values IN {{ {SearchItemsIntJoined} }}").CaptureResult(), + RealmCli.All().Filter($"ANY StrArray.@values IN {{ {SearchItemsStrJoined} }}").CaptureResult(), + RealmCli.All().Filter($"ALL IntArray.@values IN {{ {SearchItemsIntJoined} }}").CaptureResult(), + RealmCli.All().Filter($"ALL StrArray.@values IN {{ {SearchItemsStrJoined} }}").CaptureResult() + }; + + return result; + } +} diff --git a/Tests/ReindexerNet.EmbeddedBenchmarks/SelectArraySingle.cs b/Tests/ReindexerNet.EmbeddedBenchmarks/SelectArraySingle.cs new file mode 100644 index 0000000..fd6a36c --- /dev/null +++ b/Tests/ReindexerNet.EmbeddedBenchmarks/SelectArraySingle.cs @@ -0,0 +1,114 @@ +using BenchmarkDotNet.Attributes; +using ReindexerNetBenchmark.EmbeddedBenchmarks; +using ReindexerNet; +using Realms; + +namespace ReindexerNetBenchmark; + +public class SelectArraySingle: SelectBenchmarkBase +{ + [Benchmark] + public IList ReindexerNet() + { + var result = new List + { + RxClient.Execute("Entities", q => q.WhereInt32("IntArray", Condition.SET, N)).CaptureResult(), + RxClient.Execute("Entities", q => q.WhereString("StrArray", Condition.SET, N.ToString())).CaptureResult(), + }; + return result; + } + + [Benchmark] + public IList ReindexerNetSpanJson() + { + var result = new List + { + RxClientSpanJson.Execute("Entities", q => q.WhereInt32("IntArray", Condition.SET, N)).CaptureResult(), + RxClientSpanJson.Execute("Entities", q => q.WhereString("StrArray", Condition.SET, N.ToString())).CaptureResult(), + }; + return result; + } + + [Benchmark] + public IList ReindexerNetSql() + { + var result = new List + { + RxClientSql.ExecuteSql($"SELECT * FROM Entities WHERE IntArray IN ({N})").CaptureResult(), + RxClientSql.ExecuteSql($"SELECT * FROM Entities WHERE StrArray IN ('{N}')").CaptureResult(), + }; + return result; + } + + [Benchmark] + public IList Cachalot() + { + var nstr = N.ToString(); + var result = new List + { + CaDS.Where(e => e.IntArray.Contains(N)).AsEnumerable().CaptureResult(), + CaDS.Where(e => e.StrArray.Contains(nstr)).AsEnumerable().CaptureResult(), + }; + return result; + } + + [Benchmark] + public IList CachalotMemory() + { + var nstr = N.ToString(); + var result = new List + { + CaDSMemory.Where(e => e.IntArray.Contains(N)).AsEnumerable().CaptureResult(), + CaDSMemory.Where(e => e.StrArray.Contains(nstr)).AsEnumerable().CaptureResult(), + }; + return result; + } + + [Benchmark] + public IList CachalotCompressed() + { + var nstr = N.ToString(); + var result = new List + { + CaDSCompressed.Where(e => e.IntArray.Contains(N)).AsEnumerable().CaptureResult(), + CaDSCompressed.Where(e => e.StrArray.Contains(nstr)).AsEnumerable().CaptureResult(), + }; + return result; + } + + [Benchmark] + public IList LiteDb() + { + var nstr = N.ToString(); + var result = new List + { + LiteColl.Query().Where(e => e.IntArray.Contains(N)).ToEnumerable().CaptureResult(), + LiteColl.Query().Where(e => e.StrArray.Contains(nstr)).ToEnumerable().CaptureResult(), + }; + return result; + } + + [Benchmark] + public IList LiteDbMemory() + { + var nstr = N.ToString(); + var result = new List + { + LiteCollMemory.Query().Where(e => e.IntArray.Contains(N)).ToEnumerable().CaptureResult(), + LiteCollMemory.Query().Where(e => e.StrArray.Contains(nstr)).ToEnumerable().CaptureResult(), + }; + return result; + } + + [Benchmark] + public IList Realm() + { + var result = new List + { + RealmCli.All().Filter("ANY IntArray.@values == $0", N).CaptureResult(), + RealmCli.All().Filter("ANY StrArray.@values == '$0'", N.ToString()).CaptureResult() + }; + + return result; + } +} diff --git a/Tests/ReindexerNet.EmbeddedBenchmarks/SelectBenchmark.cs b/Tests/ReindexerNet.EmbeddedBenchmarks/SelectBenchmark.cs deleted file mode 100644 index baff1b5..0000000 --- a/Tests/ReindexerNet.EmbeddedBenchmarks/SelectBenchmark.cs +++ /dev/null @@ -1,1326 +0,0 @@ -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Configs; -using BenchmarkDotNet.Running; -using Cachalot.Linq; -using Client.Interface; -using LiteDB; -using MessagePack; -using MessagePack.Resolvers; -using Newtonsoft.Json.Linq; -using Realms; -using ReindexerNet; -using ReindexerNet.Embedded; -using Server; -using System.Buffers; -using System.Collections; -using System.Collections.Concurrent; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using System.Text.Json; -using System.Text.Json.Serialization.Metadata; -using static System.Runtime.InteropServices.JavaScript.JSType; -using Index = ReindexerNet.Index; -using IndexType = ReindexerNet.IndexType; - -namespace ReindexerNetBenchmark.EmbeddedBenchmarks; - -//[Config(typeof(AntiVirusFriendlyConfig))] -[SimpleJob(launchCount: 1, warmupCount:1, iterationCount:5)] -[MemoryDiagnoser()] -[CategoriesColumn] -[CustomCategoryDiscoverer] -[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory, BenchmarkLogicalGroupRule.ByMethod)] -[PlainExporter] -public class SelectBenchmark -{ - private class CustomCategoryDiscoverer : DefaultCategoryDiscoverer - { - public override string[] GetCategories(MethodInfo method) => - method.Name.Split('_').Reverse().ToArray(); - } - - [AttributeUsage(AttributeTargets.Class)] - private class CustomCategoryDiscovererAttribute : Attribute, IConfigSource - { - public CustomCategoryDiscovererAttribute() - { - Config = ManualConfig.CreateEmpty() - .WithCategoryDiscoverer(new CustomCategoryDiscoverer()); - } - - public IConfig Config { get; } - } - - private string _dataPath; - private BenchmarkEntity[] _data; - - [Params(1_000)] - public int N; - - #region Setups - - private ReindexerEmbedded? _rxClient; - private ReindexerEmbedded? _rxClientSpanJson; - private ReindexerEmbedded? _rxClientSql; - private Connector? _caConnector; - private Connector? _caConnectorMemory; - private Connector? _caConnectorCompressed; - private DataSource _caDS; - private DataSource _caDSMemory; - private DataSource _caDSCompressed; - private LiteDatabase _liteDb; - private ILiteCollection _liteColl; - private LiteDatabase _liteDbMemory; - private ILiteCollection _liteCollMemory; - private Realm _realm; - private Expression> _strAllQuery; - private Expression> _strAnyQuery; - private Expression> _intAllQuery; - private Expression> _intAnyQuery; - private int[] _searchItemsInt; - private string[] _searchItemsStr; - private string _searchItemsIntJoined; - private string _searchItemsStrJoined; - - private static void GetArrayQueryExpressions(int[] searchItemsInt, string[] searchItemsStr, - out Expression> intAnyQuery, out Expression> intAllQuery, - out Expression> strAnyQuery, out Expression> strAllQuery) - { - var firstIntValue = searchItemsInt[0]; - intAnyQuery = e => e.IntArray.Contains(firstIntValue); - intAllQuery = e => e.IntArray.Contains(firstIntValue); - foreach (var item in searchItemsInt.Skip(1)) - { - intAnyQuery = intAnyQuery.OrAlso(e => e.IntArray.Contains(item)); - intAllQuery = intAllQuery.AndAlso(e => e.IntArray.Contains(item)); - } - - var firstStrValue = searchItemsStr[0]; - strAnyQuery = e => e.StrArray.Contains(firstStrValue); - strAllQuery = e => e.StrArray.Contains(firstStrValue); - foreach (var item in searchItemsInt.Skip(1)) - { - strAnyQuery = strAnyQuery.OrAlso(e => e.IntArray.Contains(item)); - strAllQuery = strAllQuery.AndAlso(e => e.IntArray.Contains(item)); - } - } - - - public void Setup() - { - _dataPath = Directory.CreateTempSubdirectory().FullName; - _data = new BenchmarkEntity[N]; - for (int i = 0; i < N; i++) - { - _data[i] = new BenchmarkEntity - { - Id = Guid.NewGuid(), - IntProperty = i, - StringProperty = "ÇŞĞÜÖİöçşğüı" + i, - CreateDate = DateTime.UtcNow, - IntArray = Enumerable.Range(0, i).ToArray(), - StrArray = Enumerable.Range(0, i).Select(i => i.ToString()).ToArray() - }; - } - - _searchItemsInt = Enumerable.Range(0, 50).Select(i => i * 20).ToArray(); - _searchItemsStr = Enumerable.Range(0, 50).Select(i => i * 20).Select(i => i.ToString()).ToArray(); - _searchItemsIntJoined = string.Join(",", Enumerable.Range(0, 50).Select(i => i * 20)); - _searchItemsStrJoined = "'" + string.Join("','", Enumerable.Range(0, 50).Select(i => i * 20)) + "'"; - } - - public void Cleanup() - { - Directory.Delete(_dataPath, true); - } - - [GlobalSetup(Targets = new[] { - nameof(ReindexerNet_SelectArrayMultiple), - nameof(ReindexerNet_SelectArraySingle), - nameof(ReindexerNet_SelectMultipleHash), - nameof(ReindexerNet_SelectMultiplePK), - nameof(ReindexerNet_SelectRange), - nameof(ReindexerNet_SelectSingleHash), - nameof(ReindexerNet_SelectSingleHashParallel), - nameof(ReindexerNet_SelectSinglePK) - })] - public void ReindexerNetSetup() - { - Setup(); - var dbPath = Path.Combine(_dataPath, "ReindexerEmbedded"); - if (Directory.Exists(dbPath)) - Directory.Delete(dbPath, true); - _rxClient = new ReindexerEmbedded(dbPath); - _rxClient.Connect(new ConnectionOptions { Engine = StorageEngine.LevelDb }); - _rxClient.OpenNamespace("Entities"); - _rxClient.TruncateNamespace("Entities"); - _rxClient.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.Id), IsPk = true, IndexType = IndexType.Hash, FieldType = FieldType.String, IsDense = false }); - _rxClient.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.IntProperty), IndexType = IndexType.Tree, FieldType = FieldType.Int, IsDense = false }); - _rxClient.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.StringProperty), IndexType = IndexType.Hash, FieldType = FieldType.String, IsDense = false }); - _rxClient.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.CreateDate), IndexType = IndexType.Hash, FieldType = FieldType.String, IsDense = false }); - _rxClient.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.IntArray), IndexType = IndexType.Hash, FieldType = FieldType.Int, IsDense = false, IsArray = true }); - _rxClient.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.StrArray), IndexType = IndexType.Hash, FieldType = FieldType.String, IsDense = false, IsArray = true }); - _rxClient!.Insert("Entities", _data); - } - - [GlobalCleanup(Targets = new[] { - nameof(ReindexerNet_SelectArrayMultiple), - nameof(ReindexerNet_SelectArraySingle), - nameof(ReindexerNet_SelectMultipleHash), - nameof(ReindexerNet_SelectMultiplePK), - nameof(ReindexerNet_SelectRange), - nameof(ReindexerNet_SelectSingleHash), - nameof(ReindexerNet_SelectSingleHashParallel), - nameof(ReindexerNet_SelectSinglePK) - })] - public void ReindexerNetClean() - { - _rxClient!.Dispose(); - Cleanup(); - } - - private sealed class SpanJsonSerializer : IReindexerSerializer - { - public SerializerType Type => SerializerType.Json; - - public T Deserialize(ReadOnlySpan bytes) - { - return SpanJson.JsonSerializer.Generic.Utf8.Deserialize(bytes); - } - - public ReadOnlySpan Serialize(T item) - { - return SpanJson.JsonSerializer.Generic.Utf8.Serialize(item); - } - } - - [GlobalSetup(Targets = new[] { - nameof(ReindexerNetSpanJson_SelectArrayMultiple), - nameof(ReindexerNetSpanJson_SelectArraySingle), - nameof(ReindexerNetSpanJson_SelectMultipleHash), - nameof(ReindexerNetSpanJson_SelectMultiplePK), - nameof(ReindexerNetSpanJson_SelectRange), - nameof(ReindexerNetSpanJson_SelectSingleHash), - nameof(ReindexerNetSpanJson_SelectSingleHashParallel), - nameof(ReindexerNetSpanJson_SelectSinglePK) - })] - public void ReindexerNetSpanJsonSetup() - { - Setup(); - var dbPathSpanJson = Path.Combine(_dataPath, "ReindexerEmbeddedSpanJson"); - if (Directory.Exists(dbPathSpanJson)) - Directory.Delete(dbPathSpanJson, true); - _rxClientSpanJson = new ReindexerEmbedded(dbPathSpanJson, new SpanJsonSerializer()); - _rxClientSpanJson.Connect(new ConnectionOptions { Engine = StorageEngine.LevelDb }); - _rxClientSpanJson.OpenNamespace("Entities"); - _rxClientSpanJson.TruncateNamespace("Entities"); - _rxClientSpanJson.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.Id), IsPk = true, IndexType = IndexType.Hash, FieldType = FieldType.String, IsDense = true }); - _rxClientSpanJson.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.IntProperty), IndexType = IndexType.Tree, FieldType = FieldType.Int, IsDense = true }); - _rxClientSpanJson.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.StringProperty), IndexType = IndexType.Hash, FieldType = FieldType.String, IsDense = true }); - _rxClientSpanJson.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.CreateDate), IndexType = IndexType.Hash, FieldType = FieldType.String, IsDense = true }); - _rxClientSpanJson.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.IntArray), IndexType = IndexType.Hash, FieldType = FieldType.Int, IsDense = true, IsArray = true }); - _rxClientSpanJson.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.StrArray), IndexType = IndexType.Hash, FieldType = FieldType.String, IsDense = true, IsArray = true }); - _rxClientSpanJson!.Insert("Entities", _data); - } - - [GlobalCleanup(Targets = new[] { - nameof(ReindexerNetSpanJson_SelectArrayMultiple), -nameof(ReindexerNetSpanJson_SelectArraySingle), - nameof(ReindexerNetSpanJson_SelectMultipleHash), - nameof(ReindexerNetSpanJson_SelectMultiplePK), - nameof(ReindexerNetSpanJson_SelectRange), - nameof(ReindexerNetSpanJson_SelectSingleHash), - nameof(ReindexerNetSpanJson_SelectSingleHashParallel), - nameof(ReindexerNetSpanJson_SelectSinglePK) - })] - public void ReindexerNetSpanJsonClean() - { - _rxClientSpanJson!.Dispose(); - Cleanup(); - } - - [GlobalSetup(Targets = new[] { - nameof(ReindexerNetSql_SelectArrayMultiple), - nameof(ReindexerNetSql_SelectArraySingle), - nameof(ReindexerNetSql_SelectMultipleHash), - nameof(ReindexerNetSql_SelectMultiplePK), - nameof(ReindexerNetSql_SelectRange), - nameof(ReindexerNetSql_SelectSingleHash), - nameof(ReindexerNetSql_SelectSingleHashParallel), - nameof(ReindexerNetSql_SelectSinglePK) - })] - public void ReindexerNetSqlSetup() - { - Setup(); - var dbPath = Path.Combine(_dataPath, "ReindexerEmbeddedSql"); - if (Directory.Exists(dbPath)) - Directory.Delete(dbPath, true); - _rxClientSql = new ReindexerEmbedded(dbPath); - _rxClientSql.Connect(new ConnectionOptions { Engine = StorageEngine.LevelDb }); - _rxClientSql.OpenNamespace("Entities"); - _rxClientSql.TruncateNamespace("Entities"); - _rxClientSql.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.Id), IsPk = true, IndexType = IndexType.Hash, FieldType = FieldType.String, IsDense = false }); - _rxClientSql.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.IntProperty), IndexType = IndexType.Tree, FieldType = FieldType.Int, IsDense = false }); - _rxClientSql.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.StringProperty), IndexType = IndexType.Hash, FieldType = FieldType.String, IsDense = false }); - _rxClientSql.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.CreateDate), IndexType = IndexType.Hash, FieldType = FieldType.String, IsDense = false }); - _rxClientSql.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.IntArray), IndexType = IndexType.Hash, FieldType = FieldType.Int, IsDense = false, IsArray = true }); - _rxClientSql.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.StrArray), IndexType = IndexType.Hash, FieldType = FieldType.String, IsDense = false, IsArray = true }); - _rxClientSql!.Insert("Entities", _data); - } - - [GlobalCleanup(Targets = new[] { - nameof(ReindexerNetSql_SelectArrayMultiple), - nameof(ReindexerNetSql_SelectArraySingle), - nameof(ReindexerNetSql_SelectMultipleHash), - nameof(ReindexerNetSql_SelectMultiplePK), - nameof(ReindexerNetSql_SelectRange), - nameof(ReindexerNetSql_SelectSingleHash), - nameof(ReindexerNetSql_SelectSingleHashParallel), - nameof(ReindexerNetSql_SelectSinglePK) - })] - public void ReindexerNetSqlClean() - { - _rxClientSql!.Dispose(); - Cleanup(); - } - - [GlobalSetup(Targets = new[] { - nameof(Cachalot_SelectArrayMultiple), - nameof(Cachalot_SelectArraySingle), - nameof(Cachalot_SelectMultipleHash), - nameof(Cachalot_SelectMultiplePK), - nameof(Cachalot_SelectRange), - nameof(Cachalot_SelectSingleHash), - nameof(Cachalot_SelectSingleHashParallel), - nameof(Cachalot_SelectSinglePK) - })] - public void CachalotSetup() - { - Setup(); - var server = new Server.Server(new NodeConfig { DataPath = _dataPath, IsPersistent = true, ClusterName = "embedded" }); - _caConnector = new Connector(new ClientConfig { IsPersistent = true }); - _caConnector.DeclareCollection("BenchmarkEntity"); - _caConnector.GetCollectionSchema("BenchmarkEntity").UseCompression = false; - _caDS = _caConnector!.DataSource("BenchmarkEntity"); - _caDS.PutMany(_data); - - //_caDS.Where(e => e.IntArray.Any(i => searchItemsInt.Contains(i))).ToList(), - //Doesn't support .Any or All methods right now, so combining queries with OR - GetArrayQueryExpressions(_searchItemsInt, _searchItemsStr, out _intAnyQuery, out _intAllQuery, out _strAnyQuery, out _strAllQuery); - } - - [GlobalCleanup(Targets = new[] { - nameof(Cachalot_SelectArrayMultiple), - nameof(Cachalot_SelectArraySingle), - nameof(Cachalot_SelectMultipleHash), - nameof(Cachalot_SelectMultiplePK), - nameof(Cachalot_SelectRange), - nameof(Cachalot_SelectSingleHash), - nameof(Cachalot_SelectSingleHashParallel), - nameof(Cachalot_SelectSinglePK) - })] - public void CachalotClean() - { - _caConnector!.Dispose(); - Cleanup(); - } - - [GlobalSetup(Targets = new[] { - nameof(CachalotCompressed_SelectArrayMultiple), - nameof(CachalotCompressed_SelectArraySingle), - nameof(CachalotCompressed_SelectMultipleHash), - nameof(CachalotCompressed_SelectMultiplePK), - nameof(CachalotCompressed_SelectRange), - nameof(CachalotCompressed_SelectSingleHash), - nameof(CachalotCompressed_SelectSingleHashParallel), - nameof(CachalotCompressed_SelectSinglePK) - })] - public void CachalotCompressedSetup() - { - Setup(); - var server = new Server.Server(new NodeConfig { DataPath = _dataPath, IsPersistent = true, ClusterName = "embedded" }); - _caConnectorCompressed = new Connector(new ClientConfig { IsPersistent = true }); - _caConnectorCompressed.DeclareCollection("BenchmarkEntity"); - _caConnectorCompressed.GetCollectionSchema("BenchmarkEntity").UseCompression = true; - _caDSCompressed = _caConnectorCompressed!.DataSource("BenchmarkEntity"); - _caDSCompressed.PutMany(_data); - - //Doesn't support .Any or All methods right now, so combining queries with OR - GetArrayQueryExpressions(_searchItemsInt, _searchItemsStr, out _intAnyQuery, out _intAllQuery, out _strAnyQuery, out _strAllQuery); - } - - [GlobalCleanup(Targets = new[] { - nameof(CachalotCompressed_SelectArrayMultiple), - nameof(CachalotCompressed_SelectArraySingle), - nameof(CachalotCompressed_SelectMultipleHash), - nameof(CachalotCompressed_SelectMultiplePK), - nameof(CachalotCompressed_SelectRange), - nameof(CachalotCompressed_SelectSingleHash), - nameof(CachalotCompressed_SelectSingleHashParallel), - nameof(CachalotCompressed_SelectSinglePK) - })] - public void CachalotCompressedClean() - { - _caConnectorCompressed!.Dispose(); - Cleanup(); - } - - [GlobalSetup(Targets = new[] { - nameof(CachalotMemory_SelectArrayMultiple), - nameof(CachalotMemory_SelectArraySingle), - nameof(CachalotMemory_SelectMultipleHash), - nameof(CachalotMemory_SelectMultiplePK), - nameof(CachalotMemory_SelectRange), - nameof(CachalotMemory_SelectSingleHash), - nameof(CachalotMemory_SelectSingleHashParallel), - nameof(CachalotMemory_SelectSinglePK) - })] - public void CachalotMemorySetup() - { - Setup(); - var server = new Server.Server(new NodeConfig { DataPath = Path.Combine(_dataPath, "CachalotMemory"), IsPersistent = false, ClusterName = "embedded" }); - _caConnectorMemory = new Connector(new ClientConfig { IsPersistent = false }); - _caConnectorMemory.DeclareCollection("BenchmarkEntity"); - _caConnectorMemory.GetCollectionSchema("BenchmarkEntity").UseCompression = false; - _caDSMemory = _caConnectorMemory!.DataSource("BenchmarkEntity"); - _caDSMemory.PutMany(_data); - - //Doesn't support .Any or All methods right now, so combining queries with OR - GetArrayQueryExpressions(_searchItemsInt, _searchItemsStr, out _intAnyQuery, out _intAllQuery, out _strAnyQuery, out _strAllQuery); - } - - [GlobalCleanup(Targets = new[] { - nameof(CachalotMemory_SelectArrayMultiple), - nameof(CachalotMemory_SelectArraySingle), - nameof(CachalotMemory_SelectMultipleHash), - nameof(CachalotMemory_SelectMultiplePK), - nameof(CachalotMemory_SelectRange), - nameof(CachalotMemory_SelectSingleHash), - nameof(CachalotMemory_SelectSingleHashParallel), - nameof(CachalotMemory_SelectSinglePK) - })] - public void CachalotMemoryClean() - { - _caConnectorMemory!.Dispose(); - Cleanup(); - } - - [GlobalSetup(Targets = new[] { - nameof(LiteDb_SelectArrayMultiple), - nameof(LiteDb_SelectArraySingle), - nameof(LiteDb_SelectMultipleHash), - nameof(LiteDb_SelectMultiplePK), - nameof(LiteDb_SelectRange), - nameof(LiteDb_SelectSingleHash), - nameof(LiteDb_SelectSingleHashParallel), - nameof(LiteDb_SelectSinglePK) - })] - public void LiteDBSetup() - { - Setup(); - _liteDb = new LiteDatabase(Path.Combine(_dataPath, "LiteDB")); - _liteColl = _liteDb.GetCollection("Entities"); - _liteColl.EnsureIndex(e => e.Id, true); - _liteColl.EnsureIndex(e => e.IntProperty); - _liteColl.EnsureIndex(e => e.StringProperty); - _liteColl.EnsureIndex(e => e.CreateDate); - _liteColl.EnsureIndex(e => e.IntArray); - _liteColl.EnsureIndex(e => e.StrArray); - _liteColl.Upsert(_data); - - //Doesn't support .Any or All methods right now, so combining queries with OR - GetArrayQueryExpressions(_searchItemsInt, _searchItemsStr, out _intAnyQuery, out _intAllQuery, out _strAnyQuery, out _strAllQuery); - } - - [GlobalCleanup(Targets = new[] { - nameof(LiteDb_SelectArrayMultiple), - nameof(LiteDb_SelectArraySingle), - nameof(LiteDb_SelectMultipleHash), - nameof(LiteDb_SelectMultiplePK), - nameof(LiteDb_SelectRange), - nameof(LiteDb_SelectSingleHash), - nameof(LiteDb_SelectSingleHashParallel), - nameof(LiteDb_SelectSinglePK) - })] - public void LiteDBClean() - { - _liteDb!.Dispose(); - Cleanup(); - } - - [GlobalSetup(Targets = new[] { - nameof(LiteDbMemory_SelectArrayMultiple), - nameof(LiteDbMemory_SelectArraySingle), - nameof(LiteDbMemory_SelectMultipleHash), - nameof(LiteDbMemory_SelectMultiplePK), - nameof(LiteDbMemory_SelectRange), - nameof(LiteDbMemory_SelectSingleHash), - nameof(LiteDbMemory_SelectSingleHashParallel), - nameof(LiteDbMemory_SelectSinglePK) - })] - public void LiteDbMemorySetup() - { - Setup(); - _liteDbMemory = new LiteDatabase(":memory:"); - _liteCollMemory = _liteDbMemory.GetCollection("Entities"); - _liteCollMemory.EnsureIndex(e => e.Id, true); - _liteCollMemory.EnsureIndex(e => e.IntProperty); - _liteCollMemory.EnsureIndex(e => e.StringProperty); - _liteCollMemory.EnsureIndex(e => e.CreateDate); - _liteCollMemory.EnsureIndex(e => e.IntArray); - _liteCollMemory.EnsureIndex(e => e.StrArray); - _liteCollMemory.Upsert(_data); - - //Doesn't support .Any or All methods right now, so combining queries with OR - GetArrayQueryExpressions(_searchItemsInt, _searchItemsStr, out _intAnyQuery, out _intAllQuery, out _strAnyQuery, out _strAllQuery); - } - - [GlobalCleanup(Targets = new[] { - nameof(LiteDbMemory_SelectArrayMultiple), - nameof(LiteDbMemory_SelectArraySingle), - nameof(LiteDbMemory_SelectMultipleHash), - nameof(LiteDbMemory_SelectMultiplePK), - nameof(LiteDbMemory_SelectRange), - nameof(LiteDbMemory_SelectSingleHash), - nameof(LiteDbMemory_SelectSingleHashParallel), - nameof(LiteDbMemory_SelectSinglePK) - })] - public void LiteDbMemoryClean() - { - _liteDbMemory!.Dispose(); - Cleanup(); - } - - [GlobalSetup(Targets = new[] { - nameof(Realm_SelectArrayMultiple), - nameof(Realm_SelectArraySingle), - nameof(Realm_SelectMultipleHash), - nameof(Realm_SelectMultiplePK), - nameof(Realm_SelectRange), - nameof(Realm_SelectSingleHash), - nameof(Realm_SelectSingleHashParallel), - nameof(Realm_SelectSinglePK) - })] - public void RealmSetup() - { - Setup(); - _realm = Realm.GetInstance(new RealmConfiguration(Path.Combine(_dataPath, "Realms"))); - _realm.Write(() => - { - _realm.Add(_data.Select(e => (BenchmarkRealmEntity)e), update: false); - }); - } - - [GlobalCleanup(Targets = new[] { - nameof(Realm_SelectArrayMultiple), - nameof(Realm_SelectArraySingle), - nameof(Realm_SelectMultipleHash), - nameof(Realm_SelectMultiplePK), - nameof(Realm_SelectRange), - nameof(Realm_SelectSingleHash), - nameof(Realm_SelectSingleHashParallel), - nameof(Realm_SelectSinglePK) - })] - public void RealmClean() - { - _realm.Dispose(); - Realm.DeleteRealm(new RealmConfiguration(Path.Combine(_dataPath, "Realm"))); - Cleanup(); - } - #endregion - - [Benchmark] - public IList ReindexerNet_SelectSinglePK() - { - var result = new List(); - for (int i = 0; i < N; i++) - { - result.Add(_rxClient.Execute("Entities", q => q.Limit(1).WhereGuid("Id", Condition.EQ, _data[i].Id))); - } - - return result; - } - - [Benchmark] - public IList ReindexerNetSpanJson_SelectSinglePK() - { - var result = new List(); - for (int i = 0; i < N; i++) - { - result.Add(_rxClientSpanJson.Execute("Entities", q => q.Limit(1).WhereGuid("Id", Condition.EQ, _data[i].Id))); - } - return result; - } - - [Benchmark] - public IList ReindexerNetSql_SelectSinglePK() - { - var result = new List(); - for (int i = 0; i < N; i++) - { - result.Add(_rxClientSql.ExecuteSql($"SELECT * FROM Entities WHERE Id = '{_data[i].Id}' LIMIT 1")); - } - - return result; - } - - [Benchmark] - public IList Cachalot_SelectSinglePK() - { - var result = new List(); - for (int i = 0; i < N; i++) - { - var id = _data[i].Id; - result.Add(_caDS[id]); - } - - return result; - } - - [Benchmark] - public IList CachalotMemory_SelectSinglePK() - { - var result = new List(); - for (int i = 0; i < N; i++) - { - var id = _data[i].Id; - result.Add(_caDSMemory[id]); - } - - return result; - } - - [Benchmark] - public IList CachalotCompressed_SelectSinglePK() - { - var result = new List(); - for (int i = 0; i < N; i++) - { - var id = _data[i].Id; - result.Add(_caDSCompressed[id]); - } - return result; - } - - [Benchmark] - public IList LiteDb_SelectSinglePK() - { - var result = new List(); - for (int i = 0; i < N; i++) - { - var id = _data[i].Id; - result.Add(_liteColl.FindById(id)); - } - return result; - } - - [Benchmark] - public IList LiteDbMemory_SelectSinglePK() - { - var result = new List(); - for (int i = 0; i < N; i++) - { - var id = _data[i].Id; - result.Add(_liteCollMemory.FindById(id)); - } - return result; - } - - [Benchmark] - public IList Realm_SelectSinglePK() - { - var result = new List(); - for (int i = 0; i < N; i++) - { - result.Add(_realm.Find(_data[i].Id)); - } - return result; - } - - [Benchmark] - public IList ReindexerNet_SelectMultiplePK() - { - var result = new List(); - var ids = _data.Take(N).Select(i => i.Id).ToArray(); - result.Add(_rxClient.Execute("Entities", q => q.WhereGuid("Id", Condition.SET, ids))); - return result; - } - - [Benchmark] - public IList ReindexerNetSpanJson_SelectMultiplePK() - { - var result = new List(); - var ids = _data.Take(N).Select(i => i.Id).ToArray(); - result.Add(_rxClientSpanJson.Execute("Entities", q => q.WhereGuid("Id", Condition.SET, ids))); - return result; - } - - [Benchmark] - public IList ReindexerNetSql_SelectMultiplePK() - { - var result = new List(); - var ids = string.Join("','", _data.Take(N).Select(i => i.Id)); - result.Add(_rxClientSql.ExecuteSql($"SELECT * FROM Entities WHERE Id IN ('{ids}')")); - return result; - } - - [Benchmark] - public IList Cachalot_SelectMultiplePK() - { - var result = new List(); - var ids = _data.Take(N).Select(i => i.Id).ToList(); - result.Add(_caDS.Where(e => ids.Contains(e.Id)).ToList()); - return result; - } - - [Benchmark] - public IList CachalotMemory_SelectMultiplePK() - { - var result = new List(); - var ids = _data.Take(N).Select(i => i.Id).ToList(); - result.Add(_caDSMemory.Where(e => ids.Contains(e.Id)).ToList()); - return result; - } - - [Benchmark] - public IList CachalotCompressed_SelectMultiplePK() - { - var result = new List(); - var ids = _data.Take(N).Select(i => i.Id).ToList(); - result.Add(_caDSCompressed.Where(e => ids.Contains(e.Id)).ToList()); - return result; - } - - [Benchmark] - public IList LiteDb_SelectMultiplePK() - { - var result = new List(); - var ids = _data.Take(N).Select(i => i.Id).ToList(); - result.Add(_liteColl.Query().Where(e => ids.Contains(e.Id)).ToList()); - return result; - } - - [Benchmark] - public IList LiteDbMemory_SelectMultiplePK() - { - var result = new List(); - var ids = _data.Take(N).Select(i => i.Id).ToList(); - result.Add(_liteCollMemory.Query().Where(e => ids.Contains(e.Id)).ToList()); - return result; - } - - [Benchmark] - public IList Realm_SelectMultiplePK() - { - var result = new List(); - var filter = string.Join(" OR ", _data.Take(N).Select(i => $"(Id == uuid({i.Id}))")); - result.Add(_realm.All().Filter(filter).ToList()); - return result; - } - - [Benchmark] - public IList ReindexerNet_SelectSingleHash() - { - var result = new List(); - for (int i = 0; i < N; i++) - { - result.Add(_rxClient.Execute("Entities", q => q.WhereString("StringProperty", Condition.EQ, _data[i].StringProperty).Limit(1))); - } - - return result; - } - - [Benchmark] - public IList ReindexerNetSpanJson_SelectSingleHash() - { - var result = new List(); - for (int i = 0; i < N; i++) - { - result.Add(_rxClientSpanJson.Execute("Entities", q => q.WhereString("StringProperty", Condition.EQ, _data[i].StringProperty).Limit(1))); - } - return result; - } - - [Benchmark] - public IList ReindexerNetSql_SelectSingleHash() - { - var result = new List(); - for (int i = 0; i < N; i++) - { - result.Add(_rxClientSql.ExecuteSql($"SELECT * FROM Entities WHERE StringProperty = '{_data[i].StringProperty}' LIMIT 1")); - } - - return result; - } - - [Benchmark] - public IList Cachalot_SelectSingleHash() - { - var result = new List(); - for (int i = 0; i < N; i++) - { - var str = _data[i].StringProperty; - result.Add(_caDS.FirstOrDefault(e => e.StringProperty == str)); - } - return result; - } - - [Benchmark] - public IList CachalotMemory_SelectSingleHash() - { - var result = new List(); - for (int i = 0; i < N; i++) - { - var str = _data[i].StringProperty; - result.Add(_caDSMemory.FirstOrDefault(e => e.StringProperty == str)); - } - return result; - } - - [Benchmark] - public IList CachalotCompressed_SelectSingleHash() - { - var result = new List(); - for (int i = 0; i < N; i++) - { - var str = _data[i].StringProperty; - result.Add(_caDSCompressed.FirstOrDefault(e => e.StringProperty == str)); - } - return result; - } - - [Benchmark] - public IList LiteDb_SelectSingleHash() - { - var result = new List(); - for (int i = 0; i < N; i++) - { - var str = _data[i].StringProperty; - result.Add(_liteColl.Query().Where(e => e.StringProperty == str).FirstOrDefault()); - } - return result; - } - - [Benchmark] - public IList LiteDbMemory_SelectSingleHash() - { - var result = new List(); - for (int i = 0; i < N; i++) - { - var str = _data[i].StringProperty; - result.Add(_liteCollMemory.Query().Where(e => e.StringProperty == str).FirstOrDefault()); - } - return result; - } - - [Benchmark] - public IList Realm_SelectSingleHash() - { - var result = new List(); - for (int i = 0; i < N; i++) - { - var str = _data[i].StringProperty; - result.Add(_realm.All().Where(e => e.StringProperty == str).FirstOrDefault()); - } - return result; - } - - [Benchmark] - public ConcurrentBag ReindexerNet_SelectSingleHashParallel() - { - var result = new ConcurrentBag(); - Parallel.For(0, N, i => - { - result.Add(_rxClient.Execute("Entities", q => q.WhereString("StringProperty", Condition.EQ, _data[i].StringProperty).Limit(1))); - }); - return result; - } - - [Benchmark] - public ConcurrentBag ReindexerNetSpanJson_SelectSingleHashParallel() - { - var result = new ConcurrentBag(); - Parallel.For(0, N, i => - { - result.Add(_rxClientSpanJson.Execute("Entities", q => q.WhereString("StringProperty", Condition.EQ, _data[i].StringProperty).Limit(1))); - }); - return result; - } - - [Benchmark] - public ConcurrentBag ReindexerNetSql_SelectSingleHashParallel() - { - var result = new ConcurrentBag(); - Parallel.For(0, N, i => - { - result.Add(_rxClientSql.ExecuteSql($"SELECT * FROM Entities WHERE StringProperty = '{_data[i].StringProperty}' LIMIT 1")); - }); - return result; - } - - [Benchmark] - public ConcurrentBag Cachalot_SelectSingleHashParallel() - { - var result = new ConcurrentBag(); - Parallel.For(0, N, i => - { - var str = _data[i].StringProperty; - result.Add(_caDS.FirstOrDefault(e => e.StringProperty == str)); - }); - return result; - } - - [Benchmark] - public ConcurrentBag CachalotMemory_SelectSingleHashParallel() - { - var result = new ConcurrentBag(); - Parallel.For(0, N, i => - { - var str = _data[i].StringProperty; - result.Add(_caDSMemory.FirstOrDefault(e => e.StringProperty == str)); - }); - return result; - } - - [Benchmark] - public ConcurrentBag CachalotCompressed_SelectSingleHashParallel() - { - var result = new ConcurrentBag(); - Parallel.For(0, N, i => - { - var str = _data[i].StringProperty; - result.Add(_caDSCompressed.FirstOrDefault(e => e.StringProperty == str)); - }); - return result; - } - - [Benchmark] - public ConcurrentBag LiteDb_SelectSingleHashParallel() - { - var result = new ConcurrentBag(); - Parallel.For(0, N, i => - { - var str = _data[i].StringProperty; - result.Add(_liteColl.Query().Where(e => e.StringProperty == str).FirstOrDefault()); - }); - return result; - } - - [Benchmark] - public ConcurrentBag LiteDbMemory_SelectSingleHashParallel() - { - var result = new ConcurrentBag(); - Parallel.For(0, N, i => - { - var str = _data[i].StringProperty; - result.Add(_liteCollMemory.Query().Where(e => e.StringProperty == str).FirstOrDefault()); - }); - return result; - } - - [Benchmark] - public ConcurrentBag Realm_SelectSingleHashParallel() - { - var result = new ConcurrentBag(); - Parallel.For(0, N, i => - { - using var realm = Realm.GetInstance(new RealmConfiguration(Path.Combine(_dataPath, "Realms"))); - var str = _data[i].StringProperty; - result.Add(realm.All().Where(e => e.StringProperty == str).FirstOrDefault()); - }); - return result; - } - - [Benchmark] - public IList ReindexerNet_SelectMultipleHash() - { - var result = new List(); - var props = _data.Take(N).Select(i => i.StringProperty).ToArray(); - result.Add(_rxClient.Execute("Entities", q => q.WhereString("StringProperty", Condition.SET, props))); - return result; - } - - [Benchmark] - public IList ReindexerNetSpanJson_SelectMultipleHash() - { - var result = new List(); - var props = _data.Take(N).Select(i => i.StringProperty).ToArray(); - result.Add(_rxClientSpanJson.Execute("Entities", q => q.WhereString("StringProperty", Condition.SET, props))); - return result; - } - - [Benchmark] - public IList ReindexerNetSql_SelectMultipleHash() - { - var result = new List(); - var props = string.Join("','", _data.Take(N).Select(i => i.StringProperty)); - result.Add(_rxClientSql.ExecuteSql($"SELECT * FROM Entities WHERE StringProperty IN ('{props}')")); - return result; - } - - [Benchmark] - public IList Cachalot_SelectMultipleHash() - { - var result = new List(); - var props = _data.Take(N).Select(i => i.StringProperty).ToList(); - result.Add(_caDS.Where(e => props.Contains(e.StringProperty)).ToList()); - return result; - } - - [Benchmark] - public IList CachalotMemory_SelectMultipleHash() - { - var result = new List(); - var props = _data.Take(N).Select(i => i.StringProperty).ToList(); - result.Add(_caDSMemory.Where(e => props.Contains(e.StringProperty)).ToList()); - return result; - } - - [Benchmark] - public IList CachalotCompressed_SelectMultipleHash() - { - var result = new List(); - var props = _data.Take(N).Select(i => i.StringProperty).ToList(); - result.Add(_caDSCompressed.Where(e => props.Contains(e.StringProperty)).ToList()); - return result; - } - - [Benchmark] - public IList LiteDb_SelectMultipleHash() - { - var result = new List(); - var props = _data.Take(N).Select(i => i.StringProperty).ToList(); - result.Add(_liteColl.Query().Where(e => props.Contains(e.StringProperty)).ToList()); - return result; - } - - [Benchmark] - public IList LiteDbMemory_SelectMultipleHash() - { - var result = new List(); - var props = _data.Take(N).Select(i => i.StringProperty).ToList(); - result.Add(_liteCollMemory.Query().Where(e => props.Contains(e.StringProperty)).ToList()); - return result; - } - - [Benchmark] - public IList Realm_SelectMultipleHash() - { - var result = new List(); - var filter = string.Join(" OR ", _data.Take(N).Select(i => $"(StringProperty == '{i.StringProperty}')")); - result.Add(_realm.All().Filter(filter).ToList()); - return result; - } - - [Benchmark] - public IList ReindexerNet_SelectRange() - { - var result = new List(); - var entity = _data[N / 2]; - result.Add(_rxClient.Execute("Entities", q => q.WhereInt32("IntProperty", Condition.LT, entity.IntProperty ?? 0))); - result.Add(_rxClient.Execute("Entities", q => q.WhereInt32("IntProperty", Condition.GE, entity.IntProperty ?? 0))); - return result; - } - - [Benchmark] - public IList ReindexerNetSpanJson_SelectRange() - { - var result = new List(); - var entity = _data[N / 2]; - result.Add(_rxClientSpanJson.Execute("Entities", q => q.WhereInt32("IntProperty", Condition.LT, entity.IntProperty ?? 0))); - result.Add(_rxClientSpanJson.Execute("Entities", q => q.WhereInt32("IntProperty", Condition.GE, entity.IntProperty ?? 0))); - return result; - } - - [Benchmark] - public IList ReindexerNetSql_SelectRange() - { - var result = new List(); - var entity = _data[N / 2]; - result.Add(_rxClientSql.ExecuteSql($"SELECT * FROM Entities WHERE IntProperty < {entity.IntProperty}")); - result.Add(_rxClientSql.ExecuteSql($"SELECT * FROM Entities WHERE IntProperty >= {entity.IntProperty}")); - return result; - } - - [Benchmark] - public IList Cachalot_SelectRange() - { - var result = new List(); - var entity = _data[N / 2]; - result.Add(_caDS.Where(e => e.IntProperty < entity.IntProperty).ToList()); - result.Add(_caDS.Where(e => e.IntProperty >= entity.IntProperty).ToList()); - return result; - } - - [Benchmark] - public IList CachalotMemory_SelectRange() - { - var result = new List(); - var entity = _data[N / 2]; - result.Add(_caDSMemory.Where(e => e.IntProperty < entity.IntProperty).ToList()); - result.Add(_caDSMemory.Where(e => e.IntProperty >= entity.IntProperty).ToList()); - return result; - } - - [Benchmark] - public IList CachalotCompressed_SelectRange() - { - var result = new List(); - var entity = _data[N / 2]; - result.Add(_caDSCompressed.Where(e => e.IntProperty < entity.IntProperty).ToList()); - result.Add(_caDSCompressed.Where(e => e.IntProperty >= entity.IntProperty).ToList()); - return result; - } - - [Benchmark] - public IList LiteDb_SelectRange() - { - var result = new List(); - var entity = _data[N / 2]; - result.Add(_liteColl.Query().Where(e => e.IntProperty < entity.IntProperty).ToList()); - result.Add(_liteColl.Query().Where(e => e.IntProperty >= entity.IntProperty).ToList()); - return result; - } - - [Benchmark] - public IList LiteDbMemory_SelectRange() - { - var result = new List(); - var entity = _data[N / 2]; - result.Add(_liteCollMemory.Query().Where(e => e.IntProperty < entity.IntProperty).ToList()); - result.Add(_liteCollMemory.Query().Where(e => e.IntProperty >= entity.IntProperty).ToList()); - return result; - } - - [Benchmark] - public IList Realm_SelectRange() - { - var result = new List(); - var entity = _data[N / 2]; - result.Add(_realm.All().Where(e => e.IntProperty < entity.IntProperty).ToList()); - result.Add(_realm.All().Where(e => e.IntProperty >= entity.IntProperty).ToList()); - return result; - } - - [Benchmark] - public IList ReindexerNet_SelectArraySingle() - { - var result = new List - { - _rxClient.Execute("Entities", q => q.WhereInt32("IntArray", Condition.SET, N)), - _rxClient.Execute("Entities", q => q.WhereString("StrArray", Condition.SET, N.ToString())), - }; - return result; - } - - [Benchmark] - public IList ReindexerNetSpanJson_SelectArraySingle() - { - var result = new List - { - _rxClientSpanJson.Execute("Entities", q => q.WhereInt32("IntArray", Condition.SET, N)), - _rxClientSpanJson.Execute("Entities", q => q.WhereString("StrArray", Condition.SET, N.ToString())), - }; - return result; - } - - [Benchmark] - public IList ReindexerNetSql_SelectArraySingle() - { - var result = new List - { - _rxClientSql.ExecuteSql($"SELECT * FROM Entities WHERE IntArray IN ({N})"), - _rxClientSql.ExecuteSql($"SELECT * FROM Entities WHERE StrArray IN ('{N}')"), - }; - return result; - } - - [Benchmark] - public IList Cachalot_SelectArraySingle() - { - var nstr = N.ToString(); - var result = new List - { - _caDS.Where(e => e.IntArray.Contains(N)).ToList(), - _caDS.Where(e => e.StrArray.Contains(nstr)).ToList(), - }; - return result; - } - - [Benchmark] - public IList CachalotMemory_SelectArraySingle() - { - var nstr = N.ToString(); - var result = new List - { - _caDSMemory.Where(e => e.IntArray.Contains(N)).ToList(), - _caDSMemory.Where(e => e.StrArray.Contains(nstr)).ToList(), - }; - return result; - } - - [Benchmark] - public IList CachalotCompressed_SelectArraySingle() - { - var nstr = N.ToString(); - var result = new List - { - _caDSCompressed.Where(e => e.IntArray.Contains(N)).ToList(), - _caDSCompressed.Where(e => e.StrArray.Contains(nstr)).ToList(), - }; - return result; - } - - [Benchmark] - public IList LiteDb_SelectArraySingle() - { - var nstr = N.ToString(); - var result = new List - { - _liteColl.Query().Where(e => e.IntArray.Contains(N)).ToList(), - _liteColl.Query().Where(e => e.StrArray.Contains(nstr)).ToList(), - }; - return result; - } - - [Benchmark] - public IList LiteDbMemory_SelectArraySingle() - { - var nstr = N.ToString(); - var result = new List - { - _liteCollMemory.Query().Where(e => e.IntArray.Contains(N)).ToList(), - _liteCollMemory.Query().Where(e => e.StrArray.Contains(nstr)).ToList(), - }; - return result; - } - - [Benchmark] - public IList Realm_SelectArraySingle() - { - var result = new List - { - _realm.All().Filter("ANY IntArray.@values == $0", N).ToList(), - _realm.All().Filter("ANY StrArray.@values == '$0'", N.ToString()).ToList(), - }; - return result; - } - - [Benchmark] - public IList ReindexerNet_SelectArrayMultiple() - { - var result = new List - { - _rxClient.Execute("Entities", q => q.WhereInt32("IntArray", Condition.SET, _searchItemsInt)), - _rxClient.Execute("Entities", q => q.WhereString("StrArray", Condition.SET, _searchItemsStr)), - _rxClient.Execute("Entities", q => q.WhereInt32("IntArray", Condition.ALLSET, _searchItemsInt)), - _rxClient.Execute("Entities", q => q.WhereString("StrArray", Condition.ALLSET, _searchItemsStr)) - }; - return result; - } - - [Benchmark] - public IList ReindexerNetSpanJson_SelectArrayMultiple() - { - var result = new List - { - _rxClientSpanJson.Execute("Entities", q => q.WhereInt32("IntArray", Condition.SET, _searchItemsInt)), - _rxClientSpanJson.Execute("Entities", q => q.WhereString("StrArray", Condition.SET, _searchItemsStr)), - _rxClientSpanJson.Execute("Entities", q => q.WhereInt32("IntArray", Condition.ALLSET, _searchItemsInt)), - _rxClientSpanJson.Execute("Entities", q => q.WhereString("StrArray", Condition.ALLSET, _searchItemsStr)) - }; - return result; - } - - [Benchmark] - public IList ReindexerNetSql_SelectArrayMultiple() - { - var result = new List - { - _rxClientSql.ExecuteSql($"SELECT * FROM Entities WHERE IntArray IN ({_searchItemsIntJoined})"), - _rxClientSql.ExecuteSql($"SELECT * FROM Entities WHERE StrArray IN ({_searchItemsStrJoined})"), - _rxClientSql.ExecuteSql($"SELECT * FROM Entities WHERE IntArray ALLSET ({_searchItemsIntJoined})"), - _rxClientSql.ExecuteSql($"SELECT * FROM Entities WHERE StrArray ALLSET ({_searchItemsStrJoined})") - }; - return result; - } - - [Benchmark] - public IList Cachalot_SelectArrayMultiple() - { - var result = new List - { - _caDS.Where(_intAnyQuery).ToList(), - _caDS.Where(_strAnyQuery).ToList(), - _caDS.Where(_intAllQuery).ToList(), - _caDS.Where(_strAllQuery).ToList() - }; - return result; - } - - [Benchmark] - public IList CachalotMemory_SelectArrayMultiple() - { - var result = new List - { - _caDSMemory.Where(_intAnyQuery).ToList(), - _caDSMemory.Where(_strAnyQuery).ToList(), - _caDSMemory.Where(_intAllQuery).ToList(), - _caDSMemory.Where(_strAllQuery).ToList() - }; - return result; - } - - [Benchmark] - public IList CachalotCompressed_SelectArrayMultiple() - { - var result = new List - { - _caDSCompressed.Where(_intAnyQuery).ToList(), - _caDSCompressed.Where(_strAnyQuery).ToList(), - _caDSCompressed.Where(_intAllQuery).ToList(), - _caDSCompressed.Where(_strAllQuery).ToList() - }; - return result; - } - - [Benchmark] - public IList LiteDb_SelectArrayMultiple() - { - var result = new List - { - _liteColl.Query().Where(_intAnyQuery).ToList(), - _liteColl.Query().Where(_strAnyQuery).ToList(), - _liteColl.Query().Where(_intAllQuery).ToList(), - _liteColl.Query().Where(_strAllQuery).ToList() - }; - return result; - } - - [Benchmark] - public IList LiteDbMemory_SelectArrayMultiple() - { - var result = new List - { - _liteCollMemory.Query().Where(_intAnyQuery).ToList(), - _liteCollMemory.Query().Where(_strAnyQuery).ToList(), - _liteCollMemory.Query().Where(_intAllQuery).ToList(), - _liteCollMemory.Query().Where(_strAllQuery).ToList() - }; - return result; - } - - [Benchmark] - public IList Realm_SelectArrayMultiple() - { - var result = new List - { - _realm.All().Filter($"ANY IntArray.@values IN {{ {_searchItemsIntJoined} }}").ToList(), - _realm.All().Filter($"ANY StrArray.@values IN {{ {_searchItemsStrJoined} }}").ToList(), - _realm.All().Filter($"ALL IntArray.@values IN {{ {_searchItemsIntJoined} }}").ToList(), - _realm.All().Filter($"ALL StrArray.@values IN {{ {_searchItemsStrJoined} }}").ToList() - }; - return result; - } -} \ No newline at end of file diff --git a/Tests/ReindexerNet.EmbeddedBenchmarks/SelectBenchmarkBase.cs b/Tests/ReindexerNet.EmbeddedBenchmarks/SelectBenchmarkBase.cs new file mode 100644 index 0000000..43243ee --- /dev/null +++ b/Tests/ReindexerNet.EmbeddedBenchmarks/SelectBenchmarkBase.cs @@ -0,0 +1,404 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Columns; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Diagnosers; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Order; +using BenchmarkDotNet.Running; +using Cachalot.Linq; +using Client.Interface; +using LiteDB; +using Realms; +using ReindexerNet; +using ReindexerNet.Embedded; +using Server; +using System.Buffers; +using System.Linq.Expressions; +using System.Reflection; +using Index = ReindexerNet.Index; +using IndexType = ReindexerNet.IndexType; + +namespace ReindexerNetBenchmark.EmbeddedBenchmarks; + +[Config(typeof(SelectBenchmarkConfig))] +public abstract class SelectBenchmarkBase +{ + protected class SelectBenchmarkConfig: ManualConfig + { + public SelectBenchmarkConfig() + { + AddJob(Job.Default); + AddDiagnoser(new MemoryDiagnoser(new MemoryDiagnoserConfig(displayGenColumns: true))); + AddColumn(StatisticColumn.Min, StatisticColumn.Max); + CategoryDiscoverer = new CustomCategoryDiscoverer(); + Orderer = new DefaultOrderer(SummaryOrderPolicy.FastestToSlowest); + } + } + + private class CustomCategoryDiscoverer : DefaultCategoryDiscoverer + { + public override string[] GetCategories(MethodInfo method) => + method.Name.Split('_').Reverse().ToArray(); + } + + protected string DataPath; + protected BenchmarkEntity[] Data; + + [Params(1_000)] + public int N; + + #region Setups + + protected ReindexerEmbedded? RxClient; + protected ReindexerEmbedded? RxClientSpanJson; + protected ReindexerEmbedded? RxClientSql; + protected Connector? CaConnector; + protected Connector? CaConnectorMemory; + protected Connector? CaConnectorCompressed; + protected DataSource CaDS; + protected DataSource CaDSMemory; + protected DataSource CaDSCompressed; + private LiteDatabase _liteDb; + protected ILiteCollection LiteColl; + private LiteDatabase _liteDbMemory; + protected ILiteCollection LiteCollMemory; + protected Realm RealmCli; + protected Expression> StrAllQuery; + protected Expression> StrAnyQuery; + protected Expression> IntAllQuery; + protected Expression> IntAnyQuery; + protected int[] SearchItemsInt; + protected string[] SearchItemsStr; + protected string SearchItemsIntJoined; + protected string SearchItemsStrJoined; + protected string?[] SearchStringProperties; + protected string SearchStringPropertiesJoined; + protected Guid[] SearchIds; + protected string SearchIdsJoined; + + private static void GetArrayQueryExpressions(int[] searchItemsInt, string[] searchItemsStr, + out Expression> intAnyQuery, out Expression> intAllQuery, + out Expression> strAnyQuery, out Expression> strAllQuery) + { + var firstIntValue = searchItemsInt[0]; + intAnyQuery = e => e.IntArray.Contains(firstIntValue); + intAllQuery = e => e.IntArray.Contains(firstIntValue); + foreach (var item in searchItemsInt.Skip(1)) + { + intAnyQuery = intAnyQuery.OrAlso(e => e.IntArray.Contains(item)); + intAllQuery = intAllQuery.AndAlso(e => e.IntArray.Contains(item)); + } + + var firstStrValue = searchItemsStr[0]; + strAnyQuery = e => e.StrArray.Contains(firstStrValue); + strAllQuery = e => e.StrArray.Contains(firstStrValue); + foreach (var item in searchItemsInt.Skip(1)) + { + strAnyQuery = strAnyQuery.OrAlso(e => e.IntArray.Contains(item)); + strAllQuery = strAllQuery.AndAlso(e => e.IntArray.Contains(item)); + } + } + + + public void Setup() + { + DataPath = Directory.CreateTempSubdirectory().FullName; + Data = new BenchmarkEntity[N]; + for (int i = 0; i < N; i++) + { + Data[i] = new BenchmarkEntity + { + Id = Guid.NewGuid(), + IntProperty = i, + StringProperty = "ÇŞĞÜÖİöçşğüı" + i, + CreateDate = DateTime.UtcNow, + IntArray = Enumerable.Range(0, i).ToArray(), + StrArray = Enumerable.Range(0, i).Select(i => i.ToString()).ToArray() + }; + } + + SearchItemsInt = Enumerable.Range(0, 50).Select(i => i * 20).ToArray(); + SearchItemsStr = Enumerable.Range(0, 50).Select(i => i * 20).Select(i => i.ToString()).ToArray(); + SearchItemsIntJoined = string.Join(",", Enumerable.Range(0, 50).Select(i => i * 20)); + SearchItemsStrJoined = "'" + string.Join("','", Enumerable.Range(0, 50).Select(i => i * 20)) + "'"; + SearchStringProperties = Data.Take(N).Select(i => i.StringProperty).ToArray(); + SearchStringPropertiesJoined = "'" + string.Join("','", Data.Take(N).Select(i => i.StringProperty)) + "'"; + SearchIds = Data.Take(N).Select(i => i.Id).ToArray(); + SearchIdsJoined = "'" + string.Join("','", Data.Take(N).Select(i => i.Id)) + "'"; + } + + public void Cleanup() + { + Directory.Delete(DataPath, true); + } + + [GlobalSetup(Targets = new[] { + "ReindexerNet" + })] + public void ReindexerNetSetup() + { + Setup(); + var dbPath = Path.Combine(DataPath, "ReindexerEmbedded"); + if (Directory.Exists(dbPath)) + Directory.Delete(dbPath, true); + RxClient = new ReindexerEmbedded(dbPath); + RxClient.Connect(new ConnectionOptions { Engine = StorageEngine.LevelDb }); + RxClient.OpenNamespace("Entities"); + RxClient.TruncateNamespace("Entities"); + RxClient.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.Id), IsPk = true, IndexType = IndexType.Hash, FieldType = FieldType.String, IsDense = false }); + RxClient.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.IntProperty), IndexType = IndexType.Tree, FieldType = FieldType.Int, IsDense = false }); + RxClient.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.StringProperty), IndexType = IndexType.Hash, FieldType = FieldType.String, IsDense = false }); + RxClient.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.CreateDate), IndexType = IndexType.Hash, FieldType = FieldType.String, IsDense = false }); + RxClient.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.IntArray), IndexType = IndexType.Hash, FieldType = FieldType.Int, IsDense = false, IsArray = true }); + RxClient.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.StrArray), IndexType = IndexType.Hash, FieldType = FieldType.String, IsDense = false, IsArray = true }); + RxClient!.Insert("Entities", Data); + } + + [GlobalCleanup(Targets = new[] { + "ReindexerNet" + })] + public void ReindexerNetClean() + { + RxClient!.Dispose(); + Cleanup(); + } + + private sealed class SpanJsonSerializer : IReindexerSerializer + { + public SerializerType Type => SerializerType.Json; + + public T Deserialize(ReadOnlySpan bytes) + { + return SpanJson.JsonSerializer.Generic.Utf8.Deserialize(bytes); + } + + public ReadOnlySpan Serialize(T item) + { + return SpanJson.JsonSerializer.Generic.Utf8.Serialize(item); + } + } + + [GlobalSetup(Targets = new[] { + "ReindexerNetSpanJson" + })] + public void ReindexerNetSpanJsonSetup() + { + Setup(); + var dbPathSpanJson = Path.Combine(DataPath, "ReindexerEmbeddedSpanJson"); + if (Directory.Exists(dbPathSpanJson)) + Directory.Delete(dbPathSpanJson, true); + RxClientSpanJson = new ReindexerEmbedded(dbPathSpanJson, new SpanJsonSerializer()); + RxClientSpanJson.Connect(new ConnectionOptions { Engine = StorageEngine.LevelDb }); + RxClientSpanJson.OpenNamespace("Entities"); + RxClientSpanJson.TruncateNamespace("Entities"); + RxClientSpanJson.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.Id), IsPk = true, IndexType = IndexType.Hash, FieldType = FieldType.String, IsDense = true }); + RxClientSpanJson.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.IntProperty), IndexType = IndexType.Tree, FieldType = FieldType.Int, IsDense = true }); + RxClientSpanJson.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.StringProperty), IndexType = IndexType.Hash, FieldType = FieldType.String, IsDense = true }); + RxClientSpanJson.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.CreateDate), IndexType = IndexType.Hash, FieldType = FieldType.String, IsDense = true }); + RxClientSpanJson.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.IntArray), IndexType = IndexType.Hash, FieldType = FieldType.Int, IsDense = true, IsArray = true }); + RxClientSpanJson.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.StrArray), IndexType = IndexType.Hash, FieldType = FieldType.String, IsDense = true, IsArray = true }); + RxClientSpanJson!.Insert("Entities", Data); + } + + [GlobalCleanup(Targets = new[] { + "ReindexerNetSpanJson" + })] + public void ReindexerNetSpanJsonClean() + { + RxClientSpanJson!.Dispose(); + Cleanup(); + } + + [GlobalSetup(Targets = new[] { + "ReindexerNetSql" + })] + public void ReindexerNetSqlSetup() + { + Setup(); + var dbPath = Path.Combine(DataPath, "ReindexerEmbeddedSql"); + if (Directory.Exists(dbPath)) + Directory.Delete(dbPath, true); + RxClientSql = new ReindexerEmbedded(dbPath); + RxClientSql.Connect(new ConnectionOptions { Engine = StorageEngine.LevelDb }); + RxClientSql.OpenNamespace("Entities"); + RxClientSql.TruncateNamespace("Entities"); + RxClientSql.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.Id), IsPk = true, IndexType = IndexType.Hash, FieldType = FieldType.String, IsDense = false }); + RxClientSql.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.IntProperty), IndexType = IndexType.Tree, FieldType = FieldType.Int, IsDense = false }); + RxClientSql.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.StringProperty), IndexType = IndexType.Hash, FieldType = FieldType.String, IsDense = false }); + RxClientSql.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.CreateDate), IndexType = IndexType.Hash, FieldType = FieldType.String, IsDense = false }); + RxClientSql.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.IntArray), IndexType = IndexType.Hash, FieldType = FieldType.Int, IsDense = false, IsArray = true }); + RxClientSql.AddIndex("Entities", new Index { Name = nameof(BenchmarkEntity.StrArray), IndexType = IndexType.Hash, FieldType = FieldType.String, IsDense = false, IsArray = true }); + RxClientSql!.Insert("Entities", Data); + } + + [GlobalCleanup(Targets = new[] { + "ReindexerNetSql" + })] + public void ReindexerNetSqlClean() + { + RxClientSql!.Dispose(); + Cleanup(); + } + + [GlobalSetup(Targets = new[] { + "Cachalot" + })] + public void CachalotSetup() + { + Setup(); + var server = new Server.Server(new NodeConfig { DataPath = DataPath, IsPersistent = true, ClusterName = "embedded" }); + CaConnector = new Connector(new ClientConfig { IsPersistent = true }); + CaConnector.DeclareCollection("BenchmarkEntity"); + CaConnector.GetCollectionSchema("BenchmarkEntity").UseCompression = false; + CaDS = CaConnector!.DataSource("BenchmarkEntity"); + CaDS.PutMany(Data); + + //_caDS.Where(e => e.IntArray.Any(i => searchItemsInt.Contains(i))).ToList(), + //Doesn't support .Any or All methods right now, so combining queries with OR + GetArrayQueryExpressions(SearchItemsInt, SearchItemsStr, out IntAnyQuery, out IntAllQuery, out StrAnyQuery, out StrAllQuery); + } + + [GlobalCleanup(Targets = new[] { + "Cachalot" + })] + public void CachalotClean() + { + CaConnector!.Dispose(); + Cleanup(); + } + + [GlobalSetup(Targets = new[] { + "CachalotCompressed" + })] + public void CachalotCompressedSetup() + { + Setup(); + var server = new Server.Server(new NodeConfig { DataPath = DataPath, IsPersistent = true, ClusterName = "embedded" }); + CaConnectorCompressed = new Connector(new ClientConfig { IsPersistent = true }); + CaConnectorCompressed.DeclareCollection("BenchmarkEntity"); + CaConnectorCompressed.GetCollectionSchema("BenchmarkEntity").UseCompression = true; + CaDSCompressed = CaConnectorCompressed!.DataSource("BenchmarkEntity"); + CaDSCompressed.PutMany(Data); + + //Doesn't support .Any or All methods right now, so combining queries with OR + GetArrayQueryExpressions(SearchItemsInt, SearchItemsStr, out IntAnyQuery, out IntAllQuery, out StrAnyQuery, out StrAllQuery); + } + + [GlobalCleanup(Targets = new[] { + "CachalotCompressed" + })] + public void CachalotCompressedClean() + { + CaConnectorCompressed!.Dispose(); + Cleanup(); + } + + [GlobalSetup(Targets = new[] { + "CachalotMemory" + })] + public void CachalotMemorySetup() + { + Setup(); + var server = new Server.Server(new NodeConfig { DataPath = Path.Combine(DataPath, "CachalotMemory"), IsPersistent = false, ClusterName = "embedded" }); + CaConnectorMemory = new Connector(new ClientConfig { IsPersistent = false }); + CaConnectorMemory.DeclareCollection("BenchmarkEntity"); + CaConnectorMemory.GetCollectionSchema("BenchmarkEntity").UseCompression = false; + CaDSMemory = CaConnectorMemory!.DataSource("BenchmarkEntity"); + CaDSMemory.PutMany(Data); + + //Doesn't support .Any or All methods right now, so combining queries with OR + GetArrayQueryExpressions(SearchItemsInt, SearchItemsStr, out IntAnyQuery, out IntAllQuery, out StrAnyQuery, out StrAllQuery); + } + + [GlobalCleanup(Targets = new[] { + "CachalotMemory" + })] + public void CachalotMemoryClean() + { + CaConnectorMemory!.Dispose(); + Cleanup(); + } + + [GlobalSetup(Targets = new[] { + "LiteDb" + })] + public void LiteDBSetup() + { + Setup(); + _liteDb = new LiteDatabase(Path.Combine(DataPath, "LiteDB")); + LiteColl = _liteDb.GetCollection("Entities"); + LiteColl.EnsureIndex(e => e.Id, true); + LiteColl.EnsureIndex(e => e.IntProperty); + LiteColl.EnsureIndex(e => e.StringProperty); + LiteColl.EnsureIndex(e => e.CreateDate); + LiteColl.EnsureIndex(e => e.IntArray); + LiteColl.EnsureIndex(e => e.StrArray); + LiteColl.Upsert(Data); + + //Doesn't support .Any or All methods right now, so combining queries with OR + GetArrayQueryExpressions(SearchItemsInt, SearchItemsStr, out IntAnyQuery, out IntAllQuery, out StrAnyQuery, out StrAllQuery); + } + + [GlobalCleanup(Targets = new[] { + "LiteDb" + })] + public void LiteDBClean() + { + _liteDb!.Dispose(); + Cleanup(); + } + + [GlobalSetup(Targets = new[] { + "LiteDbMemory" + })] + public void LiteDbMemorySetup() + { + Setup(); + _liteDbMemory = new LiteDatabase(":memory:"); + LiteCollMemory = _liteDbMemory.GetCollection("Entities"); + LiteCollMemory.EnsureIndex(e => e.Id, true); + LiteCollMemory.EnsureIndex(e => e.IntProperty); + LiteCollMemory.EnsureIndex(e => e.StringProperty); + LiteCollMemory.EnsureIndex(e => e.CreateDate); + LiteCollMemory.EnsureIndex(e => e.IntArray); + LiteCollMemory.EnsureIndex(e => e.StrArray); + LiteCollMemory.Upsert(Data); + + //Doesn't support .Any or All methods right now, so combining queries with OR + GetArrayQueryExpressions(SearchItemsInt, SearchItemsStr, out IntAnyQuery, out IntAllQuery, out StrAnyQuery, out StrAllQuery); + } + + [GlobalCleanup(Targets = new[] { + "LiteDbMemory" + })] + public void LiteDbMemoryClean() + { + _liteDbMemory!.Dispose(); + Cleanup(); + } + + [GlobalSetup(Targets = new[] { + "Realm" + })] + public void RealmSetup() + { + Setup(); + RealmCli = Realm.GetInstance(new RealmConfiguration(Path.Combine(DataPath, "Realms"))); + RealmCli.Write(() => + { + RealmCli.Add(Data.Select(e => (BenchmarkRealmEntity)e), update: false); + }); + } + + [GlobalCleanup(Targets = new[] { + "Realm" + })] + public void RealmClean() + { + RealmCli.Dispose(); + Realm.DeleteRealm(new RealmConfiguration(Path.Combine(DataPath, "Realm"))); + Cleanup(); + } + #endregion + +} \ No newline at end of file diff --git a/Tests/ReindexerNet.EmbeddedBenchmarks/SelectMultipleHash.cs b/Tests/ReindexerNet.EmbeddedBenchmarks/SelectMultipleHash.cs new file mode 100644 index 0000000..e8c1ccd --- /dev/null +++ b/Tests/ReindexerNet.EmbeddedBenchmarks/SelectMultipleHash.cs @@ -0,0 +1,88 @@ +using BenchmarkDotNet.Attributes; +using Realms; +using ReindexerNetBenchmark.EmbeddedBenchmarks; +using ReindexerNet; + +namespace ReindexerNetBenchmark; + +public class SelectMultipleHash : SelectBenchmarkBase +{ + [Benchmark] + public IList ReindexerNet() + { + var result = new List + { + RxClient.Execute("Entities", q => q.WhereString("StringProperty", Condition.SET, SearchStringProperties)).CaptureResult() + }; + return result; + } + + [Benchmark] + public IList ReindexerNetSpanJson() + { + var result = new List + { + RxClientSpanJson.Execute("Entities", q => q.WhereString("StringProperty", Condition.SET, SearchStringProperties)).CaptureResult() + }; + return result; + } + + [Benchmark] + public IList ReindexerNetSql() + { + var result = new List + { + RxClientSql.ExecuteSql($"SELECT * FROM Entities WHERE StringProperty IN ({SearchStringPropertiesJoined})").CaptureResult() + }; + return result; + } + + [Benchmark] + public IList Cachalot() + { + var result = new List(); + result.Add(CaDS.Where(e => SearchStringProperties.Contains(e.StringProperty)).AsEnumerable().CaptureResult()); + return result; + } + + [Benchmark] + public IList CachalotMemory() + { + var result = new List(); + result.Add(CaDSMemory.Where(e => SearchStringProperties.Contains(e.StringProperty)).AsEnumerable().CaptureResult()); + return result; + } + + [Benchmark] + public IList CachalotCompressed() + { + var result = new List(); + result.Add(CaDSCompressed.Where(e => SearchStringProperties.Contains(e.StringProperty)).AsEnumerable().CaptureResult()); + return result; + } + + [Benchmark] + public IList LiteDb() + { + var result = new List(); + result.Add(LiteColl.Query().Where(e => SearchStringProperties.Contains(e.StringProperty)).ToEnumerable().CaptureResult()); + return result; + } + + [Benchmark] + public IList LiteDbMemory() + { + var result = new List(); + result.Add(LiteCollMemory.Query().Where(e => SearchStringProperties.Contains(e.StringProperty)).ToEnumerable().CaptureResult()); + return result; + } + + [Benchmark] + public IList Realm() + { + var result = new List(); + var filter = string.Join(" OR ", SearchStringProperties.Select(i => $"(StringProperty == '{i}')")); + result.Add(RealmCli.All().Filter(filter).CaptureResult()); + return result; + } +} diff --git a/Tests/ReindexerNet.EmbeddedBenchmarks/SelectMultiplePK.cs b/Tests/ReindexerNet.EmbeddedBenchmarks/SelectMultiplePK.cs new file mode 100644 index 0000000..c10d2a0 --- /dev/null +++ b/Tests/ReindexerNet.EmbeddedBenchmarks/SelectMultiplePK.cs @@ -0,0 +1,82 @@ +using BenchmarkDotNet.Attributes; +using Realms; +using ReindexerNetBenchmark.EmbeddedBenchmarks; +using ReindexerNet; + +namespace ReindexerNetBenchmark; + +public class SelectMultiplePK : SelectBenchmarkBase +{ + [Benchmark] + public IList ReindexerNet() + { + var result = new List(); + result.Add(RxClient.Execute("Entities", q => q.WhereGuid("Id", Condition.SET, SearchIds)).CaptureResult()); + return result; + } + + [Benchmark] + public IList ReindexerNetSpanJson() + { + var result = new List(); + result.Add(RxClientSpanJson.Execute("Entities", q => q.WhereGuid("Id", Condition.SET, SearchIds)).CaptureResult()); + return result; + } + + [Benchmark] + public IList ReindexerNetSql() + { + var result = new List(); + result.Add(RxClientSql.ExecuteSql($"SELECT * FROM Entities WHERE Id IN ({SearchIdsJoined})").CaptureResult()); + return result; + } + + [Benchmark] + public IList Cachalot() + { + var result = new List(); + result.Add(CaDS.Where(e => SearchIds.Contains(e.Id)).AsEnumerable().CaptureResult()); + return result; + } + + [Benchmark] + public IList CachalotMemory() + { + var result = new List(); + result.Add(CaDSMemory.Where(e => SearchIds.Contains(e.Id)).AsEnumerable().CaptureResult()); + return result; + } + + [Benchmark] + public IList CachalotCompressed() + { + var result = new List(); + result.Add(CaDSCompressed.Where(e => SearchIds.Contains(e.Id)).AsEnumerable().CaptureResult()); + return result; + } + + [Benchmark] + public IList LiteDb() + { + var result = new List(); + result.Add(LiteColl.Query().Where(e => SearchIds.Contains(e.Id)).ToEnumerable().CaptureResult()); + return result; + } + + [Benchmark] + public IList LiteDbMemory() + { + var result = new List(); + result.Add(LiteCollMemory.Query().Where(e => SearchIds.Contains(e.Id)).ToEnumerable().CaptureResult()); + return result; + } + + [Benchmark] + public IList Realm() + { + var result = new List(); + var filter = string.Join(" OR ", Data.Take(N).Select(i => $"(Id == uuid({i.Id}))")); + result.Add(RealmCli.All().Filter(filter).CaptureResult()); + return result; + } +} diff --git a/Tests/ReindexerNet.EmbeddedBenchmarks/SelectRange.cs b/Tests/ReindexerNet.EmbeddedBenchmarks/SelectRange.cs new file mode 100644 index 0000000..5cb1f9b --- /dev/null +++ b/Tests/ReindexerNet.EmbeddedBenchmarks/SelectRange.cs @@ -0,0 +1,99 @@ +using BenchmarkDotNet.Attributes; +using ReindexerNetBenchmark.EmbeddedBenchmarks; +using ReindexerNet; + +namespace ReindexerNetBenchmark; + +public class SelectRange: SelectBenchmarkBase +{ + [Benchmark] + public IList ReindexerNet() + { + var result = new List(); + var entity = Data[N / 2]; + result.Add(RxClient.Execute("Entities", q => q.WhereInt32("IntProperty", Condition.LT, entity.IntProperty ?? 0)).CaptureResult()); + result.Add(RxClient.Execute("Entities", q => q.WhereInt32("IntProperty", Condition.GE, entity.IntProperty ?? 0)).CaptureResult()); + return result; + } + + [Benchmark] + public IList ReindexerNetSpanJson() + { + var result = new List(); + var entity = Data[N / 2]; + result.Add(RxClientSpanJson.Execute("Entities", q => q.WhereInt32("IntProperty", Condition.LT, entity.IntProperty ?? 0)).CaptureResult()); + result.Add(RxClientSpanJson.Execute("Entities", q => q.WhereInt32("IntProperty", Condition.GE, entity.IntProperty ?? 0)).CaptureResult()); + return result; + } + + [Benchmark] + public IList ReindexerNetSql() + { + var result = new List(); + var entity = Data[N / 2]; + result.Add(RxClientSql.ExecuteSql($"SELECT * FROM Entities WHERE IntProperty < {entity.IntProperty}").CaptureResult()); + result.Add(RxClientSql.ExecuteSql($"SELECT * FROM Entities WHERE IntProperty >= {entity.IntProperty}").CaptureResult()); + return result; + } + + [Benchmark] + public IList Cachalot() + { + var result = new List(); + var entity = Data[N / 2]; + result.Add(CaDS.Where(e => e.IntProperty < entity.IntProperty).AsEnumerable().CaptureResult()); + result.Add(CaDS.Where(e => e.IntProperty >= entity.IntProperty).AsEnumerable().CaptureResult()); + return result; + } + + [Benchmark] + public IList CachalotMemory() + { + var result = new List(); + var entity = Data[N / 2]; + result.Add(CaDSMemory.Where(e => e.IntProperty < entity.IntProperty).AsEnumerable().CaptureResult()); + result.Add(CaDSMemory.Where(e => e.IntProperty >= entity.IntProperty).AsEnumerable().CaptureResult()); + return result; + } + + [Benchmark] + public IList CachalotCompressed() + { + var result = new List(); + var entity = Data[N / 2]; + result.Add(CaDSCompressed.Where(e => e.IntProperty < entity.IntProperty).AsEnumerable().CaptureResult()); + result.Add(CaDSCompressed.Where(e => e.IntProperty >= entity.IntProperty).AsEnumerable().CaptureResult()); + return result; + } + + [Benchmark] + public IList LiteDb() + { + var result = new List(); + var entity = Data[N / 2]; + result.Add(LiteColl.Query().Where(e => e.IntProperty < entity.IntProperty).ToEnumerable().CaptureResult()); + result.Add(LiteColl.Query().Where(e => e.IntProperty >= entity.IntProperty).ToEnumerable().CaptureResult()); + return result; + } + + [Benchmark] + public IList LiteDbMemory() + { + var result = new List(); + var entity = Data[N / 2]; + result.Add(LiteCollMemory.Query().Where(e => e.IntProperty < entity.IntProperty).ToEnumerable().CaptureResult()); + result.Add(LiteCollMemory.Query().Where(e => e.IntProperty >= entity.IntProperty).ToEnumerable().CaptureResult()); + return result; + } + + [Benchmark] + public IList Realm() + { + var result = new List(); + var entity = Data[N / 2]; + result.Add(RealmCli.All().Where(e => e.IntProperty < entity.IntProperty).CaptureResult()); + result.Add(RealmCli.All().Where(e => e.IntProperty >= entity.IntProperty).CaptureResult()); + + return result; + } +} diff --git a/Tests/ReindexerNet.EmbeddedBenchmarks/SelectSingleHash.cs b/Tests/ReindexerNet.EmbeddedBenchmarks/SelectSingleHash.cs new file mode 100644 index 0000000..4c21e4f --- /dev/null +++ b/Tests/ReindexerNet.EmbeddedBenchmarks/SelectSingleHash.cs @@ -0,0 +1,115 @@ +using BenchmarkDotNet.Attributes; +using ReindexerNetBenchmark.EmbeddedBenchmarks; +using ReindexerNet; + +namespace ReindexerNetBenchmark; + +public class SelectSingleHash : SelectBenchmarkBase +{ + [Benchmark] + public IList ReindexerNet() + { + var result = new List(); + for (int i = 0; i < N; i++) + { + result.Add(RxClient.Execute("Entities", q => q.WhereString("StringProperty", Condition.EQ, Data[i].StringProperty).Limit(1)).CaptureResult()); + } + + return result; + } + + [Benchmark] + public IList ReindexerNetSpanJson() + { + var result = new List(); + for (int i = 0; i < N; i++) + { + result.Add(RxClientSpanJson.Execute("Entities", q => q.WhereString("StringProperty", Condition.EQ, Data[i].StringProperty).Limit(1)).CaptureResult()); + } + return result; + } + + [Benchmark] + public IList ReindexerNetSql() + { + var result = new List(); + for (int i = 0; i < N; i++) + { + result.Add(RxClientSql.ExecuteSql($"SELECT * FROM Entities WHERE StringProperty = '{Data[i].StringProperty}' LIMIT 1").CaptureResult()); + } + + return result; + } + + [Benchmark] + public IList Cachalot() + { + var result = new List(); + for (int i = 0; i < N; i++) + { + var str = Data[i].StringProperty; + result.Add(CaDS.FirstOrDefault(e => e.StringProperty == str).CaptureResult()); + } + return result; + } + + [Benchmark] + public IList CachalotMemory() + { + var result = new List(); + for (int i = 0; i < N; i++) + { + var str = Data[i].StringProperty; + result.Add(CaDSMemory.FirstOrDefault(e => e.StringProperty == str).CaptureResult()); + } + return result; + } + + [Benchmark] + public IList CachalotCompressed() + { + var result = new List(); + for (int i = 0; i < N; i++) + { + var str = Data[i].StringProperty; + result.Add(CaDSCompressed.FirstOrDefault(e => e.StringProperty == str).CaptureResult()); + } + return result; + } + + [Benchmark] + public IList LiteDb() + { + var result = new List(); + for (int i = 0; i < N; i++) + { + var str = Data[i].StringProperty; + result.Add(LiteColl.Query().Where(e => e.StringProperty == str).FirstOrDefault().CaptureResult()); + } + return result; + } + + [Benchmark] + public IList LiteDbMemory() + { + var result = new List(); + for (int i = 0; i < N; i++) + { + var str = Data[i].StringProperty; + result.Add(LiteCollMemory.Query().Where(e => e.StringProperty == str).FirstOrDefault().CaptureResult()); + } + return result; + } + + [Benchmark] + public IList Realm() + { + var result = new List(); + for (int i = 0; i < N; i++) + { + var str = Data[i].StringProperty; + result.Add(RealmCli.All().Where(e => e.StringProperty == str).FirstOrDefault().CaptureResult()); + } + return result; + } +} diff --git a/Tests/ReindexerNet.EmbeddedBenchmarks/SelectSingleHashParallel.cs b/Tests/ReindexerNet.EmbeddedBenchmarks/SelectSingleHashParallel.cs new file mode 100644 index 0000000..0d3337b --- /dev/null +++ b/Tests/ReindexerNet.EmbeddedBenchmarks/SelectSingleHashParallel.cs @@ -0,0 +1,118 @@ +using BenchmarkDotNet.Attributes; +using Realms; +using ReindexerNetBenchmark.EmbeddedBenchmarks; +using System.Collections.Concurrent; +using ReindexerNet; + +namespace ReindexerNetBenchmark; + +public class SelectSingleHashParallel : SelectBenchmarkBase +{ + + + [Benchmark] + public ConcurrentBag ReindexerNet() + { + var result = new ConcurrentBag(); + Parallel.For(0, N, i => + { + result.Add(RxClient.Execute("Entities", q => q.WhereString("StringProperty", Condition.EQ, Data[i].StringProperty).Limit(1)).CaptureResult()); + }); + return result; + } + + [Benchmark] + public ConcurrentBag ReindexerNetSpanJson() + { + var result = new ConcurrentBag(); + Parallel.For(0, N, i => + { + result.Add(RxClientSpanJson.Execute("Entities", q => q.WhereString("StringProperty", Condition.EQ, Data[i].StringProperty).Limit(1)).CaptureResult()); + }); + return result; + } + + [Benchmark] + public ConcurrentBag ReindexerNetSql() + { + var result = new ConcurrentBag(); + Parallel.For(0, N, i => + { + result.Add(RxClientSql.ExecuteSql($"SELECT * FROM Entities WHERE StringProperty = '{Data[i].StringProperty}' LIMIT 1").CaptureResult()); + }); + return result; + } + + [Benchmark] + public ConcurrentBag Cachalot() + { + var result = new ConcurrentBag(); + Parallel.For(0, N, i => + { + var str = Data[i].StringProperty; + result.Add(CaDS.FirstOrDefault(e => e.StringProperty == str).CaptureResult()); + }); + return result; + } + + [Benchmark] + public ConcurrentBag CachalotMemory() + { + var result = new ConcurrentBag(); + Parallel.For(0, N, i => + { + var str = Data[i].StringProperty; + result.Add(CaDSMemory.FirstOrDefault(e => e.StringProperty == str).CaptureResult()); + }); + return result; + } + + [Benchmark] + public ConcurrentBag CachalotCompressed() + { + var result = new ConcurrentBag(); + Parallel.For(0, N, i => + { + var str = Data[i].StringProperty; + result.Add(CaDSCompressed.FirstOrDefault(e => e.StringProperty == str).CaptureResult()); + }); + return result; + } + + [Benchmark] + public ConcurrentBag LiteDb() + { + var result = new ConcurrentBag(); + Parallel.For(0, N, i => + { + var str = Data[i].StringProperty; + result.Add(LiteColl.Query().Where(e => e.StringProperty == str).FirstOrDefault().CaptureResult()); + }); + return result; + } + + [Benchmark] + public ConcurrentBag LiteDbMemory() + { + var result = new ConcurrentBag(); + Parallel.For(0, N, i => + { + var str = Data[i].StringProperty; + result.Add(LiteCollMemory.Query().Where(e => e.StringProperty == str).FirstOrDefault().CaptureResult()); + }); + return result; + } + + [Benchmark] + public ConcurrentBag Realm() + { + var result = new ConcurrentBag(); + Parallel.For(0, N, i => + { + using var realm = Realms.Realm.GetInstance(new RealmConfiguration(Path.Combine(DataPath, "Realms"))); + var str = Data[i].StringProperty; + result.Add(realm.All().Where(e => e.StringProperty == str).FirstOrDefault().CaptureResult()); + }); + return result; + } +} diff --git a/Tests/ReindexerNet.EmbeddedBenchmarks/SelectSinglePK.cs b/Tests/ReindexerNet.EmbeddedBenchmarks/SelectSinglePK.cs new file mode 100644 index 0000000..f6d4242 --- /dev/null +++ b/Tests/ReindexerNet.EmbeddedBenchmarks/SelectSinglePK.cs @@ -0,0 +1,116 @@ +using BenchmarkDotNet.Attributes; +using ReindexerNetBenchmark.EmbeddedBenchmarks; +using ReindexerNet; + +namespace ReindexerNetBenchmark; + +public class SelectSinglePK: SelectBenchmarkBase +{ + [Benchmark] + public IList ReindexerNet() + { + var result = new List(); + for (int i = 0; i < N; i++) + { + result.Add(RxClient.Execute("Entities", q => q.Limit(1).WhereGuid("Id", Condition.EQ, Data[i].Id)).CaptureResult()); + } + + return result; + } + + [Benchmark] + public IList ReindexerNetSpanJson() + { + var result = new List(); + for (int i = 0; i < N; i++) + { + result.Add(RxClientSpanJson.Execute("Entities", q => q.Limit(1).WhereGuid("Id", Condition.EQ, Data[i].Id)).CaptureResult()); + } + return result; + } + + [Benchmark] + public IList ReindexerNetSql() + { + var result = new List(); + for (int i = 0; i < N; i++) + { + result.Add(RxClientSql.ExecuteSql($"SELECT * FROM Entities WHERE Id = '{Data[i].Id}' LIMIT 1").CaptureResult()); + } + + return result; + } + + [Benchmark] + public IList Cachalot() + { + var result = new List(); + for (int i = 0; i < N; i++) + { + var id = Data[i].Id; + result.Add(CaDS[id].CaptureResult()); + } + + return result; + } + + [Benchmark] + public IList CachalotMemory() + { + var result = new List(); + for (int i = 0; i < N; i++) + { + var id = Data[i].Id; + result.Add(CaDSMemory[id].CaptureResult()); + } + + return result; + } + + [Benchmark] + public IList CachalotCompressed() + { + var result = new List(); + for (int i = 0; i < N; i++) + { + var id = Data[i].Id; + result.Add(CaDSCompressed[id].CaptureResult()); + } + return result; + } + + [Benchmark] + public IList LiteDb() + { + var result = new List(); + for (int i = 0; i < N; i++) + { + var id = Data[i].Id; + result.Add(LiteColl.FindById(id).CaptureResult()); + } + return result; + } + + [Benchmark] + public IList LiteDbMemory() + { + var result = new List(); + for (int i = 0; i < N; i++) + { + var id = Data[i].Id; + result.Add(LiteCollMemory.FindById(id).CaptureResult()); + } + return result; + } + + [Benchmark] + public IList Realm() + { + var result = new List(); + for (int i = 0; i < N; i++) + { + result.Add(RealmCli.Find(Data[i].Id)!.CaptureResult()); + } + return result; + } +} diff --git a/Tests/ReindexerNet.EmbeddedBenchmarks/UpsertBenchmark.cs b/Tests/ReindexerNet.EmbeddedBenchmarks/UpsertBenchmark.cs new file mode 100644 index 0000000..23d14c9 --- /dev/null +++ b/Tests/ReindexerNet.EmbeddedBenchmarks/UpsertBenchmark.cs @@ -0,0 +1,127 @@ +using BenchmarkDotNet.Attributes; +using Cachalot.Linq; +using Client.Interface; +using LiteDB; +using Realms; +using ReindexerNet.Embedded; +using Server; +using ReindexerNet; +using Index = ReindexerNet.Index; +using IndexType = ReindexerNet.IndexType; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Running; +using System.Reflection; + +namespace ReindexerNetBenchmark.EmbeddedBenchmarks; + +//[Config(typeof(AntiVirusFriendlyConfig))] +[SimpleJob(launchCount: 0, warmupCount: 0, iterationCount: 1)] +[MemoryDiagnoser()] +[CustomCategoryDiscoverer] +[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByParams, BenchmarkLogicalGroupRule.ByCategory)] +[PlainExporter] +[Orderer(BenchmarkDotNet.Order.SummaryOrderPolicy.FastestToSlowest)] +public class UpsertBenchmark: InsertBenchmark +{ + public override void ReindexerNetSetup() + { + base.ReindexerNetSetup(); + base.ReindexerNet(); + } + + public override void ReindexerNetDenseSetup() + { + base.ReindexerNetDenseSetup(); + base.ReindexerNetDense(); + } + + public override void CachalotSetup() + { + base.CachalotSetup(); + base.Cachalot(); + } + + public override void CachalotCompressedSetup() + { + base.CachalotCompressedSetup(); + base.CachalotCompressed(); + } + + public override void CachalotOnlyMemorySetup() + { + base.CachalotOnlyMemorySetup(); + base.CachalotOnlyMemory(); + } + + public override void LiteDbSetup() + { + base.LiteDbSetup(); + base.LiteDb(); + } + + public override void LiteDbMemorySetup() + { + base.LiteDbMemorySetup(); + base.LiteDbMemory(); + } + + public override void RealmSetup() + { + base.RealmSetup(); + base.Realm(); + } + + [Benchmark] + public override void ReindexerNet() + { + _rxClient!.Upsert("Entities", _data); + } + + [Benchmark] + public override void ReindexerNetDense() + { + _rxClientDense!.Upsert("Entities", _data); + } + + [Benchmark] + public override void Cachalot() + { + var entities = _caConnector!.DataSource("BenchmarkEntity"); + entities.PutMany(_data); + } + + [Benchmark] + public override void CachalotCompressed() + { + var entities = _caConnectorCompressed!.DataSource("BenchmarkEntity"); + entities.PutMany(_data); + } + + [Benchmark] + public override void CachalotOnlyMemory() + { + var entities = _caMemoryConnector!.DataSource("BenchmarkEntity"); + entities.PutMany(_data); + } + + [Benchmark] + public override void LiteDb() + { + _liteColl.Upsert(_data); + } + + [Benchmark] + public override void LiteDbMemory() + { + _liteCollMemory.Upsert(_data); + } + + [Benchmark] + public override void Realm() + { + _realm.Write(() => + { + _realm.Add(_data.Select(e => (BenchmarkRealmEntity)e), update: true); + }); + } +} \ No newline at end of file diff --git a/src/ReindexerNet.Embedded/Internal/ArrayBufferWriter.cs b/src/ReindexerNet.Embedded/Internal/ArrayBufferWriter.cs new file mode 100644 index 0000000..c7f5ca1 --- /dev/null +++ b/src/ReindexerNet.Embedded/Internal/ArrayBufferWriter.cs @@ -0,0 +1,195 @@ +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace ReindexerNet.Embedded.Internal; + +internal class ArrayBufferWriter : IBufferWriter, IDisposable +{ + private byte[] _rentedBuffer; + private int _written; + private long _committed; + + private const int MinimumBufferSize = 256; + + public ArrayBufferWriter(int initialCapacity = MinimumBufferSize) + { + if (initialCapacity <= 0) + throw new ArgumentException(nameof(initialCapacity)); + + _rentedBuffer = ArrayPool.Shared.Rent(initialCapacity); + _written = 0; + _committed = 0; + } + + public Memory OutputAsMemory + { + get + { + CheckIfDisposed(); + + return _rentedBuffer.AsMemory(0, _written); + } + } + + public Span OutputAsSpan + { + get + { + CheckIfDisposed(); + + return _rentedBuffer.AsSpan(0, _written); + } + } + + public int BytesWritten + { + get + { + CheckIfDisposed(); + + return _written; + } + } + + public long BytesCommitted + { + get + { + CheckIfDisposed(); + + return _committed; + } + } + + public void Clear() + { + CheckIfDisposed(); + + ClearHelper(); + } + + private void ClearHelper() + { + _rentedBuffer.AsSpan(0, _written).Clear(); + _written = 0; + } + + public async Task CopyToAsync(Stream stream, CancellationToken cancellationToken = default) + { + CheckIfDisposed(); + + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + await stream.WriteAsync(_rentedBuffer, 0, _written, cancellationToken).ConfigureAwait(false); + _committed += _written; + + ClearHelper(); + } + + public void CopyTo(Stream stream) + { + CheckIfDisposed(); + + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + stream.Write(_rentedBuffer, 0, _written); + _committed += _written; + + ClearHelper(); + } + + public void Advance(int count) + { + CheckIfDisposed(); + + if (count < 0) + throw new ArgumentException(nameof(count)); + + if (_written > _rentedBuffer.Length - count) + throw new InvalidOperationException("Cannot advance past the end of the buffer."); + + _written += count; + } + + // Returns the rented buffer back to the pool + public void Dispose() + { + if (_rentedBuffer == null) + { + return; + } + + ArrayPool.Shared.Return(_rentedBuffer, clearArray: true); + _rentedBuffer = null; + _written = 0; + } + + private void CheckIfDisposed() + { + if (_rentedBuffer == null) + throw new ObjectDisposedException(nameof(ArrayBufferWriter)); + } + + public Memory GetMemory(int sizeHint = 0) + { + CheckIfDisposed(); + + if (sizeHint < 0) + throw new ArgumentException(nameof(sizeHint)); + + CheckAndResizeBuffer(sizeHint); + return _rentedBuffer.AsMemory(_written); + } + + public Span GetSpan(int sizeHint = 0) + { + CheckIfDisposed(); + + if (sizeHint < 0) + throw new ArgumentException(nameof(sizeHint)); + + CheckAndResizeBuffer(sizeHint); + return _rentedBuffer.AsSpan(_written); + } + + private void CheckAndResizeBuffer(int sizeHint) + { + Debug.Assert(sizeHint >= 0); + + if (sizeHint == 0) + { + sizeHint = MinimumBufferSize; + } + + int availableSpace = _rentedBuffer.Length - _written; + + if (sizeHint > availableSpace) + { + int growBy = sizeHint > _rentedBuffer.Length ? sizeHint : _rentedBuffer.Length; + + int newSize = checked(_rentedBuffer.Length + growBy); + + byte[] oldBuffer = _rentedBuffer; + + _rentedBuffer = ArrayPool.Shared.Rent(newSize); + + Debug.Assert(oldBuffer.Length >= _written); + Debug.Assert(_rentedBuffer.Length >= _written); + + oldBuffer.AsSpan(0, _written).CopyTo(_rentedBuffer); + ArrayPool.Shared.Return(oldBuffer, clearArray: true); + } + + Debug.Assert(_rentedBuffer.Length - _written > 0); + Debug.Assert(_rentedBuffer.Length - _written >= sizeHint); + } +}