在.NET开发中,当我们需要对比不同算法的执行效率时,手动编写计时代码往往会受到JIT编译、垃圾回收等环境因素的干扰,得到的结果不够准确。Benchmark.NET作为专业的基准测试库,能够自动处理这些干扰因素,提供可靠的性能对比数据,是算法性能比较的常用工具。

环境准备
首先需要在项目中安装Benchmark.NET的NuGet包,目前最新稳定版本可以通过包管理器或者命令行安装。如果是控制台项目,安装完成后还需要调整项目的输出类型和编译配置,确保测试能够在合适的环境下运行。
安装命令如下:
dotnet add package BenchmarkDotNet
编写测试代码
假设我们要对比两种字符串拼接算法的性能,一种是使用string直接拼接,另一种是使用StringBuilder拼接。我们需要创建一个测试类,在类中定义对应的测试方法和待对比的算法逻辑。
测试类基本结构
测试类需要加上[BenchmarkCategory]等特性吗?不需要,Benchmark.NET的测试类只需要保证是公共的,并且包含标记了[Benchmark]特性的方法即可,每个标记了该特性的方法对应一个待测试的算法场景。
以下是完整的测试代码示例:
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System;
using System.Text;
namespace BenchmarkDemo
{
// 公共的测试类
public class StringConcatBenchmark
{
// 测试数据,避免每次生成带来额外开销
private const int LoopCount = 1000;
private string[] testData;
// 初始化方法,在每个测试执行前运行,用于准备测试数据
[GlobalSetup]
public void Setup()
{
testData = new string[LoopCount];
for (int i = 0; i < LoopCount; i++)
{
testData[i] = Guid.NewGuid().ToString();
}
}
// 标记该方法为基准测试方法,对应第一种字符串拼接算法
[Benchmark]
public string StringDirectConcat()
{
string result = "";
for (int i = 0; i < testData.Length; i++)
{
result += testData[i];
}
return result;
}
// 标记该方法为基准测试方法,对应第二种字符串拼接算法
[Benchmark]
public string StringBuilderConcat()
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < testData.Length; i++)
{
sb.Append(testData[i]);
}
return sb.ToString();
}
}
class Program
{
static void Main(string[] args)
{
// 运行基准测试,传入测试类的类型
var summary = BenchmarkRunner.Run<StringConcatBenchmark>();
}
}
}
代码说明
[GlobalSetup]特性标记的方法会在所有测试执行前运行一次,用于初始化测试所需的公共数据,避免数据生成逻辑影响测试结果。[Benchmark]特性用于标记需要测试性能的方法,Benchmark.NET会自动识别这些方法并执行多次测试。- 测试方法的返回值建议保留,避免编译器做死代码优化,导致测试结果不准确。
运行测试与结果分析
编写完代码后,需要将项目的编译配置切换为Release模式,然后直接运行项目,不要使用调试模式运行,否则调试器的附加会影响性能测试结果。
测试运行完成后,控制台会输出详细的测试结果,包含每个测试方法的均值、标准差、中位数、内存分配等核心指标。我们主要关注以下几个指标:
| 指标名称 | 指标含义 |
|---|---|
| Mean | 方法执行一次的平均耗时,是最核心的性能对比指标 |
| StdDev | 耗时的标准差,数值越小说明测试结果越稳定 |
| Gen 0/1/2 | 垃圾回收的代数,数值越高说明内存分配越频繁 |
| Allocated | 方法执行一次分配的内存总量,用于对比内存使用效率 |
以上面的字符串拼接测试为例,通常StringBuilderConcat方法的Mean耗时和Allocated内存都会远低于StringDirectConcat方法,说明在大量字符串拼接场景下,StringBuilder的性能更优。
常用配置项
如果需要对测试过程做更精细的控制,可以通过特性配置测试参数:
[IterationCount]:指定每个测试方法的迭代次数,默认由Benchmark.NET自动计算。[WarmupCount]:指定预热迭代的次数,预热可以让JIT完成编译,避免编译过程影响测试结果。[Params]:可以给测试方法传入不同的参数,测试不同参数下算法的性能表现,例如可以测试不同循环次数下两种拼接算法的性能差异。
以下是带参数的测试示例:
using BenchmarkDotNet.Attributes;
using System.Text;
public class ParamBenchmark
{
// 定义参数,测试时会分别用10、100、1000三个值运行测试
[Params(10, 100, 1000)]
public int Count { get; set; }
private string[] testData;
[GlobalSetup]
public void Setup()
{
testData = new string[Count];
for (int i = 0; i < Count; i++)
{
testData[i] = i.ToString();
}
}
[Benchmark]
public string StringConcat()
{
string result = "";
for (int i = 0; i < Count; i++)
{
result += testData[i];
}
return result;
}
[Benchmark]
public string StringBuilderConcat()
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < Count; i++)
{
sb.Append(testData[i]);
}
return sb.ToString();
}
}
注意事项
不要在调试模式下运行基准测试,调试器会严重干扰测试结果,导致数据完全失真。
测试方法的逻辑要尽量纯粹,只保留待测试的算法逻辑,避免混入日志输出、文件读写等无关操作。
如果测试涉及外部资源(如数据库、网络请求),需要保证外部环境稳定,否则测试结果会出现较大波动。
通过Benchmark.NET,我们可以快速、准确地对比不同算法的性能差异,为技术选型提供可靠的数据支撑,避免凭感觉判断算法优劣的情况。
Benchmark_NET算法性能比较C#性能测试基准测试修改时间:2026-07-07 23:06:36