[工具推荐].Net测试神器BenchmarkDotNet提升.NET应用性能
在.NET开发中,性能优化是至关重要的环节。BenchmarkDotNet作为一款强大的开源基准测试库,能够帮助开发者准确测量代码的性能表现,从而有针对性地进行优化。本文将详细介绍BenchmarkDotNet的使用方法,助力.Net开发者提升应用性能。
BenchmarkDotNet简介
BenchmarkDotNet是专为.Net应用程序设计的开源基准测试库。它通过多次运行被测试的方法,收集统计数据,生成详细的报告,涵盖执行时间、内存使用等多方面信息。该库简单易用,可轻松集成到现有项目中,为开发者提供了全面的性能评估工具。
开始写基准测试很容易,看看下面的示例
[SimpleJob(RuntimeMoniker.Net472, baseline: true)][SimpleJob(RuntimeMoniker.NetCoreApp30)][SimpleJob(RuntimeMoniker.NativeAot70)][SimpleJob(RuntimeMoniker.Mono)][RPlotExporter]publicclassMd5VsSha256{private SHA256 sha256 = SHA256.Create();private MD5 md5 = MD5.Create();privatebyte[] data; [Params(1000, 10000)]publicint N; [GlobalSetup]publicvoidSetup() { data = newbyte[N];new Random(42).NextBytes(data); } [Benchmark]publicbyte[] Sha256() => sha256.ComputeHash(data); [Benchmark]publicbyte[] Md5() => md5.ComputeHash(data);}
BenchmarkDotNet 自动运行 运行所有运行时的基准测试, 汇总测量数据, 并打印一份包含最重要信息的汇总表:
BenchmarkDotNet=v0.12.0, OS=Windows 10.0.17763.805 (1809/October2018Update/Redstone5)Intel Core i7-7700K CPU 4.20GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores [Host] : .NET Framework 4.7.2 (4.7.3468.0), X64 RyuJIT Net472 : .NET Framework 4.7.2 (4.7.3468.0), X64 RyuJIT NetCoreApp30 : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), X64 RyuJIT NativeAot70 : .NET 7.0.0-preview.4.22172.7, X64 NativeAOT Mono : Mono 6.4.0 (Visual Studio), X64| Method | Runtime | N | Mean | Error | StdDev | Ratio ||------- |-------------- |------ |-----------:|----------:|----------:|------:|| Sha256 | .NET 4.7.2 | 1000 | 7.735 us | 0.1913 us | 0.4034 us | 1.00 || Sha256 | .NET Core 3.0 | 1000 | 3.989 us | 0.0796 us | 0.0745 us | 0.50 || Sha256 | NativeAOT 7.0 | 1000 | 4.091 us | 0.0811 us | 0.1562 us | 0.53 || Sha256 | Mono | 1000 | 13.117 us | 0.2485 us | 0.5019 us | 1.70 || | | | | | | || Md5 | .NET 4.7.2 | 1000 | 2.872 us | 0.0552 us | 0.0737 us | 1.00 || Md5 | .NET Core 3.0 | 1000 | 1.848 us | 0.0348 us | 0.0326 us | 0.64 || Md5 | NativeAOT 7.0 | 1000 | 1.817 us | 0.0359 us | 0.0427 us | 0.63 || Md5 | Mono | 1000 | 3.574 us | 0.0678 us | 0.0753 us | 1.24 || | | | | | | || Sha256 | .NET 4.7.2 | 10000 | 74.509 us | 1.5787 us | 4.6052 us | 1.00 || Sha256 | .NET Core 3.0 | 10000 | 36.049 us | 0.7151 us | 1.0025 us | 0.49 || Sha256 | NativeAOT 7.0 | 10000 | 36.253 us | 0.7076 us | 0.7571 us | 0.49 || Sha256 | Mono | 10000 | 116.350 us | 2.2555 us | 3.0110 us | 1.58 || | | | | | | || Md5 | .NET 4.7.2 | 10000 | 17.308 us | 0.3361 us | 0.4250 us | 1.00 || Md5 | .NET Core 3.0 | 10000 | 15.726 us | 0.2064 us | 0.1930 us | 0.90 || Md5 | NativeAOT 7.0 | 10000 | 15.627 us | 0.2631 us | 0.2461 us | 0.89 || Md5 | Mono | 10000 | 30.205 us | 0.5868 us | 0.6522 us | 1.74 |
测量数据可以导出为多种格式(md、html、csv、xml、json 等),包括图表:

支持运行环境:.NET 5+、.NET Framework 4.6.1+、.NET Core 2.0+、Mono、NativeAOT支持语言:C#、F#、Visual Basic支持操作系统:Windows、Linux、macOS支持的架构:x86、x64、ARM、ARM64、Wasm 和 LoongArch64
特色
BenchmarkDotNet 拥有大量对全面性能调查至关重要的功能。 这些特征的设计由四个方面体现:简洁、自动化、可靠性和友好性。
简洁性
如果你想写基准测试,不应该非得是有经验的性能工程师。 你可以用声明式风格设计非常复杂的性能实验,使用简单的 API。
例如,如果你想参数化你的基准测试, 用 标记字段或属性 :BenchmarkDotNet 将枚举所有指定值 并为每个案例运行基准测试。 如果你想比较基准测试, 通过以下方式标记其中一个基准测试,BenchmarkDotNet将将其与其他所有基准进行比较。 如果你想比较不同环境的性能,可以用作业。 例如,你可以通过和 在 .NET 8.0 和 Mono 上运行所有基准测试。[Params(1, 2, 3)][Benchmark(Baseline = true)][SimpleJob(RuntimeMoniker.Net80)][SimpleJob(RuntimeMoniker.Mono)]
如果你不喜欢属性,可以通过流流式调用大多数API,写出这样的代码:
ManualConfig.CreateEmpty() // A configuration for our benchmarks .AddJob(Job.Default // Adding first job .WithRuntime(ClrRuntime.Net472) // .NET Framework 4.7.2 .WithPlatform(Platform.X64) // Run as x64 application .WithJit(Jit.LegacyJit) // Use LegacyJIT instead of the default RyuJIT .WithGcServer(true) // Use Server GC ).AddJob(Job.Default // Adding second job .AsBaseline() // It will be marked as baseline .WithEnvironmentVariable("Key", "Value") // Setting an environment variable .WithWarmupCount(0) // Disable warm-up stage );
如果你更喜欢命令行体验,可以通过以下方式配置基准测试 任何控制台应用程序中的控制台参数(其他类型的应用程序不支持)。
自动化
可靠的基准测试总是包含大量模板代码。
让我们来想想在典型情况下你应该怎么做。 首先,你应该做一个试点实验,确定方法调用次数的最佳。 接下来,你应进行多次热身迭代,确保基准达到稳态。 之后,你应该执行主要迭代并计算一些基本统计数据。 如果你在基准测试中计算了一些数值,应该用它来防止死代码消除。 如果你用循环,你应该关注循环展开对结果的影响 (这可能取决于处理器架构)。 一旦你得到结果,你应该检查获得的性能分布是否有一些特殊属性 比如多模态或极高的离群值。 你还应评估基础设施的开销,并将其从结果中扣除。 如果你想测试多个环境,应该在每个环境中进行测量并手动汇总结果。
如果你是从零开始写代码,很容易出错,弄坏你的测量结果。 请注意,这是你在基准测试时应遵循的完整清单的简化版: 还有许多隐藏的陷阱需要妥善处理。 幸运的是,你不必担心,因为 BenchmarkDotNet 会帮你完成这些枯燥且耗时的工作。
此外,图书馆还能帮助你完成一些调查过程中可能需要完成的高级任务。 例如, BenchmarkDotNet 可以测量管理和原生内存流量 并打印拆解列表以供基准测试。
可靠性
许多手写基准结果往往会给出错误的数据,导致错误的商业决策。 BenchmarkDotNet 保护您免受大多数基准测试陷阱,并实现高精度测量。
你不必担心方法调用次数、热身次数和实际迭代次数: BenchmarkDotNet 尝试选择最佳的基准参数, 在测量预估与所有基准测试总时长之间取得良好权衡。 所以,你不应该使用任何神奇的数字(比如“我们这里应该做100次迭代”), 图书馆会根据统计指标的数值帮你做决定。
BenchmarkDotNet 还阻止了使用 DEBUG 模式构建的未优化装配的基准测试,因为 相应的结果将不可靠。 如果你已经连接了调试器,库会打印警告, 如果你使用虚拟机监控程序(HyperV、VMware、VirtualBox), 或者你对当前环境有其他问题。
在6+年的开发过程中,我们遇到了数十个不同的问题,可能会影响你的测量结果。 在BenchmarkDotNet内部,有许多启发式方法、检查、技巧和技巧可以帮助你 提高结果的可靠性。
友好
绩效数据分析是一项耗时的活动,需要专注、知识和经验。 BenchmarkDotNet 为你完成了主要的分析,并以用户友好的形式呈现结果。
实验结束后,你会得到一个汇总表,里面包含了大量关于已执行基准测试的有用数据。 默认情况下,它只包含最重要的列, 但它们可以很容易定制。 列集是自适应的,依赖于基准定义和测量值。 例如,如果你将其中一个基准标记为基线, 你还会获得额外的列,帮助你将所有基准与基线进行比较。 默认情况下,它总是显示平均值列, 但如果我们检测到平均值和中位数值之间存在巨大差异, 两栏都会被介绍。
BenchmarkDotNet 会尝试找出你性能分布中一些异常属性,并发布一些关于它的美好信息。 例如,它会在多模态分布或高异常值时发出警告。 在这种情况下,你可以向上滚动结果,查看每个分布的ASCII风格直方图 或者用 生成漂亮的 PNG 图。[RPlotExporter]
BenchmarkDotNet不会让你数据过载;它只显示根据你的结果所必需的关键信息: 它允许你在原始案件中保持摘要简短,仅在复杂案件时延长摘要。 当然,你可以手动请求额外的统计数据和可视化。 如果你不自定义摘要视图, 默认的展示方式会尽可能用户友好。:)
夜雨聆风