如何为Benchmark的Foo1结果行添加内部函数Bar1的额外统计列?
在BenchmarkDotNet中同时展示主方法与内部调用方法的基准统计数据
要在Foo1的结果行中同时展示Bar1的均值、标准差等统计数据,有两种实用方案:
方案一:手动追踪内部方法耗时并输出自定义列
在Foo1中通过计时器记录Bar1的执行时间,在所有测试结束后手动计算统计值,再通过BenchmarkDotNet的自定义列功能输出到结果表格:
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.IO; using BenchmarkDotNet.Attributes; public class BenchMarkDemo { private readonly Stopwatch _stopwatch = new Stopwatch(); private List<double> _bar1ExecutionTimes = new List<double>(); public double Bar1Mean { get; private set; } public double Bar1StdDev { get; private set; } [Benchmark] public void Foo1() { // 模拟前置业务操作 var stream = dosomethingElse(); // 记录Bar1执行耗时 _stopwatch.Restart(); var output = Bar1(stream); _stopwatch.Stop(); _bar1ExecutionTimes.Add(_stopwatch.Elapsed.TotalMilliseconds); // 模拟后置业务操作 /* ... */ } public string Bar1(Stream stream) { return streamToString(stream); } // 所有测试完成后计算Bar1的统计值 [GlobalCleanup] public void CalculateBar1Statistics() { if (_bar1ExecutionTimes.Count == 0) return; Bar1Mean = _bar1ExecutionTimes.Average(); double mean = Bar1Mean; double sumOfSquares = _bar1ExecutionTimes.Sum(t => Math.Pow(t - mean, 2)); Bar1StdDev = Math.Sqrt(sumOfSquares / _bar1ExecutionTimes.Count); } // 将Bar1的均值作为自定义列输出 [BenchmarkColumn] public double Bar1MeanColumn => Bar1Mean; // 将Bar1的标准差作为自定义列输出 [BenchmarkColumn] public double Bar1StdDevColumn => Bar1StdDev; // 模拟业务方法 private Stream dosomethingElse() => new MemoryStream(Encoding.UTF8.GetBytes("sample benchmark data")); private string streamToString(Stream stream) { stream.Position = 0; using var reader = new StreamReader(stream); return reader.ReadToEnd(); } }
这种方式的优势是能直接在Foo1的结果行中展示Bar1的统计数据,缺点是需要手动实现统计逻辑,精度略低于BenchmarkDotNet原生统计。
方案二:将Bar1标记为独立基准方法
给Bar1添加[Benchmark]属性,让BenchmarkDotNet原生统计它的性能数据,还可以通过[Group]特性将两个方法归为一组,方便对比:
using System.IO; using System.Text; using BenchmarkDotNet.Attributes; public class BenchMarkDemo { [Benchmark, Group("FooBarGroup")] public void Foo1() { var stream = GetTestStream(); var output = Bar1(stream); /* ... */ } [Benchmark, Group("FooBarGroup")] public string Bar1Benchmark() { // 使用和Foo1中一致的测试输入,确保统计具备对比性 var stream = GetTestStream(); return streamToString(stream); } // 提取公共测试数据构造逻辑,避免输入不一致 private Stream GetTestStream() => new MemoryStream(Encoding.UTF8.GetBytes("sample benchmark data")); public string Bar1(Stream stream) { return streamToString(stream); } private string streamToString(Stream stream) { stream.Position = 0; using var reader = new StreamReader(stream); return reader.ReadToEnd(); } }
这种方式的优势是利用BenchmarkDotNet原生的高精度统计逻辑,无需手动计算;核心注意点是必须保证Bar1基准方法的输入和Foo1中调用时的输入完全一致,否则统计数据没有参考价值。
内容的提问来源于stack exchange,提问作者Vivek MVK
相关产品推荐
相关产品推荐

