Apworks框架各种仓储实现的性能基准测试

开发 后端
周末抽空简单地对Apworks框架所支持的三种仓储实现:Entity Framework、NHibernate以及MongoDB进行了性能基准测试,并对其结果进行对比。从对比的结果来看,MongoDB仓储的性能要远胜于其它两者。基准测试采用的是我在两年前开发的一个基于Visual Studio Unit Test Framework的基准测试程序(名为:Visual Benchmark),该程序可以设定基准测试引擎,并对测试结果进行图形化呈现。在本文末尾会简要地对此软件进行介绍,有兴趣的朋友不妨了解一下。

单体测试的设计

由于Visual Benchmark是基于Visual Studio Unit Test Framework的单体测试用例的,因此,我们只需要在Visual Studio中开发有待测试的单体测试程序即可。在开发单体测试程序之前,首先让我们了解一下Visual Benchmark所支持的“迭代基准测试”模式。所谓“迭代基准测试”,意思就是Visual Benchmark会循环地调用单体测试方法并在每次调用结束时,统计当前“代”中所消耗的CPU周期或者时间;当Visual Benchmark完成整个测试之后,会将各代的统计结果组织并显示出来。在这个过程中,单体测试方法可以通过Visual Benchmark所提供的基准测试参数来获得当前所处的“代”数(也就是循环因子的值),然后就可以基于这个“代”数对测试数据进行模拟,以反映出随着测试数据的增长,单体测试方法的执行效率。

举例来说,在测试三种不同的仓储对于聚合的保存执行效率时,我首先在Visual Studio中新建了一个Unit Test类,并在类中定义了两个成员变量:

  1. [TestClass] 
  2. public class InsertAggregateRootsTest 
  3.     private int thisIteration; 
  4.     private IEnumerable<SalesOrder> mockSalesOrders; 
  5.     // 其它部分暂时省略 

第一个成员变量thisIteration用来保存从Visual Benchmark传入的“代”数;而第二个成员变量mockSalesOrders则是保存了一组即将通过仓储插入的聚合模拟数据。

接下来,我在这个测试类中加入了Test Initialize的方法,以便在每次测试方法被调用前,执行一些数据初始化的操作。在这个方法中,会对以上两个成员变量初始化,同时清空后台数据库,为执行测试做准备。

  1. [TestInitialize()] 
  2. public void MyTestInitialize() 
  3.     thisIteration = (int)BenchmarkRuntimeArgs.Instance.ThisIteration; 
  4.     mockSalesOrders = Helper.MockSalesOrders(thisIteration); 
  5.     Helper.ClearSQLServerTables(); 
  6.     Helper.ClearMongoDB(); 

在上面的方法中,首先使用BenchmarkRuntimeArgs.Instance.ThisIteration对thisIteration进行初始化,以便获得当前测试的“代”。BenchmarkRuntimeArgs是一个跨应用程序域的单件(Singleton),在每次执行Benchmark之前都会被初始化。然后根据获得的“代”数,创建聚合模拟数据。此处mockSalesOrders中所包含的数据量会随着“代”数的增长而增加,以反映随着数据量的增长,被测函数的性能趋势。最后,使用Helper类清空后台数据库。

由于仓储的实现是基于不同的应用框架,因此,在所有测试启动前,需要对这些框架进行初始化。有写过单体测试的朋友都知道,这部分逻辑应该写在Class Initialize的方法中:

  1. [ClassInitialize()] 
  2. public static void MyClassInitialize(TestContext testContext) 
  3.     Database.SetInitializer<EntityFrameworkDbContext>(new 
  4.         DropCreateDatabaseIfModelChanges<EntityFrameworkDbContext>()); 
  5.     MongoDBRepositoryContext.RegisterConventions(); 
  6.     if (!BsonClassMap.IsClassMapRegistered(typeof(SalesLine))) 
  7.     { 
  8.         BsonClassMap.RegisterClassMap<SalesLine>(p => 
  9.         { 
  10.             p.AutoMap(); 
  11.             p.UnmapProperty<SalesOrder>(q => q.SalesOrder); 
  12.         }); 
  13.     } 

