单体测试的设计
由于Visual Benchmark是基于Visual Studio Unit Test Framework的单体测试用例的,因此,我们只需要在Visual Studio中开发有待测试的单体测试程序即可。在开发单体测试程序之前,首先让我们了解一下Visual Benchmark所支持的“迭代基准测试”模式。所谓“迭代基准测试”,意思就是Visual Benchmark会循环地调用单体测试方法并在每次调用结束时,统计当前“代”中所消耗的CPU周期或者时间;当Visual Benchmark完成整个测试之后,会将各代的统计结果组织并显示出来。在这个过程中,单体测试方法可以通过Visual Benchmark所提供的基准测试参数来获得当前所处的“代”数(也就是循环因子的值),然后就可以基于这个“代”数对测试数据进行模拟,以反映出随着测试数据的增长,单体测试方法的执行效率。
举例来说,在测试三种不同的仓储对于聚合的保存执行效率时,我首先在Visual Studio中新建了一个Unit Test类,并在类中定义了两个成员变量:
- [TestClass]
- public class InsertAggregateRootsTest
- {
- private int thisIteration;
- private IEnumerable
mockSalesOrders; - // 其它部分暂时省略
- }
第一个成员变量thisIteration用来保存从Visual Benchmark传入的“代”数;而第二个成员变量mockSalesOrders则是保存了一组即将通过仓储插入的聚合模拟数据。
接下来,我在这个测试类中加入了Test Initialize的方法,以便在每次测试方法被调用前,执行一些数据初始化的操作。在这个方法中,会对以上两个成员变量初始化,同时清空后台数据库,为执行测试做准备。
- [TestInitialize()]
- public void MyTestInitialize()
- {
- thisIteration = (int)BenchmarkRuntimeArgs.Instance.ThisIteration;
- mockSalesOrders = Helper.MockSalesOrders(thisIteration);
- Helper.ClearSQLServerTables();
- Helper.ClearMongoDB();
- }
在上面的方法中,首先使用BenchmarkRuntimeArgs.Instance.ThisIteration对thisIteration进行初始化,以便获得当前测试的“代”。BenchmarkRuntimeArgs是一个跨应用程序域的单件(Singleton),在每次执行Benchmark之前都会被初始化。然后根据获得的“代”数,创建聚合模拟数据。此处mockSalesOrders中所包含的数据量会随着“代”数的增长而增加,以反映随着数据量的增长,被测函数的性能趋势。最后,使用Helper类清空后台数据库。
由于仓储的实现是基于不同的应用框架,因此,在所有测试启动前,需要对这些框架进行初始化。有写过单体测试的朋友都知道,这部分逻辑应该写在Class Initialize的方法中:
- [ClassInitialize()]
- public static void MyClassInitialize(TestContext testContext)
- {
- Database.SetInitializer
(new - DropCreateDatabaseIfModelChanges
()); - MongoDBRepositoryContext.RegisterConventions();
- if (!BsonClassMap.IsClassMapRegistered(typeof(SalesLine)))
- {
- BsonClassMap.RegisterClassMap
(p => - {
- p.AutoMap();
- p.UnmapProperty
(q => q.SalesOrder); - });
- }
- }
在这里并没有使用代码的方式对NHibernate框架进行初始化,因为NHibernate的初始化过程是由Apworks中的NHibernateApplicationConfiguration类型完成的,这个类型依赖应用程序的app/web.config文件。所以我们需要在单体测试项目中添加app.config以及相关的配置节点。篇幅原因,这里就不贴app.config的代码了,文章末尾我会给出源代码。Visual Benchmark支持在“客户应用程序域”(Client AppDomain)中装载app/web.config文件。
现在,可以开始写测试方法了,以下是基于三种不同仓储实现的测试方法。从各方法中我们可以看到,除了所创建的IRepositoryContext、IRepository的具体实现不同之外,其它的操作逻辑完全相同:即通过仓储对聚合进行保存:
- [TestMethod]
- public void EntityFramework()
- {
- using (IRepositoryContext context = new EntityFrameworkRepositoryContext(new EntityFrameworkDbContext()))
- {
- IRepository
salesOrderRepository = new EntityFrameworkRepository (context); - foreach (var salesOrder in mockSalesOrders)
- salesOrderRepository.Add(salesOrder);
- context.Commit();
- }
- }
- [TestMethod]
- public void NHibernate()
- {
- using (IRepositoryContext context = new NHibernateContext(new NHibernateApplicationConfiguration()))
- {
- IRepository
salesOrderRepository = new NHibernateRepository (context); - foreach (var salesOrder in mockSalesOrders)
- salesOrderRepository.Add(salesOrder);
- context.Commit();
- }
- }
- [TestMethod]
- public void MongoDB()
- {
- using (IRepositoryContext context = new MongoDBRepositoryContext(new MongoDBRepositoryContextSettings()))
- {
- IRepository
salesOrderRepository = new MongoDBRepository (context); - foreach (var salesOrder in mockSalesOrders)
- salesOrderRepository.Add(salesOrder);
- context.Commit();
- }
- }
执行测试
首先,我们在Visual Studio中测试这三个方法,以确保每个方法都能够正确完成。在启动测试之前,先回到上面的MyTestInitialize方法,将thisIteration设置为一个固定的整数值,比如20,以便测试能够正常启动。在完成三个方法的测试之后,我们可以通过Test Results窗口看到测试结果。
打开Visual Benchmark,新建一个Session,在“打开”对话框中,选择已经编译好的DLL文件,此时Visual Benchmark会将其中包含的所有的测试类和测试方法加载到左边的树形结构中。在树形结构中,选中需要测试的方法,然后单击“开始”按钮,Visual Benchmark便会针对所选的测试方法进行基准测试。最后,会根据不同的测试引擎的设计,将结果显示出来。
测试结果
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
场景一:聚合保存
注:上图中X轴表示的是“代”数,亦即模拟的聚合数量;Y轴表示执行时间(毫秒数)。下同。
场景二:聚合查询
注:在此场景中,EntityFramework支线所表示的是使用Eager Loading将SalesOrder及其下所有Sales Lines实体读出所开销的时间;而EntityFramework_NoEagerLoad支线所表示的是仅读出SalesOrder(不包括其下所有Sales Lines)所开销的时间。
场景三:查询所有并删除
关于Visual Benchmark
Visual Benchmark是我在2010年开发的一款基于Visual Studio单体测试框架的性能基准测试程序,从整体上看,Visual Benchmark具有如下架构设计:
首先,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的执行
界面截图
基于两种不同引擎的执行结果显示
测试引擎的配置界面与文档界面
Session信息与客户应用程序域(Client AppDomain)信息
总结
本文对Apworks框架中所支持的三种仓储实现进行了性能上的基准测试,并得出了测试结果。在最开始的时候,我是打算结合Visual Studio的测试框架来完成这些工作的,但后来发现Visual Studio的测试框架所提供的功能并不能达到我的需求,之前也采用了Visual Studio的Load Test来做压力测试,但是效果并不算太理想。在下才疏学浅,并没有弄通Visual Studio提供的强大测试功能,所以也只能借用我之前写的Visual Benchmark程序了。如果有读者朋友知道如何在Visual Studio中完成类似的测试工作,还烦请告知在下,我会虚心向您学习。
下一步,我将对Apworks框架的线程安全性做一些评估,等到有了满意的结果,我也会将相关经验分享出来。
网站标题:Apworks框架各种仓储实现的性能基准测试
文章URL:http://www.csdahua.cn/qtweb/news4/325904.html
网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网