.NET性能测试工具BenchmarkDotNet

简述:BenchmarkDotNet是一个用于进行性能基准++测试++的开源库,可以帮助开发者在.NET 应用程序中测试代码性能。

一、常用参数(特性)详解:

1. 基准方法标记
  • [Benchmark]

    • 作用:最重要的特性,标记一个方法为基准测试方法。只有被此特性标记的方法才会被测量。

    • 常用参数

      • Description: 为方法提供自定义描述,会显示在结果表中。

      • Baseline: 设置为 true 将该方法设为基线。其他方法的 Ratio 列将会与这个基线方法进行比较。

cs 复制代码
[Benchmark(Description = "My Awesome Sort", Baseline = true)]
public void MyAlgorithm() { ... }
2. 参数化基准测试
  • [Params]

    • 作用 :用于参数化测试。你可以定义一个属性,并用 [Params] 指定一组值。BenchmarkDotNet 会为每个参数值组合生成一个单独的测试用例。这是最强大的功能之一,用于回答"我的算法在不同数据量下的表现如何?"这类问题。

    • 示例

cs 复制代码
public class MyBenchmarks
{
    [Params(100, 1000, 10000)] // 测试3种不同规模的数据
    public int DataSize { get; set; }

    private int[] _data;

    [GlobalSetup]
    public void Setup()
    {
        // 根据 DataSize 生成测试数据
        _data = new int[DataSize];
        // ... 初始化数据
    }

    [Benchmark]
    public void TestMethod()
    {
        // 使用 _data 进行测试
    }
}
3.设置与清理方法
  • [GlobalSetup]

    • 作用 :标记一个方法,该方法会在整个测试类中所有基准方法运行前仅执行一次。常用于初始化昂贵的资源(如创建大型数组、建立数据库连接)。
  • [GlobalCleanup]

    • 作用 :与 GlobalSetup 对应,在所有测试完成后执行一次,用于清理资源。
  • [IterationSetup]

    • 作用 :标记一个方法,该方法会在每一次基准迭代(Iteration)前执行。常用于重置状态,例如将测试数组恢复到未排序的状态。
  • [IterationCleanup]

    • 作用:在每一次基准迭代后执行。

    最佳实践 :在排序测试中,我们在 [GlobalSetup] 中生成原始数据,在 [IterationSetup] 中拷贝原始数据到工作数组,以确保每次迭代的初始条件完全一致。

cs 复制代码
private int[] _originalData;
private int[] _workData;

[GlobalSetup]
public void GlobalSetup()
{
    // 生成一次原始数据
    _originalData = GenerateTestData(1000);
}

[IterationSetup]
public void IterationSetup()
{
    // 每次迭代前都拷贝一份原始数据,保证起点相同
    _workData = (int[])_originalData.Clone();
}

[Benchmark]
public void Sort() => Array.Sort(_workData);
4. 诊断器 (Diagnosers)
  • 作用:用于收集基准测试之外的额外诊断信息。

  • [MemoryDiagnoser]

    • 最重要的诊断器之一 。为结果表添加两列:Gen 0Allocated

    • Allocated: 该方法一次执行所分配的内存总量(字节/B, 千字节/KB)。

    • Gen 0: 执行该方法导致的 Generation 0 垃圾回收次数。

    • 示例Allocated: 1.02 KBGen 0: 0.4565 表明该方法每次调用分配了约 1KB 内存,平均每执行两次多会触发一次 Gen 0 GC。

  • 其他诊断器 :还可以通过 NuGet 包添加 DisassemblyDiagnoser(查看生成的汇编代码)、EventPipeProfiler(生成 CPU 火焰图)等高级诊断工具。

5. 任务配置 (Jobs)
  • 作用:控制基准测试的运行环境,例如 .NET 运行时版本、JIT 编译器模式、平台等。

  • 常用配置

    • [SimpleJob] / [ShortRunJob]

      • [SimpleJob(runtimeMoniker: RuntimeMoniker.Net60)]: 指定在 .NET 6 环境下运行。

      • [ShortRunJob]: 非常常用。它配置了一个预设的"快速运行"任务,迭代次数较少,适用于快速验证和开发阶段的调试。正式测量性能时应移除它以获得更准确的结果。

