首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >ORM性能测试Benchmark(最终版)

ORM性能测试Benchmark(最终版)

原创
作者头像
用户8604107
发布2026-03-24 14:14:00
发布2026-03-24 14:14:00
600
举报

ORM性能测试Benchmark(最终版)

本测试聚焦 ORM 在查询过程中,对查询表达式解析、数据映射、流程处理的性能差异。

由于 SQL 的实际执行由数据库引擎负责,ORM 无法改变数据库层面的执行逻辑;不同 ORM 的差异主要体现在 SQL 拼接、表达式解析和数据映射等实现细节(例如插入操作可通过生成 SQL 或使用 BulkCopy 实现)。

因此,本测试不对实现方式完全不同的操作(如 BulkCopy)进行比较,而是重点衡量表达式解析与数据映射两方面的运行效率与内存占用。

测试声明

本测试不代表任何立场和原作者也没任何关系,仅是在研究、学习、优化、测试,对内部项目myTest整理过程中形成的测试,有其它测式可下载源码自行添加实现。

测试环境

项目

说明

测试框架

BenchmarkDotNet

测试数据库

SQLite(单机性能优,波动较小)

.NET 版本

.NET 6.0+

测试硬件

Intel Core i5-8265U CPU 1.60GHz

测试的ORM

下面列出了近年收集到的ORM,除Dapper外,未涉及到表达式解析的ORM没有加入此测试

代码语言:c#
复制
    <PackageReference Include="Chloe.SQLite" Version="5.55.0" />
    <PackageReference Include="Dapper" Version="2.1.66" />
    <PackageReference Include="FreeSql.Provider.Sqlite" Version="3.5.305" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.0-preview.6.23329.4" />
    <PackageReference Include="SqlSugarCore" Version="5.1.4.212-preview02" />
    <PackageReference Include="linq2db" Version="6.2.0" />
    <Reference Include="Fast.Framework">

具体测试项目如下

  1. 对表达式解析进行测试 testQueryCondition
  2. 对查询返回结果进行数据映射测试 testQueryResult
  3. 自定义数据映射测试 testQueryAnonymousResult
  4. 循环读取指定数据测试 testQueryLoop

测试代码明细

对表达式解析进行测试 testQueryCondition

指定了查询条件和返回结果筛选

这里可以反映表达式解析生成SQL效率,ORM技术核心最困难的部份,非常考验代码组织和逻辑

代码语言:c#
复制
query.Where(b => b.F_String == "111" && b.F_Decimal > 0 && b.F_Bool == true && b.F_String.StartsWith("abc")).Select(b => new { b.F_Float, b.F_Bool, b.F_Double, b.F_Byte, b.F_String, b.F_Decimal, b.F_Int64 }).ToSqlString();

对查询返回结果进行数据映射测试 testQueryResult

返回指定的数量的数据进行数据映射,以测试映射效率和内存使用情况

代码语言:c#
复制
query.Take(100).ToList();

自定义数据映射测试 testQueryAnonymousResult

指定数据映射的结构,以测试解析和映射效率、内存使用情况

这里使用了匿名对象,是一个比较特殊的处理

代码语言:c#
复制
query.Take(100).Select(b => new
           {
               b.Id,
               b.F_Float,
               b.F_Bool,
               b.F_DateTime,
               b.F_Decimal,
               b.F_Double,
               b.F_Int64
           }).ToList();

循环读取指定数据测试 testQueryLoop

循环多次查询,以测试查询和映射效率

这里验证数据连接效率和查询效率,虽然每次查询的数据量很小,但循环多次会放大差异,能更明显的看出差别

代码语言:c#
复制
for (var i = 0; i < 20; i++)
   {
       var item = query.Where(b => b.Id == i).ToList();
   }

Benchmark测试代码样例

代码语言:c#
复制
[MemoryDiagnoser]
   public class ConditionTest : TestBase
   {
       [Benchmark]
       public void TestCondition()
       {
           Invoke(b => b.testQueryCondition());
       }
   }

以下是具体测试结果,仅供参考

