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类,并在类中定义了两个成员变量:

[TestClass]
public class InsertAggregateRootsTest
{
    private int thisIteration;
    private IEnumerable<SalesOrder> 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<EntityFrameworkDbContext>(new 
        DropCreateDatabaseIfModelChanges<EntityFrameworkDbContext>());
    MongoDBRepositoryContext.RegisterConventions();
    if (!BsonClassMap.IsClassMapRegistered(typeof(SalesLine)))
    {
        BsonClassMap.RegisterClassMap<SalesLine>(p =>
        {
            p.AutoMap();
            p.UnmapProperty<SalesOrder>(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<SalesOrder> salesOrderRepository = new EntityFrameworkRepository<SalesOrder>(context);
        foreach (var salesOrder in mockSalesOrders)
            salesOrderRepository.Add(salesOrder);
        context.Commit();
    }
}

[TestMethod]
public void NHibernate()
{
    using (IRepositoryContext context = new NHibernateContext(new NHibernateApplicationConfiguration()))
    {
        IRepository<SalesOrder> salesOrderRepository = new NHibernateRepository<SalesOrder>(context);
        foreach (var salesOrder in mockSalesOrders)
            salesOrderRepository.Add(salesOrder);
        context.Commit();
    }
}

[TestMethod]
public void MongoDB()
{
    using (IRepositoryContext context = new MongoDBRepositoryContext(new MongoDBRepositoryContextSettings()))
    {
        IRepository<SalesOrder> salesOrderRepository = new MongoDBRepository<SalesOrder>(context);
        foreach (var salesOrder in mockSalesOrders)
            salesOrderRepository.Add(salesOrder);
        context.Commit();
    }
}

 

执行测试

首先,我们在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具有如下功能技术特点:

  1. 可定制的基准测量标尺:开发人员可以自己开发基准测试的测量标尺。目前仅支持两种:StopwatchTickRuler和StopwatchMillisecondsRuler。上文的测试采用的是StopwatchMillisecondsRuler
  2. 可定制的测试引擎:开发人员可以根据需求定制开发测试引擎。框架提供了完整的引擎定制功能,这包括:引擎的元数据(例如名称、作者、描述等)、版本、配置界面、结果显示界面以及HTML文档。目前支持Iterated Throughput、Simple、Simple Iteration以及Throughput四种引擎。上文的测试采用了Simple Iteration引擎
  3. 减噪选项:使用减噪选项以获得更真实的测试数据。Visual Benchmark提供两个减噪选项:在每次执行测试之前强制垃圾回收、丢弃第一次的测试结果。测试引擎也会根据情况提供获取平均执行时间的选项
  4. 在客户应用程序域(Client AppDomain)中执行基准测试:能够对单体测试环境进行模拟,被测方法能够正常地访问配置文件和资源文件
  5. 跨AppDomain的单件(Singleton)实现:能够方便地在单体测试方法中读取Visual Benchmark的相关参数信息
  6. 多线程执行:用户可以随时停止Benchmark的执行

界面截图

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

image

 

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

image

 

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

image

 

总结

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

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

相关下载

【单击此处】下载本文的单体测试解决方案及源代码

【单击此处】下载Visual Benchmark的绿色免安装程序

【单击此处】下载Visual Benchmark的源程序代码(请使用Visual Studio 2010打开,没有使用任何第三方控件,所以无需安装其它组件)

posted @ 2012-07-31 08:22  dax.net  阅读(3965)  评论(11编辑  收藏  举报