HTML5技术

EntityFramework、Dapper vs 草根框架性能大比拼,数据库访问哪家强? - 我叫So(2)

字号+ 作者:H5之家 来源:博客园 2016-07-19 11:00 我要评论( )

为公平起见,所有测试都是在非 Debug 下运行,且都经过预热。总共测试5轮,下面是测试结果数据: ChloeQueryTest(ms) ChloeSqlQueryTest(ms) DapperQueryTest(ms) EFLinqQueryTest(ms) EFSqlQueryTest(ms) 第1轮 69

 

query50W


为公平起见,所有测试都是在非 Debug 下运行,且都经过预热。总共测试5轮,下面是测试结果数据:

 

ChloeQueryTest(ms)

ChloeSqlQueryTest(ms)

DapperQueryTest(ms)

EFLinqQueryTest(ms)

EFSqlQueryTest(ms)

第1轮

6976

7170

7948

7704

7744

第2轮

7357

6853

8410

8328

7783

第3轮

7610

7833

8107

9795

8706

第4轮

7296

6957

7760

8643

7873

第5轮

9636

6705

8805

8946

8544

平均

7775

7103

8206

8683

8130

上表是单纯测试用时,下面是一次查询50万条数据,GC次数情况。

运行效果图:

neicunceshi

GC统计结果,由于相同的代码运行,内存分配和GC情况都一样,所以这个测试不必运行多轮:

 

ChloeQueryTest

ChloeSqlQueryTest

DapperQueryTest

EFLinqQueryTest

EFSqlQueryTest

GC回收次数

22

22

38

40

35

可见,Chloe.ORM 在各方面略优,看到这个结果,估计大家觉得不可能。毕竟部分国人喜欢扬眉崇外,国外的月亮比国内圆。开始看到这个结果我也觉得有点不可思议,后来翻看 Dapper 源码,发现 Chloe 稍微快一点是出乎意外,但也在情理之中。EF 映射方式我没去了解,Chloe 和 Dapper 两者创建实体以及属性赋值都是用 Emit 生成委托,这方面性能可以说毫无差距。但不同的是,Chloe 从 DataReader 读数据是调用强类型方法(GetInt(int i)、GetDateTime(int i)、GetString(int i)...等),所以对于值类型数据的读取,避免了装箱和拆箱,从而减少了很多垃圾对象,随之 GC 次数减少。Dapper 则不然,它从 DataReader 读取数据是使用 DataReader[int i],其内部实现就是调用 DataReader.GetValue(int i),如果是值类型的数据,会引起大量的装箱和拆箱,需要 CPU 大量计算的同时还会产生很多的垃圾对象,随之 GC 次数增加。我想这就是 Chloe 在映射方面略胜一筹的原因。可见,Chloe 在映射方面已经做到极致。

其实,三者在映射能力方面差距不大。为了能看出性能差异,我们一次查询了大量的数据,这仅仅是为了测试效果,在实际生产中是不会有这种情况。

结论:

1.速度:取平均值,EFLinqQuery < DapperQuery ≈ EFSqlQuery < ChloeQuery < ChloeSqlQuery

2.GC 次数:EFLinqQuery < DapperQuery < EFSqlQuery< ChloeQuery = ChloeSqlQuery

2.查询能力测试

查询能力包括 sql 生成能力和映射能力,即一个完整的查询,比较符合程序实际运行情况。本测试针对 ORM 性能评测,为减少数据库执行 sql 耗时的干扰,我设计方案是,一次查询只查一条数据,同时考验下对 lambda 的解析能力,加了个 a.Id > id(0) 的 where 条件,执行多次查询(本测试我选择执行20000次查询)。

测试 2.1:

