如何准确测试c#程序的性能?

导语:

在我们设计、开发新的软件时,基础的程序员看中完成需求而不注重性能;熟练的程序员在完成需求的同时也会对性能斤斤计较。但是在我们设计时,性能的比较往往只存在于理论中,而不是实践结果。本文将介绍利用BenckMarkDotNet包快速写出适用于c#程序的benchmark的方法。
举例:LimFx的benchmark结果


什么是benchmark

如果你是一名性能发烧友,那么你应当已经熟悉benchmark的意义。如果你没听说过,也没有关系。它的意思即为对照测试。可以理解为一种评价程序性能的科学思路。直观表现就是运行程序,测量其执行时间。

使用nuget包的原因

看到这里,你可能会想,为何我们不自己用c#类中内置的stopWatch来测量时间,自己写benchmark? 这是一个很好的想法,笔者以前也确实这么做过。但是这样做有一些问题:

  1. c#代码在执行过程中会被jit引擎不断优化

    也就是说c#代码在多次执行时会运行的越来越快,所以直接测试第一次运行的结果意义不大。应当多次执行被测试代码,待其完全优化后再多次测量执行时间,取平均值。

  2. 单次执行结果不一定有代表性 ,应当多次执行取均值。

  3. 应该同时计算方差等统计数据以证明结果可靠性。

这些功能如果都自己完成的话,会比较麻烦。所以本文会使用BenckMarkDotNet包,这个包封装了我们需要的几乎所有功能。


如何使用BenckMarkDotNet?

首先,我们创建一个.Net Core的控制台程序
然后,我们安装nuget包:

PM>Install-Package BenchmarkDotNet -Version 0.12.0

或者直接可视化界面安装


安装完成后,我们就可以写一段需要测试的代码,例如:

public class MyBenchMark
{
   [Benchmark]
   public void TestMethod()
   {
      for (int i = 0; i < 10000; i++)
      {
            File.ReadAllBytes("article.txt");
      }
   }
}

这里我们测试10000次文件读入操作,读入的文章是我预先准备好的1000行txt。
注意到[Benchmark]了吗?没错,这个attribute就是使用benchmark包的关键。
然后,我们再Main函数中:

static void Main(string[] args)
{
   BenchmarkRunner.Run<MyBenchMark>();
}

完成!

是不是非常简单? 接下来让我们来运行一下试试?
注意,所有的benchmark应当在release环境下完成,否则默认情况下benchmark包会报错
这张图中的表记载了运行时间的分布区间
这张表记录了各种统计数据结果,可以看到10000次读取长文件操作平均耗时2.074s

另外还有4个比较重要的attribute[GlobalSetup] [GlobalCleanup] [IterationSetup] [IterationCleanup],他们的作用分别是全局初始化(每个benchmark开始前调用)、全局清理(每个benchmark结束后调用)、循环初始化(每个计时循环前调用)、循环清理(每个计时循环后调用)。

结语

以上就是对c#程序进行benchmark的基础内容,希望能给大家带来一些参考,帮助大家更轻松的对自己的项目进行性能测试。

参考文献:



本文章使用limfx的vsocde插件快速发布