Skip to content

Commit 34a0a01

Browse files
authored
Merge pull request #8 from yv989c/develop
Feature/benchmarks (#7)
2 parents 81f63a7 + b7b65f6 commit 34a0a01

File tree

8 files changed

+322
-6
lines changed

8 files changed

+322
-6
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.0.32014.148
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QueryableValues.SqlServer.Benchmarks", "benchmarks\QueryableValues.SqlServer.Benchmarks\QueryableValues.SqlServer.Benchmarks.csproj", "{7C293C8A-107E-4C94-B046-7753938A4CD8}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|Any CPU = Debug|Any CPU
11+
Release|Any CPU = Release|Any CPU
12+
EndGlobalSection
13+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
14+
{7C293C8A-107E-4C94-B046-7753938A4CD8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15+
{7C293C8A-107E-4C94-B046-7753938A4CD8}.Debug|Any CPU.Build.0 = Debug|Any CPU
16+
{7C293C8A-107E-4C94-B046-7753938A4CD8}.Release|Any CPU.ActiveCfg = Release|Any CPU
17+
{7C293C8A-107E-4C94-B046-7753938A4CD8}.Release|Any CPU.Build.0 = Release|Any CPU
18+
EndGlobalSection
19+
GlobalSection(SolutionProperties) = preSolution
20+
HideSolutionNode = FALSE
21+
EndGlobalSection
22+
GlobalSection(ExtensibilityGlobals) = postSolution
23+
SolutionGuid = {13B09C6A-3CA7-41E5-B059-AE5616B18EB6}
24+
EndGlobalSection
25+
EndGlobal

README.md

Lines changed: 110 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,110 @@ The `AsQueryableValues` extension method is intended for queries that are depend
1919

2020
It provides a solution to the following long standing [EF Core issue](https://github.com/dotnet/efcore/issues/13617) and enables other currently unsupported scenarios; like the ability to efficiently create joins with in-memory data.
2121

22-
## Getting Started
22+
## Benchmarks
23+
The following [benchmarks] consist of EF Core queries that have a dependency on a random sequence of [Int32] and [Guid] values via the `Contains` LINQ method. It shows the performance differences between not using and using QueryableValues.
2324

24-
### Installation
25+
### Benchmarked Libraries
26+
| Package | Version |
27+
| ------- |:-------:|
28+
| Microsoft.EntityFrameworkCore.SqlServer | 6.0.1 |
29+
| BlazarTech.QueryableValues.SqlServer | 6.3.0 |
30+
31+
### BenchmarkDotNet Configuration and System Specs
32+
```
33+
BenchmarkDotNet=v0.13.1, OS=Windows 10.0.19042.1466 (20H2/October2020Update)
34+
Intel Core i7-8650U CPU 1.90GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores
35+
.NET SDK=6.0.101
36+
[Host] : .NET 6.0.1 (6.0.121.56705), X64 RyuJIT
37+
Job-GMTUEM : .NET 6.0.1 (6.0.121.56705), X64 RyuJIT
38+
39+
Server=True InvocationCount=200 IterationCount=25
40+
RunStrategy=Monitoring UnrollFactor=1 WarmupCount=1
41+
```
42+
### SQL Server Instance Specs
43+
```
44+
Microsoft SQL Server 2017 (RTM-GDR) (KB4583456) - 14.0.2037.2 (X64)
45+
Nov 2 2020 19:19:59
46+
Copyright (C) 2017 Microsoft Corporation
47+
Express Edition (64-bit) on Windows 10 Pro 10.0 <X64> (Build 19042: ) (Hypervisor)
48+
```
49+
- The SQL Server instance was running in the same system where the benchmark was executed.
50+
- Shared Memory is the only network protocol that's enabled on this instance.
51+
52+
53+
### Results for Int32
54+
55+
![Benchmarks Int32 Values][BenchmarksInt32]
56+
57+
<details>
58+
59+
| Method | Values | Mean (us) | Error (us) | Std Dev (us) | Median (us) | Ratio | RatioSD | Allocated |
60+
|---------|--------|--------------|--------------|----------------|---------------|-------|---------|-----------|
61+
| Without | 2 | 921.20 | 31.30 | 41.78 | 903.80 | 1.00 | 0.00 | 20 KB |
62+
| With | 2 | 734.30 | 45.28 | 60.44 | 696.10 | 0.80 | 0.04 | 51 KB |
63+
| Without | 4 | 997.80 | 31.79 | 42.44 | 981.10 | 1.00 | 0.00 | 21 KB |
64+
| With | 4 | 779.40 | 47.22 | 63.04 | 738.70 | 0.78 | 0.05 | 51 KB |
65+
| Without | 8 | 1,081.00 | 31.26 | 41.74 | 1,061.30 | 1.00 | 0.00 | 21 KB |
66+
| With | 8 | 814.20 | 47.34 | 63.20 | 775.70 | 0.75 | 0.04 | 51 KB |
67+
| Without | 16 | 1,331.70 | 88.81 | 118.56 | 1,283.40 | 1.00 | 0.00 | 23 KB |
68+
| With | 16 | 872.70 | 42.46 | 56.68 | 840.30 | 0.66 | 0.06 | 52 KB |
69+
| Without | 32 | 1,731.40 | 40.59 | 54.18 | 1,732.60 | 1.00 | 0.00 | 26 KB |
70+
| With | 32 | 1,006.00 | 47.61 | 63.56 | 973.60 | 0.58 | 0.03 | 53 KB |
71+
| Without | 64 | 2,615.40 | 103.77 | 138.53 | 2,540.20 | 1.00 | 0.00 | 31 KB |
72+
| With | 64 | 1,264.20 | 36.95 | 49.33 | 1,239.90 | 0.48 | 0.03 | 55 KB |
73+
| Without | 128 | 5,687.30 | 200.05 | 267.06 | 5,588.20 | 1.00 | 0.00 | 41 KB |
74+
| With | 128 | 1,917.00 | 34.06 | 45.47 | 1,897.90 | 0.34 | 0.02 | 60 KB |
75+
| Without | 256 | 10,565.00 | 186.05 | 248.37 | 10,473.00 | 1.00 | 0.00 | 63 KB |
76+
| With | 256 | 2,977.00 | 29.38 | 39.23 | 2,964.50 | 0.28 | 0.01 | 69 KB |
77+
| Without | 512 | 20,110.50 | 452.28 | 603.79 | 20,108.30 | 1.00 | 0.00 | 106 KB |
78+
| With | 512 | 5,313.10 | 47.66 | 63.62 | 5,340.80 | 0.26 | 0.01 | 88 KB |
79+
| Without | 1024 | 46,599.30 | 4,286.13 | 5,721.87 | 48,194.20 | 1.00 | 0.00 | 192 KB |
80+
| With | 1024 | 11,614.40 | 85.81 | 114.55 | 11,619.80 | 0.25 | 0.03 | 128 KB |
81+
| Without | 2048 | 105,096.90 | 5,359.60 | 7,154.92 | 106,405.10 | 1.00 | 0.00 | 363 KB |
82+
| With | 2048 | 19,481.40 | 66.66 | 88.99 | 19,474.80 | 0.19 | 0.01 | 213 KB |
83+
| Without | 4096 | 177,245.80 | 1,812.40 | 2,419.51 | 176,767.90 | 1.00 | 0.00 | 706 KB |
84+
| With | 4096 | 38,743.00 | 2,422.07 | 3,233.40 | 37,414.70 | 0.22 | 0.02 | 368 KB |
85+
86+
</details>
87+
88+
### Results for Guid
89+
90+
![Benchmarks Guid Values][BenchmarksGuid]
91+
92+
<details>
93+
94+
| Method | Values | Mean (us) | Error (us) | Std Dev (us) | Median (us) | Ratio | RatioSD | Allocated |
95+
|---------|--------|--------------|--------------|----------------|---------------|-------|---------|-----------|
96+
| Without | 2 | 895.60 | 30.64 | 40.91 | 877.90 | 1.00 | 0.00 | 21 KB |
97+
| With | 2 | 741.80 | 46.44 | 62.00 | 704.40 | 0.83 | 0.04 | 51 KB |
98+
| Without | 4 | 968.90 | 33.69 | 44.97 | 950.40 | 1.00 | 0.00 | 22 KB |
99+
| With | 4 | 727.00 | 43.20 | 57.68 | 689.80 | 0.75 | 0.04 | 52 KB |
100+
| Without | 8 | 1,075.50 | 34.88 | 46.57 | 1,054.90 | 1.00 | 0.00 | 23 KB |
101+
| With | 8 | 773.10 | 42.45 | 56.67 | 737.10 | 0.72 | 0.04 | 53 KB |
102+
| Without | 16 | 1,372.60 | 66.21 | 88.39 | 1,383.80 | 1.00 | 0.00 | 26 KB |
103+
| With | 16 | 808.90 | 40.12 | 53.55 | 777.80 | 0.59 | 0.06 | 55 KB |
104+
| Without | 32 | 1,710.70 | 26.25 | 35.04 | 1,699.90 | 1.00 | 0.00 | 33 KB |
105+
| With | 32 | 869.80 | 49.27 | 65.78 | 830.40 | 0.51 | 0.03 | 59 KB |
106+
| Without | 64 | 2,656.60 | 30.28 | 40.43 | 2,652.30 | 1.00 | 0.00 | 47 KB |
107+
| With | 64 | 1,038.70 | 58.99 | 78.75 | 994.40 | 0.39 | 0.03 | 67 KB |
108+
| Without | 128 | 5,415.90 | 45.76 | 61.09 | 5,417.00 | 1.00 | 0.00 | 74 KB |
109+
| With | 128 | 1,456.30 | 53.76 | 71.77 | 1,424.10 | 0.27 | 0.02 | 84 KB |
110+
| Without | 256 | 9,461.50 | 45.09 | 60.20 | 9,469.10 | 1.00 | 0.00 | 128 KB |
111+
| With | 256 | 2,156.00 | 36.01 | 48.07 | 2,139.30 | 0.23 | 0.00 | 120 KB |
112+
| Without | 512 | 18,015.10 | 117.47 | 156.82 | 17,946.50 | 1.00 | 0.00 | 219 KB |
113+
| With | 512 | 3,511.30 | 62.41 | 83.32 | 3,460.80 | 0.19 | 0.00 | 197 KB |
114+
| Without | 1024 | 44,525.60 | 754.94 | 1,007.82 | 44,601.80 | 1.00 | 0.00 | 419 KB |
115+
| With | 1024 | 7,825.80 | 72.45 | 96.72 | 7,808.20 | 0.18 | 0.00 | 319 KB |
116+
| Without | 2048 | 83,843.30 | 778.80 | 1,039.68 | 83,954.70 | 1.00 | 0.00 | 801 KB |
117+
| With | 2048 | 12,372.40 | 207.91 | 277.55 | 12,232.20 | 0.15 | 0.00 | 596 KB |
118+
| Without | 4096 | 217,255.80 | 3,458.95 | 4,617.60 | 216,353.20 | 1.00 | 0.00 | 1,566 KB |
119+
| With | 4096 | 24,981.10 | 274.10 | 365.92 | 25,116.70 | 0.12 | 0.00 | 1,132 KB |
120+
121+
</details>
122+
123+
# Getting Started
124+
125+
## Installation
25126
QueryableValues is distributed as a [NuGet Package]. The major version number of this library is aligned with the version of [Entity Framework Core] by which it's supported (e.g. If you are using EF Core 5, then you must use version 5 of QueryableValues).
26127

27128
Please choose the appropriate command below to install it using the NuGet Package Manager Console window in Visual Studio:
@@ -32,7 +133,7 @@ EF Core | Command
32133
5.x | `Install-Package BlazarTech.QueryableValues.SqlServer -Version 5.3.0`
33134
6.x | `Install-Package BlazarTech.QueryableValues.SqlServer -Version 6.3.0`
34135

35-
### Configuration
136+
## Configuration
36137
Look for the place in your code where you are setting up your [DbContext] and calling the [UseSqlServer] extension method, then use a lambda expression to access the `SqlServerDbContextOptionsBuilder` provided by it. It is on this builder that you must call the `UseQueryableValues` extension method as shown in the following simplified examples:
37138

38139
When using the `OnConfiguring` method inside your [DbContext]:
@@ -74,15 +175,15 @@ public class Startup
74175
}
75176
```
76177

77-
### How Do You Use It?
178+
## How Do You Use It?
78179
The `AsQueryableValues` extension method is provided by the `BlazarTech.QueryableValues` namespace; therefore, you must add the following `using` directive to your source code file for it to appear as a method of your [DbContext] instance:
79180
```
80181
using BlazarTech.QueryableValues;
81182
```
82183

83184
Below are a few examples composing a query using the values provided by an [IEnumerable\<T\>].
84185

85-
#### Simple Type Examples
186+
### Simple Type Examples
86187
Using the [Contains][ContainsQueryable] LINQ method:
87188

88189
```c#
@@ -141,7 +242,7 @@ var myQuery2 =
141242
i.PropA
142243
};
143244
```
144-
#### Complex Type Example
245+
### Complex Type Example
145246
```c#
146247
// Performance Tip:
147248
// If your IEnumerable<T> item type (T) has many properties, project only
@@ -321,3 +422,6 @@ PRs are welcome! 🙂
321422
[Guid]: https://docs.microsoft.com/en-us/dotnet/api/system.guid
322423
[Char]: https://docs.microsoft.com/en-us/dotnet/api/system.char
323424
[String]: https://docs.microsoft.com/en-us/dotnet/api/system.string
425+
[benchmarks]: /benchmarks/QueryableValues.SqlServer.Benchmarks
426+
[BenchmarksInt32]: /docs/images/benchmarks/int32-v6.3.0.png
427+
[BenchmarksGuid]: /docs/images/benchmarks/guid-v6.3.0.png
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
using BenchmarkDotNet.Attributes;
2+
using BenchmarkDotNet.Configs;
3+
using BenchmarkDotNet.Engines;
4+
using BlazarTech.QueryableValues;
5+
using Microsoft.Data.SqlClient;
6+
using Microsoft.EntityFrameworkCore;
7+
8+
namespace QueryableValues.SqlServer.Benchmarks;
9+
10+
[SimpleJob(RunStrategy.Monitoring, warmupCount: 1, targetCount: 25, invocationCount: 200)]
11+
[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)]
12+
[GcServer(true), MemoryDiagnoser]
13+
public class ContainsBenchmarks
14+
{
15+
#pragma warning disable CS8618
16+
private IQueryable<Int32Entity> _int32Query;
17+
private IQueryable<GuidEntity> _guidQuery;
18+
private IQueryable<Int32Entity> _queryableValuesInt32Query;
19+
private IQueryable<GuidEntity> _queryableValuesGuidQuery;
20+
#pragma warning restore CS8618
21+
22+
[Params(2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096)]
23+
public int NumberOfValues { get; set; }
24+
25+
private IEnumerable<int> GetIntValues()
26+
{
27+
for (var i = 0; i < NumberOfValues; i++)
28+
{
29+
yield return Random.Shared.Next(10000);
30+
}
31+
}
32+
33+
private IEnumerable<Guid> GetGuidValues()
34+
{
35+
for (var i = 0; i < NumberOfValues; i++)
36+
{
37+
yield return Guid.NewGuid();
38+
}
39+
}
40+
41+
[GlobalSetup]
42+
public void GlobalSetup()
43+
{
44+
Console.WriteLine("Initializing...");
45+
46+
var dbContext = new MyDbContext();
47+
48+
#region Init db
49+
{
50+
var wasCreated = dbContext.Database.EnsureCreated();
51+
52+
if (wasCreated)
53+
{
54+
for (int i = 0; i < 1000; i++)
55+
{
56+
dbContext.Add(new Int32Entity());
57+
dbContext.Add(new GuidEntity());
58+
}
59+
60+
dbContext.SaveChanges();
61+
}
62+
63+
var versionParam = new SqlParameter("@Version", System.Data.SqlDbType.NVarChar, -1)
64+
{
65+
Direction = System.Data.ParameterDirection.Output
66+
};
67+
68+
dbContext.Database.ExecuteSqlRaw("SET @Version = @@VERSION;", versionParam);
69+
70+
Console.WriteLine(versionParam.Value);
71+
72+
dbContext.Database.ExecuteSqlRaw("DBCC FREEPROCCACHE; DBCC DROPCLEANBUFFERS;");
73+
}
74+
#endregion
75+
76+
#region Int32 Queries
77+
{
78+
var intValues = GetIntValues();
79+
80+
_int32Query = dbContext.Int32Entities
81+
.Where(i => intValues.Contains(i.Id));
82+
83+
_queryableValuesInt32Query = dbContext.Int32Entities
84+
.Where(i => dbContext.AsQueryableValues(intValues).Contains(i.Id));
85+
}
86+
#endregion
87+
88+
#region Guid Queries
89+
{
90+
var guidValues = GetGuidValues();
91+
92+
_guidQuery = dbContext.GuidEntities
93+
.Where(i => guidValues.Contains(i.Id));
94+
95+
_queryableValuesGuidQuery = dbContext.GuidEntities
96+
.Where(i => dbContext.AsQueryableValues(guidValues).Contains(i.Id));
97+
}
98+
#endregion
99+
}
100+
101+
[Benchmark(Baseline = true), BenchmarkCategory("Int32")]
102+
public void Without_Int32()
103+
{
104+
_int32Query.Any();
105+
}
106+
107+
[Benchmark, BenchmarkCategory("Int32")]
108+
public void With_Int32()
109+
{
110+
_queryableValuesInt32Query.Any();
111+
}
112+
113+
[Benchmark(Baseline = true), BenchmarkCategory("Guid")]
114+
public void Without_Guid()
115+
{
116+
_guidQuery.Any();
117+
}
118+
119+
[Benchmark, BenchmarkCategory("Guid")]
120+
public void With_Guid()
121+
{
122+
_queryableValuesGuidQuery.Any();
123+
}
124+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using BlazarTech.QueryableValues;
2+
using Microsoft.EntityFrameworkCore;
3+
4+
namespace QueryableValues.SqlServer.Benchmarks
5+
{
6+
public class MyDbContext : DbContext
7+
{
8+
#pragma warning disable CS8618
9+
public DbSet<Int32Entity> Int32Entities { get; set; }
10+
public DbSet<GuidEntity> GuidEntities { get; set; }
11+
#pragma warning restore CS8618
12+
13+
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
14+
{
15+
optionsBuilder.UseSqlServer(
16+
@"Server=.\SQLEXPRESS;Integrated Security=true;Database=QueryableValuesBenchmarks",
17+
builder => builder.UseQueryableValues()
18+
);
19+
}
20+
21+
protected override void OnModelCreating(ModelBuilder modelBuilder)
22+
{
23+
modelBuilder.HasDefaultSchema("dbo");
24+
}
25+
}
26+
27+
public class Int32Entity
28+
{
29+
public int Id { get; set; }
30+
}
31+
32+
public class GuidEntity
33+
{
34+
public Guid Id { get; set; }
35+
}
36+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using BenchmarkDotNet.Running;
2+
3+
namespace QueryableValues.SqlServer.Benchmarks;
4+
5+
class Program
6+
{
7+
static void Main(string[] args)
8+
{
9+
var summary = BenchmarkRunner.Run<ContainsBenchmarks>();
10+
}
11+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net6.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
12+
<PackageReference Include="BlazarTech.QueryableValues.SqlServer" Version="6.3.0" />
13+
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.1" />
14+
</ItemGroup>
15+
16+
</Project>
283 KB
Loading
286 KB
Loading

0 commit comments

Comments
 (0)