BenchmarkDotNet可帮助您将方法转换为基准,跟踪其性能并共享可再现的测量实验。这比编写单元测试更难!在引擎盖下,它发挥了许多魔力,可确保借助Perfolizer统计引擎,可确保可靠和精确的结果。 BenchmarkDotNet可以保护您免受流行的基准测试错误,并警告您,如果您的基准设计出了问题或获得的测量结果。结果以用户友好的形式呈现,该形式突出了有关您的实验的所有重要事实。 BenchmarkDotNet已被26400+ GITHUB项目采用,包括.NET运行时,.NET编译器,.NET Performance等。
很容易开始编写基准测试,请查看以下示例(在此处复制版本):
[ SimpleJob ( RuntimeMoniker . Net472 , baseline : true ) ] [ SimpleJob ( RuntimeMoniker . NetCoreApp30 ) ] [ SimpleJob ( RuntimeMoniker . NativeAot70 ) ] [ SimpleJob ( RuntimeMoniker . Mono ) ] [ RPlotExporter ] public class Md5VsSha256 { private SHA256 sha256 = SHA256 . Create ( ) ; private MD5 md5 = MD5 . Create ( ) ; private byte [ ] data ; [ Params ( 1000 , 10000 ) ] public int N ; [ GlobalSetup ] public void Setup ( ) { data = new byte [ N ] ; new Random ( 42 ) . NextBytes ( data ) ; } [ Benchmark ] public byte [ ] Sha256 ( ) => sha256 . ComputeHash ( data ) ; [ Benchmark ] public byte [ ] 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 |
\”>
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框架4.6.1+,.net Core 2.0+,Mono,Nativeaot
支持的语言: C#,F#,Visual Basic
支持的操作系统: Windows,Linux,MacOS
支持的架构: X86,X64,ARM,ARM64,WASM和LOONGARCH64
特征
BenchmarkDotNet具有许多在全面绩效调查中必不可少的功能。四个方面定义了这些功能的设计:简单,自动化,可靠性和友好性。
简单
如果您想编写基准测试,则不必成为经验丰富的性能工程师。您可以使用简单的API以声明性的样式设计非常复杂的性能实验。
例如,如果要参数化基准标准,请标记一个字段或具有[Params(1, 2, 3)]的属性: BenchmarkDotNet将枚举所有指定值并为每种情况下运行基准测试。如果您想彼此比较基准,请通过[Benchmark(Baseline = true)]标记一个基准标准为基线]: BenchmarkDotNet将其与所有其他基准测试标准进行比较。如果要比较不同环境中的性能,请使用作业。例如,您可以通过[SimpleJob(RuntimeMoniker.Net80)]和[SimpleJob(RuntimeMoniker.Mono)]在.NET 8.0上运行所有基准和单声道]。
如果您不喜欢属性,则可以通过流利的样式调用大多数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还防止了使用调试模式构建的非优化组件的基准测试,因为相应的结果将不可靠。如果您使用连接的调试器,如果您使用管理程序(HyperV,VMware,VirtualBox),或者如果当前环境有其他问题,则该库将打印警告。
在开发的6年以上,我们面临着许多不同的问题,可能会破坏您的测量。在BenchmarkDotNet中,有很多启发式方法,检查,黑客和技巧可帮助您提高结果的可靠性。
友善
绩效数据的分析是一项耗时的活动,需要注意力,知识和经验。 BenchmarkDotNet为您执行此分析的主要部分,并以用户友好的形式提供了结果。
实验后,您将获得一个摘要表,其中包含有关执行基准测试的许多有用数据。默认情况下,它仅包含最重要的列,但可以轻松自定义。列集是自适应的,取决于基准定义和测量值。例如,如果将基准之一标记为基准,则将获得其他列,以帮助您将所有基准测试与基线进行比较。默认情况下,它始终显示均值列,但是如果我们检测到均值和中间值之间的巨大差异,则将显示两个列。
BenchmarkDotNet试图找到您的性能分布的一些异常属性,并打印出关于它的好消息。例如,它会警告您在多模式分布或较高的异常值的情况下。在这种情况下,您可以向上滚动结果并查看每个分布的ASCII风格直方图,或使用[RPlotExporter]生成漂亮的PNG图。
BenchmarkDotNet不会超载您的数据;它仅根据您的结果显示基本信息:它使您可以将摘要较小用于原始情况,并仅针对复杂的情况扩展。当然,您可以手动请求任何其他统计信息和可视化。如果您不自定义摘要视图,则默认演示文稿将尽可能多地对用户友好。 🙂
了解有关基准测试的更多信息
BenchmarkDotNet并不是一个银色的子弹,它神奇地使您的所有基准都正确,并为您分析测量值。即使您使用此库,您仍然应该知道如何设计基准实验以及如何根据原始数据得出正确的结论。如果您想进一步了解基准测试方法和良好实践,建议阅读Andrey Akinshin( BenchmarkDotNet Project Lead)的一本书:“ Pro .Net Bench Markeding”。使用此深入指南正确设计基准测试,测量.NET应用程序的关键性能指标并分析结果。本书介绍了数十个案例研究,以帮助您了解复杂的基准主题。您将避免常见的陷阱,控制测量的准确性并提高软件的性能。
欢迎捐款!
BenchmarkDotNet已经是一个稳定的全功能图书馆,可以在专业水平上进行性能调查。它继续发展!我们一直在添加新功能,但是我们有太多新的很酷的想法。任何帮助将不胜感激。您可以开发新功能,修复错误,改进文档或做其他一些很酷的事情。
如果您想做出贡献,请查看贡献指南和提出的问题。如果您有新的想法或想抱怨错误,请随时创建一个新问题。让我们构建最佳的工具,以共同进行基准测试!
行为守则
该项目采用了贡献者盟约定义的行为准则,以阐明我们社区的预期行为。有关更多信息,请参见.NET基金会行为准则。
