.NET热门ORM真实的性能测试总结

上次对收集到的ORM作了详细的测试,并整理成了测试项目,下面针对测试结果,详细剖析

测试环境

项目 说明
测试框架 BenchmarkDotNet
测试数据库 SQLite(单机性能优,波动较小)
.NET 版本 .NET 6.0+
测试硬件 Intel Core i5-8265U CPU 1.60GHz
为什么使用 BenchmarkDotNet?
  1. 专业级微基准测试:BenchmarkDotNet 是 .NET 生态中最成熟的基准测试框架,能有效规避 JIT 预热、GC 干扰等常见陷阱,确保测试结果的准确性和可重复性。
  2. 统计严谨:自动执行多次迭代,输出均值、标准差、置信区间等统计指标,避免偶发波动对结论的误导。
  3. 结果易读:自动生成 Markdown/HTML/CSV 等多种格式的报告,便于横向对比和归档分析。
  4. 低侵入性:通过 Attribute 标记即可完成测试定义,无需修改业务代码,保持测试的纯净性。

为什么使用 SQLite?

  1. 消除网络与并发干扰 :作为嵌入式单机数据库,SQLite 不存在网络 I/O、连接池争用或服务端负载波动,能更纯粹地反映 ORM 或数据访问层本身的性能差异
  2. 环境一致性强 :所有测试在同一进程内完成,数据库文件可内存化(:memory:),避免磁盘 I/O 成为瓶颈,测试结果波动极小,适合做精细化对比。
  3. 轻量快速:无需安装、配置或启动外部服务,测试启动速度极快,便于 CI/CD 流水线中频繁执行。
  4. SQL 兼容性好 :支持标准 SQL 和事务,能覆盖大部分 CRUD 场景,虽非生产级数据库,但作为 "性能标尺" 已足够客观。

和之前网上写的测试相比(使用大型数据库,使用时间相减,不预热,不多次采样),本次更准确,更客观,更能反映真实的效率问题

测试的ORM

下面列出常见的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">(停止维护)
	<Reference Include="myTest">(私有项目)

测试结果关注点

除了运行耗时,内存分配也是非常重要的方面,谁也不想系统内存暴炸吧

下面是具体的测试结果,增加了耗时和内存排名

TestCondition

对表达式进行解析测试,执行一次

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();
Method ProvideType Mean Error StdDev Rank Gen0 Allocated
TestCondition ChloeTest 87.30 us 1.745 us 4.865 us 2 5.1270 16.27 KB
TestCondition EfSqlliteTest 227.82 us 5.201 us 14.922 us 3 19.5313 61.19 KB
TestCondition FastFrameworkTest 92.47 us 1.783 us 2.723 us 2 6.7139 20.71 KB
TestCondition FreeSqlTest 405.34 us 5.108 us 4.778 us 4 12.6953 40.84 KB
TestCondition LinqToDbTest 92.26 us 0.563 us 0.499 us 2 5.8594 18.29 KB
TestCondition MyTest 43.16 us 0.689 us 0.575 us 1 4.6997 14.41 KB
TestCondition SqlSugarTest 390.76 us 8.115 us 23.800 us 4 33.6914 103.94 KB

TestResult

一次查询100条数据,主要消耗在对象映射上

c# 复制代码
query.Take(100).ToList();
Method ProvideType Mean Error StdDev Rank Gen0 Allocated
TestResult ChloeTest 583.0 us 10.02 us 9.84 us 2 22.4609 70.68 KB
TestResult DapperTest 530.3 us 9.75 us 12.33 us 1 16.6016 52.68 KB
TestResult EfSqlliteTest 1,129.8 us 8.19 us 7.26 us 4 64.4531 202.9 KB
TestResult FastFrameworkTest 11,685.6 us 114.75 us 95.82 us 7 46.8750 153.21 KB
TestResult FreeSqlTest 719.6 us 15.48 us 45.40 us 3 23.4375 74.36 KB
TestResult LinqToDbTest 1,267.2 us 24.38 us 21.61 us 5 17.5781 54.6 KB
TestResult MyTest 563.9 us 3.62 us 3.39 us 2 10.7422 35.42 KB
TestResult SqlSugarTest 1,481.1 us 29.39 us 65.12 us 6 46.8750 147.58 KB

