.NET热门ORM真实的性能测试总结
上次对收集到的ORM作了详细的测试并整理成了测试项目下面针对测试结果详细剖析测试环境项目说明测试框架BenchmarkDotNet测试数据库SQLite单机性能优波动较小.NET 版本.NET 6.0测试硬件Intel Core i5-8265U CPU 1.60GHz为什么使用 BenchmarkDotNet专业级微基准测试BenchmarkDotNet 是 .NET 生态中最成熟的基准测试框架能有效规避 JIT 预热、GC 干扰等常见陷阱确保测试结果的准确性和可重复性。统计严谨自动执行多次迭代输出均值、标准差、置信区间等统计指标避免偶发波动对结论的误导。结果易读自动生成 Markdown/HTML/CSV 等多种格式的报告便于横向对比和归档分析。低侵入性通过 Attribute 标记即可完成测试定义无需修改业务代码保持测试的纯净性。为什么使用 SQLite消除网络与并发干扰作为嵌入式单机数据库SQLite 不存在网络 I/O、连接池争用或服务端负载波动能更纯粹地反映 ORM 或数据访问层本身的性能差异。环境一致性强所有测试在同一进程内完成数据库文件可内存化:memory:避免磁盘 I/O 成为瓶颈测试结果波动极小适合做精细化对比。轻量快速无需安装、配置或启动外部服务测试启动速度极快便于 CI/CD 流水线中频繁执行。SQL 兼容性好支持标准 SQL 和事务能覆盖大部分 CRUD 场景虽非生产级数据库但作为“性能标尺”已足够客观。和之前网上写的测试相比(使用大型数据库使用时间相减不预热不多次采样)本次更准确更客观更能反映真实的效率问题测试的ORM下面列出常见的ORM特别是国产PackageReference IncludeChloe.SQLite Version5.55.0 /(半活跃) PackageReference IncludeDapper Version2.1.66 / PackageReference IncludeFreeSql.Provider.Sqlite Version3.5.305 / PackageReference IncludeMicrosoft.EntityFrameworkCore.Sqlite Version8.0.0-preview.6.23329.4 / PackageReference IncludeSqlSugarCore Version5.1.4.212-preview02 / PackageReference Includelinq2db Version6.2.0 / Reference IncludeFast.Framework(停止维护) Reference IncludemyTest(私有项目)测试结果关注点除了运行耗时内存分配也是非常重要的方面谁也不想系统内存暴炸吧下面是具体的测试结果增加了耗时和内存排名TestCondition对表达式进行解析测试执行一次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();MethodProvideTypeMeanErrorStdDevRankGen0AllocatedTestConditionChloeTest87.30 us1.745 us4.865 us25.127016.27 KBTestConditionEfSqlliteTest227.82 us5.201 us14.922 us319.531361.19 KBTestConditionFastFrameworkTest92.47 us1.783 us2.723 us26.713920.71 KBTestConditionFreeSqlTest405.34 us5.108 us4.778 us412.695340.84 KBTestConditionLinqToDbTest92.26 us0.563 us0.499 us25.859418.29 KBTestConditionMyTest43.16 us0.689 us0.575 us14.699714.41 KBTestConditionSqlSugarTest390.76 us8.115 us23.800 us433.6914103.94 KBTestResult一次查询100条数据主要消耗在对象映射上query.Take(100).ToList();MethodProvideTypeMeanErrorStdDevRankGen0AllocatedTestResultChloeTest583.0 us10.02 us9.84 us222.460970.68 KBTestResultDapperTest530.3 us9.75 us12.33 us116.601652.68 KBTestResultEfSqlliteTest1,129.8 us8.19 us7.26 us464.4531202.9 KBTestResultFastFrameworkTest11,685.6 us114.75 us95.82 us746.8750153.21 KBTestResultFreeSqlTest719.6 us15.48 us45.40 us323.437574.36 KBTestResultLinqToDbTest1,267.2 us24.38 us21.61 us517.578154.6 KBTestResultMyTest563.9 us3.62 us3.39 us210.742235.42 KBTestResultSqlSugarTest1,481.1 us29.39 us65.12 us646.8750147.58 KBTestAnonymousResult一次查询100条数据映射为匿名对象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();MethodProvideTypeMeanErrorStdDevMedianRankGen0AllocatedTestAnonymousResultChloeTest526.8 us7.11 us5.55 us527.4 us220.507865.47 KBTestAnonymousResultDapperTest510.1 us7.21 us6.02 us508.3 us217.578155.8 KBTestAnonymousResultEfSqlliteTest652.3 us8.81 us7.35 us653.3 us333.2031102.21 KBTestAnonymousResultFastFrameworkTest8,186.3 us199.25 us584.38 us7,999.0 us631.2500122.24 KBTestAnonymousResultFreeSqlTest1,252.1 us11.99 us10.63 us1,251.6 us566.4063203.89 KBTestAnonymousResultLinqToDbTest1,203.3 us8.91 us8.75 us1,202.3 us415.625052.67 KBTestAnonymousResultMyTest463.2 us9.25 us21.81 us455.0 us110.742233.73 KBTestAnonymousResultSqlSugarTest9,063.9 us181.09 us522.50 us8,839.5 us71046.87503210.79 KBTestQueryLoop循环20次查询一条记录for (var i 0; i 20; i) { var item query.Where(b b.Id i).ToList(); }MethodProvideTypeMeanErrorStdDevRankGen0Gen1AllocatedTestQueryLoopChloeTest2.816 ms0.0557 ms0.0494 ms374.2188-231.78 KBTestQueryLoopDapperTest1.621 ms0.0320 ms0.0803 ms117.5781-54.7 KBTestQueryLoopEfSqlliteTest6.139 ms0.1165 ms0.2327 ms6375.0000-1156.31 KBTestQueryLoopFastFrameworkTest234.891 ms2.8211 ms2.3557 ms8666.6667-2332.27 KBTestQueryLoopFreeSqlTest3.600 ms0.0743 ms0.2178 ms474.218835.1563228.63 KBTestQueryLoopLinqToDbTest16.907 ms0.1417 ms0.1257 ms7125.0000-409.78 KBTestQueryLoopMyTest2.274 ms0.0536 ms0.1556 ms242.9688-134.7 KBTestQueryLoopSqlSugarTest4.737 ms0.0800 ms0.0786 ms5210.9375-651.24 KB为什么不测试UPDATE/INSERT由于 SQL 的实际执行由数据库引擎负责ORM 无法改变数据库层面的执行逻辑不同 ORM 的差异主要体现在 SQL 拼接、表达式解析和数据映射等实现细节例如插入操作可通过生成 SQL 或使用 BulkCopy 实现假定实现原理一样测试UPDATE/INSERT无非是在测试SQL拼接上面的测试完全可以满足。使用AI进行总结Q:对此测试进行性能和内存使用总结,排除FastFrameworkTestAI:BEGIN性能与内存使用总结排除 FastFrameworkTest一、整体排名概况本次测试覆盖了 7 个 ORM 框架排除 FastFrameworkTest 后实际对比 7 个ChloeTest、DapperTest、EfSqlliteTest、FreeSqlTest、LinqToDbTest、MyTest、SqlSugarTest在 4 种不同场景下进行了性能与内存测试。各场景综合表现框架TestConditionTestResultTestAnonymousResultTestQueryLoop综合排名MyTest⭐ 第 1⭐ 第 1⭐ 第 1 第 2 第 1DapperTest— 第 2 第 3⭐ 第 1 第 2ChloeTest 第 3 第 3第 4第 4 第 3FreeSqlTest第 6第 4第 7第 3第 4LinqToDbTest第 4第 5第 2第 7第 5SqlSugarTest第 8第 7第 8第 5第 6EfSqlliteTest第 7第 6第 5第 6第 7二、各场景详细分析1. TestCondition表达式解析测试表达式解析和 SQL 生成能力不含数据读取排名框架MeanAllocated特点1MyTest43.16 us14.41 KB 性能 内存双冠领先优势明显2ChloeTest87.30 us16.27 KB性能优秀内存略高于 MyTest3LinqToDbTest92.26 us18.29 KB性能接近 Chloe内存控制良好4FastFrameworkTest92.47 us20.71 KB⚠️ 已排除5FreeSqlTest405.34 us40.84 KB性能落后约 4.6 倍于 MyTest6EfSqlliteTest227.82 us61.19 KB内存占用较高7SqlSugarTest390.76 us103.94 KB❌性能 内存双垫底关键发现MyTest 在表达式解析上具有碾压性优势速度是第 2 名的2 倍SqlSugarTest 内存分配是 MyTest 的 7.2 倍性能慢9 倍2. TestResult实体映射 - 100 条数据查询 100 条数据并映射为实体对象模拟真实业务场景排名框架MeanAllocated特点1MyTest563.9 us35.42 KB 内存最优性能极佳2DapperTest530.3 us52.68 KB⚡ 性能最快微秒级领先内存良好3ChloeTest583.0 us70.68 KB性能接近 Dapper内存稍高4FreeSqlTest719.6 us74.36 KB性能与内存均衡5LinqToDbTest1,267.2 us54.6 KB内存控制较好但性能偏慢6EfSqlliteTest1,129.8 us202.9 KB内存飙升性能慢于 FreeSql7SqlSugarTest1,481.1 us147.58 KB❌ 性能最慢MyTest 的 2.6 倍关键发现DapperTest 以微弱优势获得性能第 1MyTest 紧随其后MyTest 在内存控制上遥遥领先仅 35.42 KBEfSqlliteTest 内存分配是 MyTest 的5.7 倍3. TestAnonymousResult匿名对象映射 - 100 条数据查询 100 条数据并映射为匿名对象减少实体跟踪开销排名框架MeanAllocated特点1MyTest463.2 us33.73 KB 双冠王性能 内存最优2LinqToDbTest1,203.3 us52.67 KB 内存第二但性能下降明显3DapperTest510.1 us55.8 KB⚡ 性能第二内存控制良好4ChloeTest526.8 us65.47 KB性能接近 Dapper内存稍高5EfSqlliteTest652.3 us102.21 KB内存是 MyTest 的 3 倍6FreeSqlTest1,252.1 us203.89 KB⚠️ 内存飙升性能倒数第二7SqlSugarTest9,063.9 us3210.79 KB❌ 惨不忍睹内存是 MyTest 的95 倍关键发现MyTest 在此场景表现出统治级优势LinqToDbTest 在匿名对象映射时内存表现优异52.67 KB但性能慢于 Dapper/ChloeSqlSugarTest 的 3210.79 KB 内存分配异常惊人应引起重视4. TestQueryLoop循环 20 次单条查询循环 20 次单条查询模拟频繁单条查询场景测试连接管理、SQL 解析等开销排名框架MeanAllocated特点1DapperTest1.621 ms54.7 KB 性能王者轻量级优势明显2MyTest2.274 ms134.7 KB 性能第二内存控制良好3FreeSqlTest3.600 ms228.63 KB性能第三内存中等4ChloeTest2.816 ms231.78 KB性能略优于 FreeSql5SqlSugarTest4.737 ms651.24 KB⚠️ 内存是 Dapper 的 12 倍6EfSqlliteTest6.139 ms1156.31 KB❌ 内存灾难Dapper 的 21 倍7LinqToDbTest16.907 ms409.78 KB❌ 性能灾难比 Dapper 慢 10 倍关键发现DapperTest在循环单次查询场景中绝对领先体现了其轻量级优势MyTest 紧随其后性能比 Dapper 慢约 40%但远超其他框架EfSqlliteTest 内存分配高达 1156.31 KB说明 EF 在处理多次短查询时开销巨大三、性能与内存对比矩阵性能对比以 MyTest 为基准框架TestConditionTestResultTestAnonymousTestQueryLoop平均倍数MyTest1.0x1.0x1.0x1.0x1.0xDapperTest-0.94x1.10x0.71x0.92xChloeTest2.02x1.03x1.14x1.24x1.36xFreeSqlTest9.39x1.28x2.70x1.58x3.74xLinqToDbTest2.14x2.25x2.60x7.43x3.61xEfSqlliteTest5.28x2.00x1.41x2.70x2.85xSqlSugarTest9.05x2.63x19.57x2.08x8.33x内存对比以 MyTest 为基准框架TestConditionTestResultTestAnonymousTestQueryLoop平均倍数MyTest1.0x1.0x1.0x1.0x1.0xDapperTest-1.49x1.65x0.41x1.18xChloeTest1.13x2.00x1.94x1.72x1.70xFreeSqlTest2.83x2.10x6.04x1.70x3.17xLinqToDbTest1.27x1.54x1.56x3.04x1.85xEfSqlliteTest4.25x5.73x3.03x8.58x5.40xSqlSugarTest7.21x4.17x95.19x4.83x27.85x四、核心结论 综合性能冠军MyTest在3/4场景中表现最优TestCondition、TestAnonymousResult、TestResult 内存最优在所有场景中均位列前二综合性能最强内存控制最佳。 内存控制王者MyTest在全部 4 个场景中内存消耗均为最低或次低平均内存仅为其他框架的 **11%-35%**表现卓越。⚡ 特定场景冠军DapperTest纯 SQL 查询TestResult和循环小查询TestQueryLoop场景速度第一MyTest表达式解析TestCondition和匿名映射TestAnonymousResult场景速度第一⚠️ 严重问题警告SqlSugarTest在TestAnonymousResult中性能异常耗时 9ms是 MyTest 的 19.6 倍内存 3.2MB是 MyTest 的 95.2 倍建议检查是否存在 Bug 或配置错误EfSqlliteTest内存消耗巨大在所有场景中内存均为 MyTest 的3-8.6 倍不适合内存敏感场景LinqToDbTest循环查询极慢耗时 16.9ms是 MyTest 的7.4 倍不适合高频小查询场景AI:END 分割符 经过分析在国产开源项目整体效率上ChloeFreeSqlSqlSugar然而热度是反过来的很有意思的情况在此测试中SqlSugarTest和FreeSqlTest并没有宣称的比Dapper强为什么出现这种情况本着实事求是的原则找找他们官网是如何测试的SqlSugarhttps://www.donet5.com/Home/Doc?typeId1180下载测试代码发现测试不标准直接使用的DateTime那此测试肯定比这个准确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.csFreeSqlhttps://freesql.net/guide/performance.html下载源代码找到自带的测试项目FreeSql.Bechmarker仔细检查测试发现这个测试项目写得有点问题作了以下修改改为SQLite数据库注释掉[IterationSetup]以自动采样率增加表结构字段数重新运行测试OrmVsSelect得到结果跟此测试的结果差不多MethodsizeMeanErrorStdDevMedianRankGen0Gen1AllocatedFreeSqlSelect100699.0 μs10.93 μs9.69 μs699.6 μs227.3438-84.39 KBSqlSugarSelect1001,701.3 μs46.45 μs136.22 μs1,691.0 μs539.0625-131.5 KBEfCoreSelect100909.3 μs16.50 μs19.00 μs905.2 μs339.0625-125.75 KBDapperSelete100307.1 μs7.68 μs22.39 μs301.9 μs113.1836-41.32 KBFreeSqlSelect5003,175.2 μs93.43 μs275.49 μs3,086.2 μs6109.375031.2500393.77 KBSqlSugarSelect5007,309.5 μs216.67 μs638.86 μs7,051.8 μs8125.000031.2500475.49 KBEfCoreSelect5003,746.5 μs106.78 μs314.83 μs3,625.6 μs7117.187523.4375413.27 KBDapperSelete5001,317.8 μs39.90 μs117.64 μs1,329.9 μs464.4531-200.49 KB最后此轮测试可以反映和ORM的效率水平侧面反映了编码水平和整体结构设计附上测试项目源码https://gitee.com/hubro/dbTest.git