表格字段说明:

  • Mean: 所有测量值的算术平均值 ns纳秒 μs微秒 ms毫秒。
  • Error: 99.9% 置信区间的一半。
  • StdDev: 所有测量值的标准差。
  • Gen0: 第 0 代 GC 每 1000 次操作收集一次。
  • Gen1: 第 1 代 GC 每 1000 次操作收集一次。
  • Gen2: 第 2 代 GC 每 1000 次操作收集一次。
  • Allocated: 每次操作分配的内存(仅托管内存,包含所有内容,1KB = 1024B)。

TestCondition

Dapper由于没有表达式解析,空跑,最低和最高不管是效率和内存占用差了一数量级

提示:拼接SQL会导致注入风险,除了语法字符外还有字符编码问题

Method

ProvideType

Mean

Error

StdDev

Gen0

Gen1

Allocated

TestCondition

ChloeTest

90.75 μs

1.020 μs

0.954 μs

5.3711

16.68 KB

TestCondition

EfSqlliteTest

217.38 μs

2.950 μs

2.615 μs

19.5313

61.19 KB

TestCondition

FastFrameworkTest

94.90 μs

0.807 μs

0.630 μs

6.7139

20.71 KB

TestCondition

FreeSqlTest

779.56 μs

12.514 μs

11.706 μs

9.7656

3.9063

62.56 KB

TestCondition

LinqToDbTest

103.50 μs

2.002 μs

2.056 μs

6.1035

18.79 KB

TestCondition

MyTest

43.00 μs

0.505 μs

0.395 μs

4.8218

14.95 KB

TestCondition

SqlSugarTest

366.82 μs

7.205 μs

6.016 μs

33.6914

103.83 KB

TestResult

强类型直接转换,最低和最高差别两倍,EF效率比大部份好

Method

ProvideType

Mean

Error

StdDev

Gen0

Gen1

Allocated

TestResult

ChloeTest

582.8 μs

10.02 μs

8.37 μs

22.4609

70.68 KB

TestResult

DapperTest

500.8 μs

3.14 μs

2.94 μs

16.6016

52.68 KB

TestResult

EfSqlliteTest

1,127.4 μs

15.77 μs

14.75 μs

64.4531

202.9 KB

TestResult

FastFrameworkTest

11,561.9 μs

119.70 μs

93.46 μs

46.8750

153.21 KB

TestResult

FreeSqlTest

1,300.0 μs

24.25 μs

22.68 μs

31.2500

11.7188

96.32 KB

TestResult

LinqToDbTest

1,273.8 μs

20.71 μs

18.36 μs

17.5781

54.77 KB

TestResult

MyTest

600.5 μs

11.94 μs

11.16 μs

15.6250

48.34 KB

TestResult

SqlSugarTest

1,408.5 μs

27.42 μs

38.44 μs

46.8750

147.57 KB

TestAnonymousResult

Dapper由于没有结果筛选,同TestResult(SqlSugar内存溢出?)

Method

ProvideType

Mean

Error

StdDev

Gen0

Gen1

Allocated

TestAnonymousResult

ChloeTest

552.7 μs

5.48 μs

4.86 μs

20.5078

65.54 KB

TestAnonymousResult

DapperTest

484.2 μs

4.71 μs

4.18 μs

17.5781

55.8 KB

TestAnonymousResult

EfSqlliteTest

692.2 μs

11.48 μs

10.74 μs

33.2031

103.18 KB

TestAnonymousResult

FastFrameworkTest

7,838.4 μs

67.26 μs

52.51 μs

31.2500

122.4 KB

TestAnonymousResult

FreeSqlTest

1,940.9 μs

20.24 μs

18.93 μs

70.3125

11.7188

225.84 KB

TestAnonymousResult

LinqToDbTest

1,241.1 μs

18.58 μs

14.51 μs

15.6250

53.21 KB

TestAnonymousResult

MyTest

473.4 μs

4.67 μs

4.14 μs

12.6953

39.89 KB

TestAnonymousResult

SqlSugarTest

8,667.7 μs

133.08 μs

111.13 μs

1046.8750

3210.65 KB

TestQueryLoop

循环多次查询调用,由于调用sqlite驱动不同,时间差别较大,但从内存使用上能看出差别

Method

ProvideType

Mean

Error

StdDev

Gen0

Gen1

Allocated

TestQueryLoop

ChloeTest

2.535 ms

0.0427 ms

