.NET Core中的性能测试工具BenchmarkDotnet

发表于:2021-2-01 09:55

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:Lamond Lu    来源:博客园

  为什么需要性能基准测试?
  性能基准测试可以帮助程序员对比2个代码段或者方法的性能,这对于代码重写或者重构来说,可以提供一种很好的量化标准。如果没有性能基准测试,很难想象将方法A改为B方法时候,仅凭肉眼如何区分性能的变化。
  BenchmarkDotNet
  BenchmarkDotNet是一款强力的.NET性能基准测试库, 官网https://benchmarkdotnet.org/。
  运行时支持
  NET Framework (4.6+),
  .NET Core (2.0+)
  Mono
  CoreRT。
  BenchmarkDotnet为每个被测试的方法提供了孤立的环境, 使用BenchmarkDotnet, 程序员可以很容易的编写各种性能测试方法,并可以避免许多常见的坑。
  代码基准测试(Code Benchmarking)
  现在我们希望来对比一下Linq to object中First和Single方法的性能
  虽然我们知道First的性能肯定比Single高, First方法会在查询到第一个满足条件的对象之后就停止集合遍历,而Single找到第一个满足条件的对象之后,不会停止查找,它会去继续查找集合中的剩余对象,直到遍历整个集合或者在集合中找到第二个匹配条件的对象。 这里我们只是为了演示一下如何进行代码基准测试。
  为了使用BenchmarkDotNet来进行代码基准测试,我们首先创建一个空的.Net Core控制台程序。
  然后我们使用Package Manage Console添加BenchmarkDotNet库
  PM> Install-Package BenchmarkDotNet
  然后我们修改Program.cs文件, 代码如下
  代码解释说明
  以上代码中SingleVsFirst类是一个测试类。
  测试类中我们生成了一个拥有100万对象的字符串集合。
  我们在集合的中间位置插入了一个测试字符串,字符串的内容是"needle"。
  代码中的Single和First方法,分别调用了Linq to object的SingleOrDefault和FirstOrDefault方法来查询字符串集合中的"needle"字符串。
  在Single和First方法上,我们加入[Benchmark]特性, 拥有该特性的方法会出现在最后的基准检测报告中。
  注意:
  测试的方法必须是公开的(public), 如果把public去掉,程序不会产生任何结果
  在运行程序之前,还有一步关键的操作,测试的程序需要使用Release模式编译,并且不能附加任何调试器(Debugger)
  最终结果
  现在我们运行程序,程序产生的最终报告如下:
  结果中的第一列Mean表明了2个方法处理的平均响应时间,First比Single快了一倍(这和我们测试字符串放置的位置有关系)。
  带测试参数的基准测试(Input Benchmarking)
  BenchmarkDotNet中我们还可以使用[ParamsSource]参数来指定测试的用例范围。
  在上面的代码中,我们测试了匹配字符串在集合中间位置时,First和Single的效率对比,下面我们修改上面的代码,我们希望分别测试匹配字符串在集合头部,尾部以及中间位置时First和Single的效率对比。
  using System;
  using System.Collections.Generic;
  using System.Linq;
   
  using BenchmarkDotNet.Attributes;
  using BenchmarkDotNet.Running;
   
  namespace BenchmarkExample
  {
      public class SingleVsFirst
      {
          private readonly List<string> _haystack = new List<string>();
          private readonly int _haystackSize = 1000000;
   
          public List<string> _needles => new List<string> { "StartNeedle", "MiddleNeedle", "EndNeedle" };
   
          public SingleVsFirst()
          {
              //Add a large amount of items to our list. 
              Enumerable.Range(1, _haystackSize).ToList().ForEach(x => _haystack.Add(x.ToString()));
   
              //One at the start. 
              _haystack.Insert(0, _needles[0]);
              //One right in the middle. 
              _haystack.Insert(_haystackSize / 2, _needles[1]);
              //One at the end. 
              _haystack.Insert(_haystack.Count - 1, _needles[2]);
          }
   
          [ParamsSource(nameof(_needles))]
          public string Needle { get; set; }
   
          [Benchmark]
          public string Single() => _haystack.SingleOrDefault(x => x == Needle);
   
          [Benchmark]
          public string First() => _haystack.FirstOrDefault(x => x == Needle);
   
      }
   
      class Program
      {
          static void Main(string[] args)
          {
              var summary = BenchmarkRunner.Run<SingleVsFirst>();
              Console.ReadLine();
          }
      }
  }
  代码解释说明
  我们创建了测试的用例字符串集合_needles
  在构造函数中,我们在字符串集合的头部,中部,尾部分别插入了3个字符串
  我们添加了一个属性Needle, 表示当前测试的用例,在被测试Single和First方法中,我们使用属性Needle来匹配
  在属性Needle上我们加上了参数来源特性[ParamsSource], 并设置参数来源是_needles
  最终效果
  现在我们运行程序,程序产生的最终报告如下:
  从结果上看
  当匹配字符串在集合头部的时候,First性能比Single高的多
  当匹配字符串在集合中部的时候,First性能是比Single的一倍
  当匹配字符串在集合尾部的时候,First和比Single的性能差不多
  加入内存测试
  在.NET Core中的CSV解析库中,我们使用了以下代码
  其中除了[Benchmark]特性,我们还在测试类CsvBenchmarking上添加了[MemoryDiagnoser]特性,该特性会在测试报告中追加,2个方法执行时的内存使用情况。
  其中Allocated表明了内存占用情况。
  总结
  BenchmarkDotNet绝对是.NET开发人员了解代码性能,以及对比代码性能的必备神器。你的项目里用了BenchmarkDotnet了么?

      本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号