TestAnonymousResult

一次查询100条数据,映射为匿名对象

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();
Method ProvideType Mean Error StdDev Median Rank Gen0 Allocated
TestAnonymousResult ChloeTest 526.8 us 7.11 us 5.55 us 527.4 us 2 20.5078 65.47 KB
TestAnonymousResult DapperTest 510.1 us 7.21 us 6.02 us 508.3 us 2 17.5781 55.8 KB
TestAnonymousResult EfSqlliteTest 652.3 us 8.81 us 7.35 us 653.3 us 3 33.2031 102.21 KB
TestAnonymousResult FastFrameworkTest 8,186.3 us 199.25 us 584.38 us 7,999.0 us 6 31.2500 122.24 KB
TestAnonymousResult FreeSqlTest 1,252.1 us 11.99 us 10.63 us 1,251.6 us 5 66.4063 203.89 KB
TestAnonymousResult LinqToDbTest 1,203.3 us 8.91 us 8.75 us 1,202.3 us 4 15.6250 52.67 KB
TestAnonymousResult MyTest 463.2 us 9.25 us 21.81 us 455.0 us 1 10.7422 33.73 KB
TestAnonymousResult SqlSugarTest 9,063.9 us 181.09 us 522.50 us 8,839.5 us 7 1046.8750 3210.79 KB

TestQueryLoop

循环20次,查询一条记录

c# 复制代码
for (var i = 0; i < 20; i++)
   {
       var item = query.Where(b => b.Id == i).ToList();
   }
Method ProvideType Mean Error StdDev Rank Gen0 Gen1 Allocated
TestQueryLoop ChloeTest 2.816 ms 0.0557 ms 0.0494 ms 3 74.2188 - 231.78 KB
TestQueryLoop DapperTest 1.621 ms 0.0320 ms 0.0803 ms 1 17.5781 - 54.7 KB
TestQueryLoop EfSqlliteTest 6.139 ms 0.1165 ms 0.2327 ms 6 375.0000 - 1156.31 KB
TestQueryLoop FastFrameworkTest 234.891 ms 2.8211 ms 2.3557 ms 8 666.6667 - 2332.27 KB
TestQueryLoop FreeSqlTest 3.600 ms 0.0743 ms 0.2178 ms 4 74.2188 35.1563 228.63 KB
TestQueryLoop LinqToDbTest 16.907 ms 0.1417 ms 0.1257 ms 7 125.0000 - 409.78 KB
TestQueryLoop MyTest 2.274 ms 0.0536 ms 0.1556 ms 2 42.9688 - 134.7 KB
TestQueryLoop SqlSugarTest 4.737 ms 0.0800 ms 0.0786 ms 5 210.9375 - 651.24 KB

为什么不测试UPDATE/INSERT

由于 SQL 的实际执行由数据库引擎负责,ORM 无法改变数据库层面的执行逻辑;不同 ORM 的差异主要体现在 SQL 拼接、表达式解析和数据映射等实现细节(例如插入操作可通过生成 SQL 或使用 BulkCopy 实现),假定实现原理一样,测试UPDATE/INSERT无非是在测试SQL拼接,上面的测试完全可以满足。

使用AI进行总结

Q:对此测试进行性能和内存使用总结,排除FastFrameworkTest


AI:BEGIN

性能与内存使用总结(排除 FastFrameworkTest)

一、整体排名概况

本次测试覆盖了 7 个 ORM 框架(排除 FastFrameworkTest 后,实际对比 7 个:ChloeTest、DapperTest、EfSqlliteTest、FreeSqlTest、LinqToDbTest、MyTest、SqlSugarTest),在 4 种不同场景下进行了性能与内存测试。

各场景综合表现

