Skip to content

Instantly share code, notes, and snippets.

@devzerker
Created May 2, 2021 16:20
Show Gist options
  • Save devzerker/30fd494e560dfba0ec7ea38897b5cb8d to your computer and use it in GitHub Desktop.
Save devzerker/30fd494e560dfba0ec7ea38897b5cb8d to your computer and use it in GitHub Desktop.
Bulk insert benchmark EF, EF Extensions and Linq2Db
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<StartWorkingDirectory>$(MSBuildProjectDirectory)</StartWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
<PackageReference Include="Bogus" Version="33.0.2" />
<PackageReference Include="linq2db" Version="3.3.0" />
<PackageReference Include="linq2db.SqlServer" Version="3.3.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.5" />
<PackageReference Include="System.Data.SqlClient" Version="4.8.2" />
<PackageReference Include="Z.EntityFramework.Extensions.EFCore" Version="5.1.33" />
</ItemGroup>
<ItemGroup>
<Compile Remove="BenchmarkDotNet.Artifacts\**" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Remove="BenchmarkDotNet.Artifacts\**" />
</ItemGroup>
<ItemGroup>
<None Remove="BenchmarkDotNet.Artifacts\**" />
</ItemGroup>
</Project>
using System;
using System.Linq;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using Bogus;
using Bogus.DataSets;
using LinqToDB;
using LinqToDB.Configuration;
using LinqToDB.Data;
using LinqToDB.Mapping;
using Microsoft.EntityFrameworkCore;
namespace BulkInsertBench
{
[MemoryDiagnoser]
public class BulkInsertBench
{
private readonly Faker<User> _faker = new Faker<User>()
.RuleFor(u => u.Id, _ => 0)
.RuleFor(u => u.Gender, f => f.PickRandom<Name.Gender>())
.RuleFor(u => u.FirstName, (f, u) => f.Name.FirstName(u.Gender))
.RuleFor(u => u.LastName, (f, u) => f.Name.LastName(u.Gender))
.RuleFor(u => u.Avatar, f => f.Internet.Avatar())
.RuleFor(u => u.UserName, (f, u) => f.Internet.UserName(u.FirstName, u.LastName))
.RuleFor(u => u.Email, (f, u) => f.Internet.Email(u.FirstName, u.LastName))
.RuleFor(u => u.DateOfBirth, f => f.Date.Past(100, DateTime.Parse("1/1/2010")));
private User[] _users;
[Params(10_000, 100_000)]
public int N;
[IterationSetup]
public void IterationSetup()
{
_users = Enumerable.Range(0, N).Select(_ => _faker.Generate()).ToArray();
}
[IterationCleanup]
public void IterationCleanup()
{
using var db = new LinqToDb();
db.Users.Delete();
}
private async Task EfAdd(EfContext context)
{
foreach (var user in _users) await context.AddAsync(user);
await context.SaveChangesAsync();
}
[Benchmark]
public async Task EfAdd()
{
await using var context = new EfContext();
await EfAdd(context);
}
[Benchmark]
public async Task EfAddNoTracking()
{
await using var context = new EfContext {ChangeTracker = {AutoDetectChangesEnabled = false}};
await EfAdd(context);
}
private async Task EfAddRange(EfContext context)
{
await context.Users.AddRangeAsync(_users);
await context.SaveChangesAsync();
}
[Benchmark]
public async Task EfAddRange()
{
await using var context = new EfContext();
await EfAddRange(context);
}
[Benchmark]
public async Task EfAddRangeNoTracking()
{
await using var context = new EfContext {ChangeTracker = {AutoDetectChangesEnabled = false}};
await EfAddRange(context);
}
[Benchmark]
public async Task EfExtensionsBulkInsert()
{
await using var db = new EfContext();
await db.BulkInsertAsync(_users);
}
[Benchmark]
public async Task LinqToDbBulkCopy()
{
await using var db = new LinqToDb();
await db.BulkCopyAsync(_users);
}
}
class Program
{
static void Main()
{
BenchmarkRunner.Run<BulkInsertBench>();
}
}
class EfContext : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
options.UseSqlServer(@"Data Source=(LocalDb)\MSSQLLocalDB;Database=BulkInsertBenchDb;");
}
}
public class LinqToDb : DataConnection
{
public LinqToDb() : base(new LinqToDbConnectionOptionsBuilder()
.UseSqlServer(@"Data Source=(LocalDb)\MSSQLLocalDB;Database=BulkInsertBenchDb;").Build()) { }
public ITable<User> Users => GetTable<User>();
}
[Table(Name = "Users")]
public class User
{
[PrimaryKey] public int Id { get; set; }
[Column] public string FirstName { get; set; }
[Column] public string LastName { get; set; }
[Column] public string UserName { get; set; }
[Column] public string Email { get; set; }
[Column] public string Avatar { get; set; }
[Column] public Name.Gender Gender { get; set; }
[Column] public DateTime DateOfBirth { get; set; }
}
}
BenchmarkDotNet=v0.12.1, OS=Windows 10.0.17763.1879 (1809/October2018Update/Redstone5)
Intel Xeon CPU E5-2670 0 2.60GHz, 1 CPU, 16 logical and 8 physical cores
.NET Core SDK=5.0.202
  [Host]     : .NET Core 5.0.5 (CoreCLR 5.0.521.16609, CoreFX 5.0.521.16609), X64 RyuJIT
  Job-RYVZJD : .NET Core 5.0.5 (CoreCLR 5.0.521.16609, CoreFX 5.0.521.16609), X64 RyuJIT

InvocationCount=1  UnrollFactor=1  
Method N Mean Error StdDev Gen 0 Gen 1 Gen 2 Allocated
EfAdd 10000 1,664.8 ms 15.22 ms 12.71 ms 13000.0000 4000.0000 - 139.58 MB
EfAddNoTracking 10000 1,549.3 ms 11.74 ms 10.99 ms 12000.0000 3000.0000 - 127.42 MB
EfAddRange 10000 1,599.0 ms 6.82 ms 5.33 ms 13000.0000 4000.0000 - 139.26 MB
EfAddRangeNoTracking 10000 1,534.1 ms 6.07 ms 5.67 ms 12000.0000 3000.0000 - 127.14 MB
EfExtensionsBulkInsert 10000 222.9 ms 1.62 ms 1.26 ms 1000.0000 - - 12.64 MB
LinqToDbBulkCopy 10000 128.7 ms 2.30 ms 2.04 ms - - - 8.48 MB
EfAdd 100000 16,686.5 ms 152.93 ms 135.57 ms 137000.0000 31000.0000 1000.0000 1390.61 MB
EfAddNoTracking 100000 15,851.7 ms 114.80 ms 107.39 ms 125000.0000 30000.0000 1000.0000 1269.15 MB
EfAddRange 100000 16,526.9 ms 168.50 ms 157.62 ms 136000.0000 30000.0000 1000.0000 1387.58 MB
EfAddRangeNoTracking 100000 15,710.1 ms 124.62 ms 116.57 ms 124000.0000 29000.0000 1000.0000 1266.45 MB
EfExtensionsBulkInsert 100000 2,258.4 ms 43.92 ms 65.74 ms 11000.0000 3000.0000 - 124.34 MB
LinqToDbBulkCopy 100000 1,241.7 ms 23.93 ms 21.22 ms 8000.0000 1000.0000 - 81.19 MB
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment