必须用release模式运行,否则benchmarkdotnet直接报错退出;测试类须为public,[benchmark]方法须为public void无参,初始化逻辑须置于[globalsetup]中,禁用干扰源并关注mean与allocated指标。

必须用 Release 模式运行,否则 BenchmarkDotNet 直接报错退出 —— 这不是警告,是硬性限制。
测试类和方法的可见性与签名要求
BenchmarkDotNet 不会自动发现 internal 或 private 类,也不会执行返回 string、Task 或带参数的方法。它只认 public 的无参 void 方法(或显式配置为支持 Task)。
- 类必须声明为
public class MyBenchmark,不能是internal class或嵌套类 -
[Benchmark]方法必须是public、void、无参数(哪怕只传一个IHost也要额外启用配置) - 若用 .NET SDK 风格项目,确认
.csproj顶部是<project sdk="Microsoft.NET.Sdk"></project>,不是旧式ToolsVersion项目 - 首次运行失败时先检查
dotnet --list-sdks,确保安装了 .NET 6+ SDK(BenchmarkDotNet 从 v0.13 起已弃用对 .NET Framework 4.x 的默认支持)
初始化逻辑必须放在 [GlobalSetup] 里
在 [Benchmark] 方法体内 new 对象、读文件、生成随机数据 —— 这些操作会被反复执行,污染耗时和内存统计。BenchmarkDotNet 的预热和多轮迭代机制,只对「被测逻辑本身」生效,不保护你的错误放置。
- 所有一次性准备动作:如构造测试数组、写临时文件、初始化
StringBuilder缓冲区,统一挪到[GlobalSetup]方法中 - 对应清理动作(如删除临时文件)放
[GlobalCleanup],避免残留影响下一轮测试 - 字段应声明为
private readonly,并在[GlobalSetup]中赋值,不要在构造函数里做 —— BenchmarkDotNet 不保证构造函数只调用一次 - 示例中常见错误:
[Benchmark] public void Test() { var list = new List<int>(1000); ... }</int>→ list 创建开销全算进结果里
别信 Stopwatch,也别信 Debug 模式下的任何数字
手动用 Stopwatch 测字符串拼接或哈希计算,结果偏差可达 3–5 倍;Debug 模式下 JIT 不启用优化,BenchmarkRunner.Run<t>()</t> 会直接抛异常并提示 “Benchmark was built without optimization enabled”。
- 务必用
dotnet run -c Release运行,Visual Studio 中也要切到 Release 配置再启动 - 禁用干扰源:关闭 Chrome、OneDrive、杀毒软件实时扫描;测试期间勿打开 Visual Studio 调试器(附加进程会拖慢计时)
- 对 IO 类测试(如文件读取),需额外加
[MemoryDiagnoser(disableGcTimer: true)],否则 GC 强制触发会打断磁盘等待,让结果失真 - 关键指标看
Mean(平均耗时)和Allocated(单次分配字节数),Gen0波动大说明对象生命周期不稳定,得回头查[GlobalSetup]是否漏了复用逻辑
报告里的 Allocated 字段比 Mean 更容易暴露真实瓶颈
两个方法 Mean 差距不大,但一个 Allocated = 24 B,另一个 Allocated = 0 B —— 后者在高频循环中可能省下数 MB/s 的 GC 压力。C# 13 的零分配委托优化、Span<char></char> 替代 string.Substring,都靠这个字段验证效果。
-
Allocated是每轮迭代的堆内存净增,不是峰值;多次调用同一方法时,它反映的是“每次调用引入的新对象” - 若某方法
Allocated非零但逻辑上不该分配(比如只做数值计算),大概率是隐式装箱、LINQ 构造器(Enumerable.Range)、或字符串插值未用Span优化 - 对比不同 .NET 版本时,注意
Gen0次数:.NET 8 的 GC 改进会让同量分配触发更少回收,但Allocated数值不变,仍是横向比较的锚点