0.0379 ms

74.2188

231.78 KB

TestQueryLoop

DapperTest

1.433 ms

0.0274 ms

0.0243 ms

17.5781

54.7 KB

TestQueryLoop

EfSqlliteTest

5.410 ms

0.0697 ms

0.0618 ms

375.0000

1156.31 KB

TestQueryLoop

FastFrameworkTest

230.424 ms

4.5239 ms

4.0103 ms

666.6667

2332.27 KB

TestQueryLoop

FreeSqlTest

16.176 ms

0.3208 ms

0.6257 ms

93.7500

31.2500

664.93 KB

TestQueryLoop

LinqToDbTest

16.881 ms

0.2541 ms

0.2252 ms

125.0000

406.34 KB

TestQueryLoop

MyTest

2.058 ms

0.0405 ms

0.0379 ms

46.8750

148.13 KB

TestQueryLoop

SqlSugarTest

4.210 ms

0.0739 ms

0.0655 ms

210.9375

651.17 KB

引入不同数据库实现的方式

本次测试使用的sqlite数据库,各种引入的方式也不相司,大致分为三类:

  1. 引用相关数据库的项目扩展包(ORM主体+扩展+数据库驱动)
  2. 直接包含所有支持的数据库驱动(ORM主体+数据库驱动*N)
  3. 按需自动或手动配置引入的数据库驱动(ORM主体+按需数据库驱动)

对于第一种,多数项目都采用这种方式,缺点是需要引入多个包,增加了依赖关系,并且强绑定了数据库驱动

对于第二种,虽然可以支持多种数据库,但会增加项目的体积,并且可能引入不必要的依赖,也强绑定了数据库驱动

对于第三种,封装度最高,虽然可以按需引入数据库驱动,减少了项目的体积和依赖复杂度,但由于没有强依赖,部份实现可能由于驱动不一致而实现复杂,如 BulkCopy

以LinqToDb为例,直接引入LinqToDb包,手动配置连接串 new DataOptions().UseConnectionString(ProviderName.SQLite, ITest.sqlLiteDb) 它就能自别识别当前项目引入的sqlite驱动,并且对特殊方法也进行了封装(BulkCopy),不管是哪种数据库,引入驱动后所有方法行为一至,无其它依赖。

各种测试项目的引入方式如下表所示:

ProvideType

引入方式

ChloeTest

扩展包&手动配置

DapperTest

扩展包

EfSqlliteTest

扩展包

FastFrameworkTest

手动配置

FreeSqlTest

扩展包

MyTest

手动或自动配置

SqlSugarTest

全包含

LinqToDbTest

自动配置

如何使用此测试

  1. 使用Release发布此项目
  2. 运行dbTest.exe
  3. 输入序号选择需要运行测试的方法,示例如下
代码语言:cs
复制
---------------------[ Program ]---------------------
[1]  testQueryResult               [2]  testQueryAnonymousResult
[3]  testQueryCondition            [4]  testQueryLoop
[5]  testMethod
---------------------[ invokeAll ]---------------------
[6]  invokeAll
invoke method:
  1. 输入序号后回车,等待测试完成,测试结果会输出到控制台,并在运行目录生成BenchmarkDotNet.Artifacts文件夹,里面有详细的测试结果和分析报告
  2. 运行效果截图
    dbTest.gif
    dbTest.gif
    https://gitee.com/hubro/dbTest.git参考

使用 BenchmarkDotNet 对 .NET 代码进行性能基准测试

https://cloud.tencent.com/developer/article/2483382

mysql注入-字符编码技巧

https://developer.aliyun.com/article/1658273

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • ORM性能测试Benchmark(最终版)
  • 测试声明
  • 测试环境
  • 测试的ORM
    • 具体测试项目如下
  • 测试代码明细
    • 对表达式解析进行测试 testQueryCondition
    • 对查询返回结果进行数据映射测试 testQueryResult
    • 自定义数据映射测试 testQueryAnonymousResult
    • 循环读取指定数据测试 testQueryLoop
  • 以下是具体测试结果,仅供参考
    • TestCondition
    • TestResult
    • TestAnonymousResult
    • TestQueryLoop
  • 引入不同数据库实现的方式
    • 如何使用此测试
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档