在这里并没有使用代码的方式对NHibernate框架进行初始化,因为NHibernate的初始化过程是由Apworks中的NHibernateApplicationConfiguration类型完成的,这个类型依赖应用程序的app/web.config文件。所以我们需要在单体测试项目中添加app.config以及相关的配置节点。篇幅原因,这里就不贴app.config的代码了,文章末尾我会给出源代码。Visual Benchmark支持在“客户应用程序域”(Client AppDomain)中装载app/web.config文件。

现在,可以开始写测试方法了,以下是基于三种不同仓储实现的测试方法。从各方法中我们可以看到,除了所创建的IRepositoryContext、IRepository的具体实现不同之外,其它的操作逻辑完全相同:即通过仓储对聚合进行保存:

  1. [TestMethod] 
  2. public void EntityFramework() 
  3.     using (IRepositoryContext context = new EntityFrameworkRepositoryContext(new EntityFrameworkDbContext())) 
  4.     { 
  5.         IRepository<SalesOrder> salesOrderRepository = new EntityFrameworkRepository<SalesOrder>(context); 
  6.         foreach (var salesOrder in mockSalesOrders) 
  7.             salesOrderRepository.Add(salesOrder); 
  8.         context.Commit(); 
  9.     } 
  10.   
  11. [TestMethod] 
  12. public void NHibernate() 
  13.     using (IRepositoryContext context = new NHibernateContext(new NHibernateApplicationConfiguration())) 
  14.     { 
  15.         IRepository<SalesOrder> salesOrderRepository = new NHibernateRepository<SalesOrder>(context); 
  16.         foreach (var salesOrder in mockSalesOrders) 
  17.             salesOrderRepository.Add(salesOrder); 
  18.         context.Commit(); 
  19.     } 
  20.   
  21. [TestMethod] 
  22. public void MongoDB() 
  23.     using (IRepositoryContext context = new MongoDBRepositoryContext(new MongoDBRepositoryContextSettings())) 
  24.     { 
  25.         IRepository<SalesOrder> salesOrderRepository = new MongoDBRepository<SalesOrder>(context); 
  26.         foreach (var salesOrder in mockSalesOrders) 
  27.             salesOrderRepository.Add(salesOrder); 
  28.         context.Commit(); 
  29.     } 

执行测试

首先,我们在Visual Studio中测试这三个方法,以确保每个方法都能够正确完成。在启动测试之前,先回到上面的MyTestInitialize方法,将thisIteration设置为一个固定的整数值,比如20,以便测试能够正常启动。在完成三个方法的测试之后,我们可以通过Test Results窗口看到测试结果。

image

打开Visual Benchmark,新建一个Session,在“打开”对话框中,选择已经编译好的DLL文件,此时Visual Benchmark会将其中包含的所有的测试类和测试方法加载到左边的树形结构中。在树形结构中,选中需要测试的方法,然后单击“开始”按钮,Visual Benchmark便会针对所选的测试方法进行基准测试。最后,会根据不同的测试引擎的设计,将结果显示出来。

image

测试结果

Visual Benchmark能够根据设置,采用一些减噪手段以尽量保证测试结果的真实性。通过所测结果不难看出,在我所测试的三个场景中,基于MongoDB实现的仓储,性能要优于其它两者。而NHibernate仓储又要好于Entity Framework仓储。

测试环境

以下是执行测试的环境配置:

CPU:Intel Core i5-540M Cores: 2 Logical: 4

Chipset:Intel QM57 (IbexPeak-M DO)

Memory:Hynix 666.7MHz (PC3-10600) 2048MB x1, Kingston 666.7MHz (PC3-10600) 4096MB x1. Totally 6144MB

OS:Microsoft Windows 7 Enterprise (x64) Build 7601

场景一:聚合保存

Insert

注:上图中X轴表示的是“代”数,亦即模拟的聚合数量;Y轴表示执行时间(毫秒数)。下同。

场景二:聚合查询

Retrieve

注:在此场景中,EntityFramework支线所表示的是使用Eager Loading将SalesOrder及其下所有Sales Lines实体读出所开销的时间;而EntityFramework_NoEagerLoad支线所表示的是仅读出SalesOrder(不包括其下所有Sales Lines)所开销的时间。

场景三:查询所有并删除

FindAndDelete

关于Visual Benchmark

Visual Benchmark是我在2010年开发的一款基于Visual Studio单体测试框架的性能基准测试程序,从整体上看,Visual Benchmark具有如下架构设计:

image