框架 TestCondition TestResult TestAnonymousResult TestQueryLoop 综合排名
MyTest ⭐ 第 1 ⭐ 第 1 ⭐ 第 1 🥈 第 2 🏆 第 1
DapperTest --- 🥈 第 2 🥉 第 3 ⭐ 第 1 🥈 第 2
ChloeTest 🥉 第 3 🥉 第 3 第 4 第 4 🥉 第 3
FreeSqlTest 第 6 第 4 第 7 第 3 第 4
LinqToDbTest 第 4 第 5 第 2 第 7 第 5
SqlSugarTest 第 8 第 7 第 8 第 5 第 6
EfSqlliteTest 第 7 第 6 第 5 第 6 第 7

二、各场景详细分析

1. TestCondition(表达式解析)

测试表达式解析和 SQL 生成能力,不含数据读取

排名 框架 Mean Allocated 特点
1 MyTest 43.16 us 14.41 KB 🏆 ​性能 & 内存双冠​,领先优势明显
2 ChloeTest 87.30 us 16.27 KB 性能优秀,内存略高于 MyTest
3 LinqToDbTest 92.26 us 18.29 KB 性能接近 Chloe,内存控制良好
4 FastFrameworkTest 92.47 us 20.71 KB ⚠️ 已排除
5 FreeSqlTest 405.34 us 40.84 KB 性能落后约 4.6 倍于 MyTest
6 EfSqlliteTest 227.82 us 61.19 KB 内存占用较高
7 SqlSugarTest 390.76 us 103.94 KB 性能 & 内存双垫底

关键发现​:

  • MyTest 在表达式解析上具有碾压性优势,速度是第 2 名的 2 倍
  • SqlSugarTest 内存分配是 MyTest 的 7.2 倍 ,性能慢 9 倍

2. TestResult(实体映射 - 100 条数据)

查询 100 条数据并映射为实体对象,模拟真实业务场景

排名 框架 Mean Allocated 特点
1 MyTest 563.9 us 35.42 KB 🏆 ​内存最优​,性能极佳
2 DapperTest 530.3 us 52.68 KB ⚡ ​性能最快​(微秒级领先),内存良好
3 ChloeTest 583.0 us 70.68 KB 性能接近 Dapper,内存稍高
4 FreeSqlTest 719.6 us 74.36 KB 性能与内存均衡
5 LinqToDbTest 1,267.2 us 54.6 KB 内存控制较好,但性能偏慢
6 EfSqlliteTest 1,129.8 us 202.9 KB 内存飙升,性能慢于 FreeSql
7 SqlSugarTest 1,481.1 us 147.58 KB ❌ ​性能最慢​(MyTest 的 2.6 倍)

关键发现​:

  • DapperTest 以微弱优势获得性能第 1,MyTest 紧随其后
  • MyTest 在内存控制上遥遥领先,仅 35.42 KB
  • EfSqlliteTest 内存分配是 MyTest 的 5.7 倍

3. TestAnonymousResult(匿名对象映射 - 100 条数据)

查询 100 条数据并映射为​匿名对象​,减少实体跟踪开销

排名 框架 Mean Allocated 特点
1 MyTest 463.2 us 33.73 KB 🏆 ​双冠王​,性能 & 内存最优
2 LinqToDbTest 1,203.3 us 52.67 KB 💾 内存第二,但性能下降明显
3 DapperTest 510.1 us 55.8 KB ⚡ ​性能第二​,内存控制良好
4 ChloeTest 526.8 us 65.47 KB 性能接近 Dapper,内存稍高
5 EfSqlliteTest 652.3 us 102.21 KB 内存是 MyTest 的 3 倍
6 FreeSqlTest 1,252.1 us 203.89 KB ⚠️ 内存飙升,性能倒数第二
7 SqlSugarTest 9,063.9 us 3210.79 KB ❌ ​惨不忍睹 ​:内存是 MyTest 的 95 倍

关键发现​:

  • MyTest 在此场景表现出统治级优势
  • LinqToDbTest 在匿名对象映射时内存表现优异(52.67 KB),但性能慢于 Dapper/Chloe
  • SqlSugarTest 的 3210.79 KB 内存分配异常惊人,应引起重视

4. TestQueryLoop(循环 20 次单条查询)

循环 20 次单条查询,​模拟频繁单条查询场景​,测试连接管理、SQL 解析等开销

