原文:https://codevision.medium.com/native-aot-and-databases-87b26f2fcfc8
补充一下,DapperAOT声称可在有限条件下支持AOT,原来用DAPPER实现的应用甚至无需修改代码,待测。
Updated:
Nanorm
A tiny data-access helper library for ADO.NET. Trimming and native AOT friendly.
It supports:
- PostgreSQL via
Npgsql
- SQLite via
Microsoft.Data.SQLite
- Any ADO.NET data provider via
System.Data.Common
A lot of applications communicate with databases at this point of time. That’s undeniable fact. Also most people in .NET world do not use ADO.NET directly and instead of rely on the ORM or ORM-like libraries. Given that Native AOT currently has several limitations, what kind of code can be compiled, I decide to test what libraries can be used with NativeAOT without too much pain.
Setting up tests
In order for test what’s available, I decide that Dapper.Performance.Tests suite would work just fine for this purposes. This collection of benchmarks easy to setup and it has references to a lot of other libraries which can be tested too. I run these tests locally without NativeAOT first, and was more then satisfied how easy it was to run the test. Just follow instructions in the repo https://github.com/StackExchange/Dapper#performance and that’s it. After that I start poking into run tests under NativeAOT.
Since Dapper.Performance.Tests uses manual configuration of run for BenchmarkDotNet I have to modify these lines https://github.com/StackExchange/Dapper/blob/main/benchmarks/Dapper.Tests.Performance/Config.cs#L36-L41
By replacing then with these lines
1 2 3 4 5 6 7 8 9 10 11 12 13 |
AddJob(Job.ShortRun .WithLaunchCount(1) .WithWarmupCount(2) .WithUnrollFactor(Iterations) .WithIterationCount(10) .WithToolchain(BenchmarkDotNet.Toolchains.CoreRt.CoreRtToolchain.CreateBuilder() .UseCoreRtNuGet(microsoftDotNetILCompilerVersion: "6.0.0-preview.4.21175.3") .AdditionalNuGetFeed("dotnet-experimental", "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-experimental/nuget/v3/index.json")// the version goes here .DisplayName("NativeAOT NuGet") .TargetFrameworkMoniker("net5.0") .Timeout(System.TimeSpan.FromSeconds(1000)) .ToToolchain()) ); |
For most projects that would be enough, but unfortunately Microsoft.Data.SqlClient has native DLL in the Nuget package Microsoft.Data.SqlClient.SNI.dll and BenchmarkDotNet has issue where it run executable from current location. See https://github.com/dotnet/BenchmarkDotNet/pull/1679 for context. So right now, you have to checkout BenchmarkDotnet locally and use that version instead of package. Hopefully 0.13.0 would be released soon and it would be less necessary use local copy for running perf tests.
I start with test for following benchmarks which run on .NET 5.
- Dapper
- EntityFrameworkCore
- ServiceStack
- Linq2Db
- DevExpress XPO
- Hand coded ADO.NET access
- PetaPoco
- Mighty
- NHibernate
- Massive
- LLBGenPro
After I start adding benchmarks, as usually with NativeAOT I have to provide custom RD.xml file to augment what’s types should be added to compilation. More about that I wrote previously here. Here I hit one more limitation of BenchmarkDotNet, you cannot override RD.xml because it provided by BenchmarkDotNet. Since I already have local version of BenchmarkDotnet so I have to manually modify content of RD.xml from location here https://github.com/dotnet/BenchmarkDotNet/blob/b67cfb45a0b0396c2e97bbe49de78b24b5d1a572/src/BenchmarkDotNet/Toolchains/CoreRt/Generator.cs#L198-L212 Keep this in mind if you decide to reproduce my results or improve them.
After all this was done, I start expanding RD.xml until I manage to cover all frameworks. It was a bit painful, since I have to run benchmarks again and again until they stop producing errors about missing types. Here the actual content of RD.xml which is needed for benchmarks to run.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
<Directives> <Application> <Assembly Name="System.Data.SqlClient"> <Type Name="System.Data.SqlClient.SqlCommandBuilder" Dynamic="Required All" /> <Type Name="System.Data.SqlClient.SqlBulkCopy" Dynamic="Required All" /> <Type Name="System.Data.SqlClient.SqlConnection" Dynamic="Required All" /> <Type Name="System.Data.SqlClient.SqlBulkCopyOptions" Dynamic="Required All" /> <Type Name="System.Data.SqlClient.SqlBulkCopyOptions[]" Dynamic="Required All" /> </Assembly> <Assembly Name="DevExpress.Data.v20.2"> <Type Name="DevExpress.Xpo.DB.Helpers.ReflectConnectionHelper+GetHelper`2[[System.Data.SqlClient.SqlException,System.Data.SqlClient],[System.Int32,System.Private.CoreLib]]" Dynamic="Required All" /> <Type Name="DevExpress.Xpo.DB.Helpers.ReflectConnectionHelper+GetHelper`2[[System.Data.SqlClient.SqlException,System.Data.SqlClient],[System.String,System.Private.CoreLib]]" Dynamic="Required All" /> </Assembly> <Assembly Name="System.Configuration.ConfigurationManager"> <Type Name="System.Configuration.ClientConfigurationHost" Dynamic="Required All" /> <Type Name="System.Configuration.AppSettingsSection" Dynamic="Required All" /> </Assembly> <Assembly Name="linq2db"> <Type Name="LinqToDB.DataProvider.SqlServer.SqlServerProviderAdapter" Dynamic="Required All" /> <Type Name="LinqToDB.DataProvider.SqlServer.SqlServerProviderAdapter+SqlBulkCopyOptions" Dynamic="Required All" /> <Type Name="LinqToDB.DataProvider.SqlServer.SqlServerProviderAdapter+SqlBulkCopyOptions[]" Dynamic="Required All" /> <Type Name="LinqToDB.DataProvider.SqlServer.SqlServerProviderAdapter+SqlConnection" Dynamic="Required All" /> <Type Name="LinqToDB.DataProvider.SqlServer.SqlServerProviderAdapter+SqlErrorCollection" Dynamic="Required All" /> <Type Name="LinqToDB.DataProvider.SqlServer.SqlServerProviderAdapter+SqlException" Dynamic="Required All" /> <Type Name="LinqToDB.DataProvider.SqlServer.SqlServerProviderAdapter+SqlError" Dynamic="Required All" /> <Type Name="LinqToDB.DataProvider.SqlServer.SqlServerProviderAdapter+SqlConnectionStringBuilder" Dynamic="Required All" /> <Type Name="LinqToDB.DataProvider.SqlServer.SqlServerProviderAdapter+SqlBulkCopy" Dynamic="Required All" /> <Type Name="LinqToDB.DataProvider.SqlServer.SqlServerProviderAdapter+SqlBulkCopyColumnMappingCollection" Dynamic="Required All" /> <Type Name="LinqToDB.DataProvider.SqlServer.SqlServerProviderAdapter+SqlRowsCopiedEventArgs" Dynamic="Required All" /> <Type Name="LinqToDB.Linq.CompiledTable`1[[SqlMarshal.Tests.Performance.Post,SqlMarshal.Tests.Performance]]" Dynamic="Required All" /> <Type Name="LinqToDB.Data.DataParameter" Dynamic="Required All" /> </Assembly> <Assembly Name="Microsoft.EntityFrameworkCore"> <Type Name="Microsoft.EntityFrameworkCore.Internal.DbSetInitializer" Dynamic="Required All" /> </Assembly> <Assembly Name="System.Runtime"> <Type Name="System.GC" Dynamic="Required All" /> <Type Name="System.Enum" Dynamic="Required All" /> <Type Name="System.Func`2[[LinqToDB.DataProvider.SqlServer.SqlServerProviderAdapter+SqlBulkCopyOptions,linq2db],[System.Data.SqlClient.SqlBulkCopyOptions,System.Data.SqlClient]]" Dynamic="Required All" /> <Type Name="System.Func`2[[System.Data.SqlClient.SqlBulkCopyOptions,System.Data.SqlClient],[LinqToDB.DataProvider.SqlServer.SqlServerProviderAdapter+SqlBulkCopyOptions,linq2db]]" Dynamic="Required All" /> <Type Name="System.Func`2[[System.Int32,System.Private.CoreLib],[System.Nullable`1[[System.Int32,System.Private.CoreLib]],System.Private.CoreLib]]" Dynamic="Required All" /> <Type Name="System.Func`4[[System.Data.IDbConnection,System.Data.Common],[System.Data.SqlClient.SqlBulkCopyOptions,System.Data.SqlClient],[System.Data.IDbTransaction,System.Data.Common],[System.Data.SqlClient.SqlBulkCopy,System.Data.SqlClient]]" Dynamic="Required All" /> <Type Name="System.Func`3[[System.Data.SqlClient.SqlDataReader,System.Data.SqlClient],[System.Int32,System.Private.CoreLib],[System.Data.SqlTypes.SqlBinary,System.Data.Common]]" Dynamic="Required All" /> <Type Name="System.Func`3[[System.Data.SqlClient.SqlDataReader,System.Data.SqlClient],[System.Int32,System.Private.CoreLib],[System.Data.SqlTypes.SqlBoolean,System.Data.Common]]" Dynamic="Required All" /> <Type Name="System.Func`3[[System.Data.SqlClient.SqlDataReader,System.Data.SqlClient],[System.Int32,System.Private.CoreLib],[System.Data.SqlTypes.SqlDateTime,System.Data.Common]]" Dynamic="Required All" /> <Type Name="System.Func`3[[System.Data.SqlClient.SqlDataReader,System.Data.SqlClient],[System.Int32,System.Private.CoreLib],[System.Data.SqlTypes.SqlByte,System.Data.Common]]" Dynamic="Required All" /> <Type Name="System.Func`3[[System.Data.SqlClient.SqlDataReader,System.Data.SqlClient],[System.Int32,System.Private.CoreLib],[System.Data.SqlTypes.SqlDecimal,System.Data.Common]]" Dynamic="Required All" /> <Type Name="System.Func`3[[System.Data.SqlClient.SqlDataReader,System.Data.SqlClient],[System.Int32,System.Private.CoreLib],[System.Data.SqlTypes.SqlDouble,System.Data.Common]]" Dynamic="Required All" /> <Type Name="System.Func`3[[System.Data.SqlClient.SqlDataReader,System.Data.SqlClient],[System.Int32,System.Private.CoreLib],[System.Data.SqlTypes.SqlGuid,System.Data.Common]]" Dynamic="Required All" /> <Type Name="System.Func`3[[System.Data.SqlClient.SqlDataReader,System.Data.SqlClient],[System.Int32,System.Private.CoreLib],[System.Data.SqlTypes.SqlInt16,System.Data.Common]]" Dynamic="Required All" /> <Type Name="System.Func`3[[System.Data.SqlClient.SqlDataReader,System.Data.SqlClient],[System.Int32,System.Private.CoreLib],[System.Data.SqlTypes.SqlInt32,System.Data.Common]]" Dynamic="Required All" /> <Type Name="System.Func`3[[System.Data.SqlClient.SqlDataReader,System.Data.SqlClient],[System.Int32,System.Private.CoreLib],[System.Data.SqlTypes.SqlInt64,System.Data.Common]]" Dynamic="Required All" /> <Type Name="System.Func`3[[System.Data.SqlClient.SqlDataReader,System.Data.SqlClient],[System.Int32,System.Private.CoreLib],[System.Data.SqlTypes.SqlMoney,System.Data.Common]]" Dynamic="Required All" /> <Type Name="System.Func`3[[System.Data.SqlClient.SqlDataReader,System.Data.SqlClient],[System.Int32,System.Private.CoreLib],[System.Data.SqlTypes.SqlSingle,System.Data.Common]]" Dynamic="Required All" /> <Type Name="System.Func`3[[System.Data.SqlClient.SqlDataReader,System.Data.SqlClient],[System.Int32,System.Private.CoreLib],[System.Data.SqlTypes.SqlString,System.Data.Common]]" Dynamic="Required All" /> <Type Name="System.Action`2[[SqlMarshal.Tests.Performance.Post,SqlMarshal.Tests.Performance],[System.DateTime,System.Private.CoreLib]]" Dynamic="Required All" /> <Type Name="System.Action`2[[SqlMarshal.Tests.Performance.Post,SqlMarshal.Tests.Performance],[System.Nullable`1[[System.Int32,System.Private.CoreLib]],System.Private.CoreLib]]" Dynamic="Required All" /> </Assembly> <Assembly Name="SqlMarshal.Tests.Performance"> <Type Name="SqlMarshal.Tests.Performance.Xpo.Post" Dynamic="Required All" /> <Type Name="SqlMarshal.Tests.Performance.Post" Dynamic="Required All" /> </Assembly> <Assembly Name="NHibernate"> <Type Name="NHibernate.Cfg.MappingSchema.HbmMapping" Dynamic="Required All" /> <Type Name="NHibernate.Cfg.MappingSchema.HbmClass" Dynamic="Required All" /> <Type Name="NHibernate.Cfg.MappingSchema.HbmSubselect" Dynamic="Required All" /> <Type Name="NHibernate.Cfg.MappingSchema.HbmComment" Dynamic="Required All" /> <Type Name="NHibernate.Cfg.MappingSchema.HbmNaturalId" Dynamic="Required All" /> <Type Name="NHibernate.Cfg.MappingSchema.HbmComponent" Dynamic="Required All" /> <Type Name="NHibernate.Cfg.MappingSchema.HbmDynamicComponent" Dynamic="Required All" /> <Type Name="NHibernate.Cfg.MappingSchema.HbmPrimitiveArray" Dynamic="Required All" /> <Type Name="NHibernate.Cfg.MappingSchema.IColumnsMapping" Dynamic="Required All" /> <Type Name="NHibernate.Cfg.MappingSchema.IFormulasMapping" Dynamic="Required All" /> <Type Name="NHibernate.Cfg.MappingSchema.HbmCustomSQLCheck" Dynamic="Required All" /> <Type Name="NHibernate.Cfg.MappingSchema.HbmPrimitivearrayOuterjoin" Dynamic="Required All" /> <Type Name="NHibernate.Cfg.MappingSchema.HbmPrimitivearrayFetch" Dynamic="Required All" /> <Type Name="NHibernate.Cfg.MappingSchema.ITypeMapping" Dynamic="Required All" /> <Type Name="NHibernate.Cfg.MappingSchema.HbmTimestampUnsavedvalue" Dynamic="Required All" /> <Type Name="NHibernate.Cfg.MappingSchema.HbmQueryParam[]" Dynamic="Required All" /> <Type Name="NHibernate.Cfg.MappingSchema.HbmDialectScope" Dynamic="Required All" /> <Type Name="NHibernate.Cfg.MappingSchema.HbmDiscriminator" Dynamic="Required All" /> <Type Name="NHibernate.Cfg.MappingSchema.HbmId" Dynamic="Required All" /> <Type Name="NHibernate.Cfg.MappingSchema.HbmProperty" Dynamic="Required All" /> <Type Name="NHibernate.Dialect.MsSql2005Dialect" Dynamic="Required All" /> <Type Name="NHibernate.Connection.DriverConnectionProvider" Dynamic="Required All" /> <Type Name="NHibernate.Driver.SqlClientDriver" Dynamic="Required All" /> </Assembly> <Assembly Name="System.Private.Xml"> <Type Name="System.Xml.Serialization.ReflectionXmlSerializationReaderHelper" Dynamic="Required All"> <Method Name="GetSetMemberValueDelegateWithType" Dynamic="Required All"> <GenericArgument Name="NHibernate.Cfg.MappingSchema.HbmMapping, NHibernate" /> <GenericArgument Name="System.Boolean, System.Private.CoreLib" /> <Parameter Name="System.Reflection.MemberInfo, System.Private.CoreLib" /> </Method> </Type> </Assembly> <Assembly Name="System.Threading.ThreadPool"> <Type Name="System.Threading.ThreadPool" Dynamic="Required All" /> </Assembly> <Assembly Name="System.Threading"> <Type Name="System.Threading.Monitor" Dynamic="Required All" /> </Assembly> </Application> </Directives> |
Running tests
Unfortunately, results were not very impressive.
Dapper would likely not work on NativeAOT completely since it emits IL on the fly. One out.
ServiceStack also use dynamic code generation, so second is out too.
EntityFrameworkCore is still not implemented. See https://github.com/dotnet/runtimelab/issues/733. One more cannot work with NativeAOT.
Linq2Db works. Excellent.
DevExpress XPO was working. This is good.
Hand coded ADO.NET access was working also.
PetaPoco uses dynamic IL generation. Cannot compile.
Mighty fails with strange error “System.InvalidOperationException: Unknown database provider: System.Data.SqlClient” even if I explicitly register DbProvider. If somebody know what’s going on would be interesting to hear.
Massive works.
NHibernate also fails with strange error. System.ArgumentException: Column requires a valid DataType. Problem sitting somewhere inside System.Data.SqlClient when you retrieve schema and null passed to DataColumn.DataType, so you would need be wary of that scenarios too.
LLBGenPro with Adapter template group works. I was not able to figure out how to create sample code for SelfServicing group.
Summary
So after tests there 5 libraries was working:
- Hand written ADO.NET
- Linq2Db 截至2023.6.29, 2.7k个star
- DevExpress XPO 截至2023.6.29, 147个star
- Massive 截至2023.6.29, 1.8k个star
- LLBLGenPro 貌似不是开源的,299欧元
Two potentially working libraries which I simply cannot make work: Mighty and NHibernate. And unfortunately 2 most popular choices Dapper and EF Core does not working.
Performance comparison
Here the results of running BencmarkDotNet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
// * Summary * BenchmarkDotNet=v0.12.1.20210402-develop, OS=Windows 10.0.19042.867 (20H2/October2020Update) Intel Core i7-6700HQ CPU 2.60GHz (Skylake), 1 CPU, 8 logical and 4 physical cores .NET SDK=5.0.201 [Host] : .NET 5.0.4 (5.0.421.11614), X64 RyuJIT ShortRun : .NET 6.0.0-preview.4.21175.3, X64 AOT | ORM | Method | Return | Mean | StdDev | Error | Gen 0 | Gen 1 | Gen 2 | Allocated | |--------------- |------------------- |----------- |---------:|----------:|----------:|--------:|-------:|-------:|----------:| | Hand Coded | DataTable | dynamic | 138.3 us | 5.27 us | 8.86 us | 3.0000 | - | - | 9 KB | | Hand Coded | SqlCommand | Post | 138.7 us | 9.28 us | 17.74 us | 1.5000 | 0.5000 | - | 7 KB | | SqlMarshal | Alterative | Post | 151.6 us | 17.26 us | 33.01 us | 1.7500 | 0.5000 | 0.2500 | 11 KB | | SqlMarshal | SqlCommand | Post | 160.1 us | 16.79 us | 32.11 us | 2.2500 | 0.5000 | 0.2500 | 11 KB | | Massive | 'Query (dynamic)' | dynamic | 166.7 us | 19.55 us | 37.37 us | 2.0000 | 0.5000 | 0.2500 | 12 KB | | LINQ to DB | Query<T> | Post | 235.2 us | 77.64 us | 117.38 us | 2.0000 | 0.5000 | - | 13 KB | | DevExpress.XPO | GetObjectByKey<T> | Post | 237.6 us | 6.85 us | 13.10 us | 5.5000 | 1.0000 | - | 33 KB | | LINQ to DB | 'First (Compiled)' | Post | 237.9 us | 74.46 us | 112.57 us | 2.5000 | 0.5000 | - | 14 KB | | DevExpress.XPO | FindObject<T> | Post | 242.6 us | 65.94 us | 126.08 us | 8.0000 | - | - | 27 KB | | LINQ to DB | First | Post | 324.6 us | 12.67 us | 24.23 us | 5.0000 | 1.0000 | - | 26 KB | | DevExpress.XPO | Query<T> | Post | 329.6 us | 12.69 us | 24.26 us | 11.0000 | - | - | 36 KB | | LLBGenPro | LinqMetaData | PostEntity | 658.6 us | 193.18 us | 292.06 us | 7.0000 | - | - | 48 KB | // * Legends * ORM : The object/relational mapper being tested Return : The return type of the method Mean : Arithmetic mean of all measurements StdDev : Standard deviation of all measurements Error : Half of 99.9% confidence interval Gen 0 : GC Generation 0 collects per 1000 operations Gen 1 : GC Generation 1 collects per 1000 operations Gen 2 : GC Generation 2 collects per 1000 operations Allocated : Allocated memory per single operation (managed only, inclusive, 1KB = 1024B) 1 us : 1 Microsecond (0.000001 sec) |
Small promotion
You may be found that there entry SqlMarshal in the benchmarks. This is my project where I use Source generators for generate mapping of objects from DbDataReader in a manner similar to Dapper, but which actually friendly to NativeAOT. If you think that interesting to you, take a look at my project: https://github.com/kant2002/SqlMarshal. Performance characteristics actually the same with Dapper if run on actual .NET so you may use that project if you plan to NativeAOT your project later on.
If you want comparison how it stands against other ORM in regular .NET usage — see results below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
// * Summary * BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19042 Intel Core i7-6700HQ CPU 2.60GHz (Skylake), 1 CPU, 8 logical and 4 physical cores .NET Core SDK=5.0.201 [Host] : .NET Core 5.0.4 (CoreCLR 5.0.421.11614, CoreFX 5.0.421.11614), X64 RyuJIT ShortRun : .NET Core 5.0.4 (CoreCLR 5.0.421.11614, CoreFX 5.0.421.11614), X64 RyuJIT | ORM | Method | Return | Mean | StdDev | Error | Gen 0 | Gen 1 | Gen 2 | Allocated | |--------------- |------------------------------ |-------- |---------:|---------:|----------:|--------:|-------:|-------:|----------:| | Hand Coded | DataTable | dynamic | 130.9 us | 7.97 us | 15.24 us | 3.0000 | - | - | 9.37 KB | | Dapper | QueryFirstOrDefault<dynamic> | dynamic | 134.6 us | 5.98 us | 10.05 us | 3.5000 | - | - | 11.39 KB | | Belgrade | ExecuteReader | Post | 134.6 us | 13.25 us | 20.04 us | 1.5000 | 0.5000 | - | 8.52 KB | | SqlMarshal | Alterative | Post | 138.0 us | 3.19 us | 6.09 us | 1.7500 | 0.5000 | 0.2500 | 11.2 KB | | SqlMarshal | SqlCommand | Post | 138.6 us | 5.96 us | 10.01 us | 2.0000 | 0.5000 | 0.2500 | 11.27 KB | | LINQ to DB | Query<T> | Post | 142.0 us | 4.10 us | 7.84 us | 2.0000 | 0.5000 | 0.2500 | 10.64 KB | | Dapper | QueryFirstOrDefault<T> | Post | 142.7 us | 4.94 us | 8.31 us | 1.7500 | 0.5000 | 0.2500 | 11.35 KB | | PetaPoco | 'Fetch<T> (Fast)' | Post | 144.3 us | 7.39 us | 14.13 us | 2.0000 | 0.5000 | 0.2500 | 11.52 KB | | Mighty | Query<T> | Post | 144.7 us | 3.73 us | 6.26 us | 2.0000 | 0.5000 | 0.2500 | 12.53 KB | | Dapper | 'Query<dynamic> (buffered)' | dynamic | 145.1 us | 5.50 us | 9.24 us | 2.0000 | 0.5000 | - | 11.72 KB | | Dapper | 'Query<T> (buffered)' | Post | 145.6 us | 8.25 us | 15.78 us | 2.2500 | 0.5000 | 0.2500 | 11.64 KB | | PetaPoco | Fetch<T> | Post | 146.6 us | 2.91 us | 4.89 us | 2.0000 | 0.5000 | 0.2500 | 12.23 KB | | Mighty | SingleFromQuery<dynamic> | dynamic | 152.0 us | 9.17 us | 15.41 us | 2.0000 | 0.5000 | 0.2500 | 12.43 KB | | Massive | 'Query (dynamic)' | dynamic | 153.2 us | 19.72 us | 37.70 us | 2.0000 | 0.5000 | 0.2500 | 12.07 KB | | Hand Coded | SqlCommand | Post | 154.5 us | 46.95 us | 70.99 us | 1.5000 | 0.6250 | 0.2500 | 7.42 KB | | Dapper | 'Contrib Get<T>' | Post | 154.7 us | 12.76 us | 24.41 us | 2.5000 | 0.5000 | 0.2500 | 12.29 KB | | Mighty | SingleFromQuery<T> | Post | 157.2 us | 10.86 us | 20.77 us | 2.0000 | 0.5000 | 0.2500 | 12.53 KB | | LINQ to DB | 'First (Compiled)' | Post | 160.8 us | 13.19 us | 25.21 us | 1.7500 | 0.5000 | 0.2500 | 11.23 KB | | ServiceStack | SingleById<T> | Post | 161.0 us | 19.14 us | 36.60 us | 2.5000 | 0.5000 | 0.2500 | 15.27 KB | | Mighty | Query<dynamic> | dynamic | 171.9 us | 25.22 us | 48.23 us | 2.0000 | 0.5000 | 0.2500 | 12.43 KB | | DevExpress.XPO | GetObjectByKey<T> | Post | 198.1 us | 5.34 us | 10.22 us | 5.7500 | 1.5000 | - | 29.28 KB | | DevExpress.XPO | FindObject<T> | Post | 199.0 us | 16.24 us | 27.29 us | 8.0000 | - | - | 27.32 KB | | Dapper | 'Query<dynamic> (unbuffered)' | dynamic | 201.1 us | 6.23 us | 11.91 us | 2.5000 | 0.5000 | 0.2500 | 11.8 KB | | EF 6 | SqlQuery | Post | 201.3 us | 11.05 us | 16.70 us | 4.5000 | 1.0000 | 0.2500 | 23.68 KB | | LINQ to DB | First | Post | 207.4 us | 16.34 us | 27.45 us | 2.5000 | 0.5000 | - | 15.3 KB | | Dapper | 'Query<T> (unbuffered)' | Post | 221.0 us | 35.61 us | 59.84 us | 2.0000 | 0.5000 | 0.2500 | 11.76 KB | | DevExpress.XPO | Query<T> | Post | 226.7 us | 12.22 us | 20.54 us | 10.0000 | - | - | 31.61 KB | | EF Core | 'First (Compiled)' | Post | 239.7 us | 18.60 us | 31.25 us | 2.5000 | - | - | 8.33 KB | | NHibernate | Get<T> | Post | 304.9 us | 11.83 us | 17.89 us | 5.0000 | 1.0000 | - | 29.32 KB | | NHibernate | HQL | Post | 308.2 us | 26.28 us | 39.73 us | 5.0000 | 1.0000 | - | 31.31 KB | | EF 6 | First | Post | 327.6 us | 15.99 us | 26.87 us | 14.0000 | - | - | 43.62 KB | | EF Core | First | Post | 333.6 us | 20.63 us | 31.18 us | 3.5000 | - | - | 12.05 KB | | EF 6 | 'First (No Tracking)' | Post | 366.2 us | 40.03 us | 67.27 us | 8.5000 | 1.0000 | - | 50.39 KB | | EF Core | SqlQuery | Post | 369.7 us | 34.58 us | 58.10 us | 5.5000 | - | - | 17.31 KB | | NHibernate | Criteria | Post | 370.7 us | 25.55 us | 48.84 us | 11.0000 | 1.0000 | - | 56.66 KB | | EF Core | 'First (No Tracking)' | Post | 371.8 us | 17.11 us | 25.87 us | 3.0000 | 0.5000 | - | 19.45 KB | | NHibernate | SQL | Post | 427.8 us | 28.43 us | 47.77 us | 19.0000 | 1.0000 | - | 79 KB | | NHibernate | LINQ | Post | 921.2 us | 74.70 us | 142.83 us | 10.0000 | 1.0000 | - | 52.34 KB | // * Legends * ORM : The object/relational mapper being tested Return : The return type of the method Mean : Arithmetic mean of all measurements StdDev : Standard deviation of all measurements Error : Half of 99.9% confidence interval Gen 0 : GC Generation 0 collects per 1000 operations Gen 1 : GC Generation 1 collects per 1000 operations Gen 2 : GC Generation 2 collects per 1000 operations Allocated : Allocated memory per single operation (managed only, inclusive, 1KB = 1024B) 1 us : 1 Microsecond (0.000001 sec) |
Thanks for reading. I think at least this article can help you properly assess current limitations with NativeAOT and database usage, and hopefully simplify your journey porting your application to that platform.