LoopQueryTest { static int takeCount = 1; static int queryCount = 20000; GCMemoryTest() { /* * 内存分配测试通过 vs 自带诊断与分析工具测,vs --> 分析 --> 性能与诊断 --> 内存使用率。 * 每次运行程序只能调用下面中的一个方法,不能同时调用 */ ChloeQueryTest(takeCount, queryCount); } SpeedTest() { long useTime = 0; //预热 ChloeQueryTest(1, 1); useTime = SW.Do(() => { ChloeQueryTest(takeCount, queryCount); }); Console.WriteLine("ChloeQueryTest 执行{0}次查询总用时:{1}ms", queryCount, useTime); GC.Collect(); useTime = SW.Do(() => { ChloeSqlQueryTest(takeCount, queryCount); }); Console.WriteLine("ChloeSqlQueryTest 执行{0}次查询总用时:{1}ms", queryCount, useTime); GC.Collect(); //预热 DapperQueryTest(1, 1); useTime = SW.Do(() => { DapperQueryTest(takeCount, queryCount); }); Console.WriteLine("DapperQueryTest 执行{0}次查询总用时:{1}ms", queryCount, useTime); GC.Collect(); //预热 EFLinqQueryTest(1, 1); useTime = SW.Do(() => { EFLinqQueryTest(takeCount, queryCount); }); Console.WriteLine("EFLinqQueryTest 执行{0}次查询总用时:{1}ms", queryCount, useTime); GC.Collect(); //预热 EFSqlQueryTest(1, 1); useTime = SW.Do(() => { EFSqlQueryTest(takeCount, queryCount); }); Console.WriteLine("EFSqlQueryTest 执行{0}次查询总用时:{1}ms", queryCount, useTime); GC.Collect(); Console.WriteLine("GAME OVER"); Console.ReadKey(); } static void ChloeQueryTest(int takeCount, int loops) { for (int i = 0; i < loops; i++) { using (MsSqlContext context = new MsSqlContext(DbHelper.ConnectionString)) { int id = 0; var list = context.Query<TestEntity>().Where(a => a.Id > id).Take(takeCount).ToList(); } } } static void ChloeSqlQueryTest(int takeCount, int loops) { for (int i = 0; i < loops; i++) { using (MsSqlContext context = new MsSqlContext(DbHelper.ConnectionString)) { int id = 0; var list = context.SqlQuery<TestEntity>(string.Format("select top {0} * from TestEntity where Id>@Id", takeCount.ToString()), DbParam.Create("@Id", id)).ToList(); } } } static void DapperQueryTest(int takeCount, int loops) { for (int i = 0; i < loops; i++) { using (IDbConnection conn = DbHelper.CreateConnection()) { int id = 0; var list = conn.Query<TestEntity>(string.Format("select top {0} * from TestEntity where Id>@Id", takeCount.ToString()), new { Id = id }).ToList(); } } } static void EFLinqQueryTest(int takeCount, int loops) { for (int i = 0; i < loops; i++) { using (EFContext efContext = new EFContext()) { int id = 0; var list = efContext.TestEntity.AsNoTracking().Where(a => a.Id > id).Take(takeCount).ToList(); } } } static void EFSqlQueryTest(int takeCount, int loops) { for (int i = 0; i < loops; i++) { using (EFContext efContext = new EFContext()) { int id = 0; var list = efContext.Database.SqlQuery<TestEntity>(string.Format("select top {0} * from TestEntity where Id>@Id", takeCount.ToString()), new SqlParameter("@Id", id)).ToList(); } } } }

View Code

运行效果图:

多次查询结果

运行5次,下面是用时结果:

 

ChloeQueryTest(ms)

ChloeSqlQueryTest(ms)

DapperQueryTest(ms)

EFLinqQueryTest(ms)

EFSqlQueryTest(ms)

第1轮

15083

12594

13134

41163

24339

第2轮

15597

12711

12133

40294

25281

第3轮

15356

11885

11587

44913

25707

第4轮

16419

13089

12803

46196

25635

第5轮

16216

12463

12221

40064

23749

平均

15734

12548

12375

42526

24942

再来看看GC情况:

 

ChloeQueryTest

ChloeSqlQueryTest

DapperQueryTest

EFLinqQueryTest

EFSqlQueryTest

GC回收次数

116

47

49

538

359

不测不知道,一测吓一跳。看到这个结果,我吃了那啥好几斤,不知道是我代码有问题还是怎么回事,EF 居然如此“差强人意”,好伤心。

 

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

相关文章
  • EntityFramework Core并发深挖详解,一纸长文,你准备好看完了吗? - Jeffcky

    EntityFramework Core并发深挖详解,一纸长文,你准备好看完了吗? -

    2017-04-05 14:03

  • EntityFramework Core不得不注意的性能优化意外收获,你会用错? - Jeffcky

    EntityFramework Core不得不注意的性能优化意外收获,你会用错? - J

    2017-04-05 14:00

  • DapperPoco -- 基于Dapper的、轻量级的、高性能的、简单的、灵活的ORM框架 - Frank.Cui

    DapperPoco -- 基于Dapper的、轻量级的、高性能的、简单的、灵活的OR

    2017-03-18 14:07

  • 用DapperExtensions和反射来实现一个通用搜索 - yt1983

    用DapperExtensions和反射来实现一个通用搜索 - yt1983

    2017-03-09 08:00

网友点评