排名 框架 Mean Allocated 特点
1 DapperTest 1.621 ms 54.7 KB 🏆 ​性能王者​,轻量级优势明显
2 MyTest 2.274 ms 134.7 KB 🥈 性能第二,内存控制良好
3 FreeSqlTest 3.600 ms 228.63 KB 性能第三,内存中等
4 ChloeTest 2.816 ms 231.78 KB 性能略优于 FreeSql
5 SqlSugarTest 4.737 ms 651.24 KB ⚠️ 内存是 Dapper 的 12 倍
6 EfSqlliteTest 6.139 ms 1156.31 KB ❌ ​内存灾难​,Dapper 的 21 倍
7 LinqToDbTest 16.907 ms 409.78 KB ❌ ​性能灾难​,比 Dapper 慢 10 倍

关键发现​:

  • DapperTest 在循环单次查询场景中绝对领先,体现了其轻量级优势
  • MyTest 紧随其后,性能比 Dapper 慢约 40%,但远超其他框架
  • EfSqlliteTest 内存分配高达 1156.31 KB,说明 EF 在处理多次短查询时开销巨大

三、性能与内存对比矩阵

性能对比(以 MyTest 为基准)

框架 TestCondition TestResult TestAnonymous TestQueryLoop 平均倍数
MyTest 1.0x 1.0x 1.0x 1.0x 1.0x
DapperTest - 0.94x 1.10x 0.71x 0.92x
ChloeTest 2.02x 1.03x 1.14x 1.24x 1.36x
FreeSqlTest 9.39x 1.28x 2.70x 1.58x 3.74x
LinqToDbTest 2.14x 2.25x 2.60x 7.43x 3.61x
EfSqlliteTest 5.28x 2.00x 1.41x 2.70x 2.85x
SqlSugarTest 9.05x 2.63x 19.57x 2.08x 8.33x

内存对比(以 MyTest 为基准)

框架 TestCondition TestResult TestAnonymous TestQueryLoop 平均倍数
MyTest 1.0x 1.0x 1.0x 1.0x 1.0x
DapperTest - 1.49x 1.65x 0.41x 1.18x
ChloeTest 1.13x 2.00x 1.94x 1.72x 1.70x
FreeSqlTest 2.83x 2.10x 6.04x 1.70x 3.17x
LinqToDbTest 1.27x 1.54x 1.56x 3.04x 1.85x
EfSqlliteTest 4.25x 5.73x 3.03x 8.58x 5.40x
SqlSugarTest 7.21x 4.17x 95.19x 4.83x 27.85x

四、核心结论

🎯 综合性能冠军

MyTest3/4 场景中表现最优(TestCondition、TestAnonymousResult、TestResult 内存最优),在所有场景中均位列前二,综合性能最强,内存控制最佳。

💾 内存控制王者

MyTest 在全部 4 个场景中内存消耗均为最低或次低,平均内存仅为其他框架的 ​**11%-35%**​,表现卓越。

⚡ 特定场景冠军

  • DapperTest:纯 SQL 查询(TestResult)和循环小查询(TestQueryLoop)场景速度第一
  • MyTest:表达式解析(TestCondition)和匿名映射(TestAnonymousResult)场景速度第一