cs 复制代码
// 同时测试 .NET 6 和 .NET 8 下的性能差异
[SimpleJob(RuntimeMoniker.Net60)]
[SimpleJob(RuntimeMoniker.Net80)]
[MemoryDiagnoser]
public class MyBenchmarks { ... }

二、输出结果:

运行基准测试后,你会得到一个详细的表格,包含以下关键列:

我们可以用该工具测试下冒泡排序和快速排序的算法性能比较,Console窗口结果输出如下:

,我们也可以使用**[HtmlExporter]**等特性设置结果输出格式,目前支持的Html,csv,markdown,json,xml等。

测试样例代码:

cs 复制代码
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Environments;
using BenchmarkDotNet.Jobs;
using System;
using System.Linq;

namespace SortingBenchmarks
{
    [ShortRunJob]
    //[HtmlExporter]
    [MemoryDiagnoser] // 内存诊断器,分析内存分配
    public class SortingBenchmark
    {
        // 使用 Params 特性来测试不同规模的数据
        [Params(100, 1000, 5000)]
        public int ArraySize { get; set; }

        private int[] _originalArray;
        private int[] _testArray;
        private Random _random;

        // 全局初始化,每个参数组合执行一次
        [GlobalSetup]
        public void Setup()
        {
            _random = new Random(42); // 固定种子确保每次生成的数据一致
            _originalArray = new int[ArraySize];
            for (int i = 0; i < ArraySize; i++)
            {
                _originalArray[i] = _random.Next();
            }
        }

        // 每次迭代前拷贝数据,确保每次排序的初始数据相同
        [IterationSetup]
        public void IterationSetup()
        {
            _testArray = (int[])_originalArray.Clone();
        }

        [Benchmark(Baseline = true, Description = "QuickSort")]
        public void QuickSort()
        {

            void Sort(int left, int right)
            {
                if (left < right)
                {
                    int pivot = _testArray[right];
                    int i = left - 1;

                    for (int j = left; j < right; j++)
                    {
                        if (_testArray[j] <= pivot)
                        {
                            i++;
                            int temp1 = _testArray[i];
                            _testArray[i]= _testArray[j];
                            _testArray[j]= temp1;
                        }
                    }
                    int temp2 = _testArray[i+1];
                    _testArray[i + 1] = _testArray[right];
                    _testArray[right]=temp2;
                    int pivotIndex = i + 1;
                    Sort(left, pivotIndex - 1);
                    Sort(pivotIndex + 1, right);
                }
            }

            Sort(0, _testArray.Length - 1);
        }

        [Benchmark(Description = "BubbleSort")]
        public void BubbleSort()
        {
            int n = _testArray.Length;
            for (int i = 0; i < n - 1; i++)
            {
                for (int j = 0; j < n - i - 1; j++)
                {
                    if (_testArray[j] > _testArray[j + 1])
                    {
                        // 交换元素
                        int temp = _testArray[j];
                        _testArray[j] = _testArray[j + 1];
                        _testArray[j + 1] = temp;
                    }
                }
            }
        }

    }

    class Program
    {
        static void Main(string[] args)
        {
            var summary = BenchmarkRunner.Run<SortingBenchmark>();
        }
    }
}
相关推荐
工程师0071 天前
C#中的CIL(公共中间语言)
开发语言·c#·中间语言cil
mudtools1 天前
基于.NET操作Excel COM组件生成数据透视报表
c#·.net·excel
天才测试猿1 天前
自动化测试基础知识总结
自动化测试·软件测试·python·测试工具·程序人生·职场和发展·测试用例
卓码软件测评1 天前
CMA/CNAS双资质软件测评机构【Apifox高效编写自动化测试用例的技巧和规范】
测试工具·ci/cd·性能优化·单元测试·测试用例
测试秃头怪1 天前
支付宝性能测试案例分析详解
自动化测试·软件测试·python·测试工具·职场和发展·测试用例·性能测试
kylezhao20191 天前
C# 写一个Http 服务器和客户端
服务器·http·c#
冰茶_1 天前
WPF路由事件:隧道与冒泡机制解析
学习·c#·.net·wpf·.netcore·mvvm
我是唐青枫1 天前
深入理解 Volatile:C#.NET 内存可见性与有序性
c#·.net
少云清1 天前
【接口测试】1_Dubbo接口 _xx健康项目
测试工具·接口测试