首先,Visual Benchmark和被测试的程序集都是基于Microsoft .NET Framework的,在Visual Benchmark中,基准测试的执行是以Session为单位的。Engine Management System为Visual Benchmark提供了安全的、可扩展的基准测试引擎管理系统,因此,通过这套管理系统,用户可以选用各种不同的引擎进行测试,开发人员也可以根据自己的实际需求对引擎进行二次开发与定制,并应用到Visual Benchmark系统中。

其次,当Session被打开时,它会通过Remote Proxy将被测试的程序集装载到客户应用程序域(Client AppDomain)中。这样做的理由是:1、能够在完成测试后,以AppDomain.Unload的方式卸载被测试程序集;2、能够在装载程序集时,同时将app/web.config和resource都装载到Client AppDomain中,以此模拟真实的执行环境。

功能技术特点

Visual Benchmark具有如下功能技术特点:

可定制的基准测量标尺:开发人员可以自己开发基准测试的测量标尺。目前仅支持两种:StopwatchTickRuler和StopwatchMillisecondsRuler。上文的测试采用的是StopwatchMillisecondsRuler

可定制的测试引擎:开发人员可以根据需求定制开发测试引擎。框架提供了完整的引擎定制功能,这包括:引擎的元数据(例如名称、作者、描述等)、版本、配置界面、结果显示界面以及HTML文档。目前支持Iterated Throughput、Simple、Simple Iteration以及Throughput四种引擎。上文的测试采用了Simple Iteration引擎

减噪选项:使用减噪选项以获得更真实的测试数据。Visual Benchmark提供两个减噪选项:在每次执行测试之前强制垃圾回收、丢弃第一次的测试结果。测试引擎也会根据情况提供获取平均执行时间的选项

在客户应用程序域(Client AppDomain)中执行基准测试:能够对单体测试环境进行模拟,被测方法能够正常地访问配置文件和资源文件

跨AppDomain的单件(Singleton)实现:能够方便地在单体测试方法中读取Visual Benchmark的相关参数信息

多线程执行:用户可以随时停止Benchmark的执行

界面截图

基于两种不同引擎的执行结果显示

image

测试引擎的配置界面与文档界面

image

Session信息与客户应用程序域(Client AppDomain)信息

image

总结

本文对Apworks框架中所支持的三种仓储实现进行了性能上的基准测试,并得出了测试结果。在最开始的时候,我是打算结合Visual Studio的测试框架来完成这些工作的,但后来发现Visual Studio的测试框架所提供的功能并不能达到我的需求,之前也采用了Visual Studio的Load Test来做压力测试,但是效果并不算太理想。在下才疏学浅,并没有弄通Visual Studio提供的强大测试功能,所以也只能借用我之前写的Visual Benchmark程序了。如果有读者朋友知道如何在Visual Studio中完成类似的测试工作,还烦请告知在下,我会虚心向您学习。

下一步,我将对Apworks框架的线程安全性做一些评估,等到有了满意的结果,我也会将相关经验分享出来。

原文链接:http://www.cnblogs.com/daxnet/archive/2012/07/31/2616197.html

【编辑推荐】

 

责任编辑:彭凡 来源: 博客园
相关推荐

2016-09-23 16:36:25

LinuxPCPhoronix

2023-07-31 09:13:13

ValidatorGolang

2021-07-17 15:25:05

PHP 8.1基准测试开发

2024-03-06 18:09:06

Linux性能工具

2009-10-10 11:11:40

服务器测试

2021-07-08 14:59:05

JMHMongodb数据

2022-03-29 11:48:40

Go泛型测试

2013-05-07 09:47:30

测试MySQLMySQL测试

2019-06-19 10:00:45

vue.jsimbajavascript

2023-08-30 13:22:00

测试框架工具

2023-12-21 14:02:35

机器学习深度学习

2011-07-19 09:35:46

.Net

2012-12-18 13:32:45

IBMdW

2015-06-17 13:58:22

Java序列化库

2020-10-23 14:05:39

AI 服务器测试

2023-05-12 07:38:46

Python基准测试性能分析

2015-07-21 09:18:48

Windows 10Windows 8性能

2011-09-22 13:49:44

XML基准测试

2009-02-09 11:06:19

WindowsUbuntuWindows7

2023-01-06 08:31:53

数据库基准测试
点赞
收藏

51CTO技术栈公众号