⚠️ 严重问题警告

  1. SqlSugarTestTestAnonymousResult 中性能异常:
    • 耗时 9ms(是 MyTest 的 19.6 倍
    • 内存 3.2MB(是 MyTest 的 95.2 倍
    • 建议检查是否存在 Bug 或配置错误
  2. EfSqlliteTest 内存消耗巨大:
    • 在所有场景中内存均为 MyTest 的 3-8.6 倍
    • 不适合内存敏感场景
  3. LinqToDbTest 循环查询极慢:
    • 耗时 16.9ms,是 MyTest 的 7.4 倍
    • 不适合高频小查询场景

AI:END

====================== 分割符 ======================================

经过分析,在国产开源项目整体效率上Chloe>FreeSql>SqlSugar,然而热度是反过来的,很有意思的情况

在此测试中,SqlSugarTest和FreeSqlTest并没有宣称的比Dapper强,为什么出现这种情况

本着实事求是的原则,找找他们官网是如何测试的

SqlSugar

https://www.donet5.com/Home/Doc?typeId=1180

下载测试代码,发现测试不标准,直接使用的DateTime,那此测试肯定比这个准确

c# 复制代码
public void Init(OrmType type)
{
    Console.WriteLine("GetAll");
    var eachCount = 1;

    var beginDate = DateTime.Now;
    for (int i = 0; i < 10; i++)
    {
        switch (type)
        {
            case OrmType.SqlSugar:
                SqlSugarTest(eachCount);
                break;
            case OrmType.EF:
                EFTest(eachCount);
                break;
            default:
                break;
        }
    }
    Console.WriteLine("总计:" + (DateTime.Now - beginDate).TotalMilliseconds / 1000.0);
    
}
_CharSource.Add(new PerformanceTestChartModel
{
    Name = name,
    Time = GetTime(),
    CPU = GetCurrentProcessSize()
});
rollBack($"总共执行时间:{GetTime()}秒");
private double GetTime()
{
    _endTime = DateTime.Now;
    return (_endTime - _beginTime).TotalMilliseconds / 1000.0;
}

SqlSugar除了查询匿名对象内存会爆炸,还有其它BUG,见

https://gitee.com/hubro/dbTest/blob/master/items/SqlSugarTest.cs

FreeSql

https://freesql.net/guide/performance.html

下载源代码,找到自带的测试项目FreeSql.Bechmarker,仔细检查测试,发现这个测试项目写得有点问题,作了以下修改

  1. 改为SQLite数据库
  2. 注释掉IterationSetup,以自动采样率
  3. 增加表结构字段数
    重新运行测试OrmVsSelect得到结果,跟此测试的结果差不多
Method size Mean Error StdDev Median Rank Gen0 Gen1 Allocated
FreeSqlSelect 100 699.0 μs 10.93 μs 9.69 μs 699.6 μs 2 27.3438 - 84.39 KB
SqlSugarSelect 100 1,701.3 μs 46.45 μs 136.22 μs 1,691.0 μs 5 39.0625 - 131.5 KB
EfCoreSelect 100 909.3 μs 16.50 μs 19.00 μs 905.2 μs 3 39.0625 - 125.75 KB
DapperSelete 100 307.1 μs 7.68 μs 22.39 μs 301.9 μs 1 13.1836 - 41.32 KB
FreeSqlSelect 500 3,175.2 μs 93.43 μs 275.49 μs 3,086.2 μs 6 109.3750 31.2500 393.77 KB
SqlSugarSelect 500 7,309.5 μs 216.67 μs 638.86 μs 7,051.8 μs 8 125.0000 31.2500 475.49 KB
EfCoreSelect 500 3,746.5 μs 106.78 μs 314.83 μs 3,625.6 μs 7 117.1875 23.4375 413.27 KB
DapperSelete 500 1,317.8 μs 39.90 μs 117.64 μs 1,329.9 μs 4 64.4531 - 200.49 KB

最后

此轮测试可以反映和ORM的效率水平,侧面反映了编码水平和整体结构设计

附上测试项目源码

https://gitee.com/hubro/dbTest.git

相关推荐
FPGA验证小袁2 天前
Wishbone BFM 设计与实现:从手写总线到自动化自检
测试
刘棕霆4 天前
30—AI Skill 怎么写才可测:Skill 编写规范与设计方法论
aigc·ai编程·测试
ClouGence5 天前
Selenium、Playwright、CueCast 深度对比:Web 自动化测试工具怎么选
selenium·测试
刘棕霆5 天前
29—AI Skill 测评集如何保持有效:从线上负反馈到 regression 用例
aigc·ai编程·测试
得物技术5 天前
AI UITester:AI Native 的 UI 自动化测试新范式|得物技术
llm·aigc·测试
析数塔5 天前
AI 时代测试开发新范式:从用例验证到 Agent 评测体系
agent·测试·aiops
ClouGence6 天前
Vibe Coding 之后,UI 测试如何跟上开发速度?
测试·vibecoding
刘棕霆6 天前
27—AI Skill 测评如何避免确认偏误:盲测对比与解盲分析
aigc·ai编程·测试