.NET9 实现字符串拼接(StringConcatenation)性能测试
为了评估 .NET9
平台上使用 C#
中不同字符串拼接操作的性能表现,我们可以使用 BenchmarkDotNet
这一强大的开源库来构建科学且可重复的基准测试。
BenchmarkDotNet
能够自动处理诸如 JIT 编译、预热(Warm-up)、运行次数控制、统计误差分析等底层细节,确保测试结果具有高度准确性与可比性。
在 .NET9
中,使用 C#
字符串拼接的常见方式包括:
- 使用
+
运算符 - 使用
string.Concat
- 使用
string.Format
- 使用插值字符串
$"{variable}"
- 使用
StringBuilder
为了满足大家对性能的需求,我会创建两个类:
StringConcatenationOperations.cs
:包含各种字符串拼接操作的实现。StringConcatenationBenchmark.cs
:使用BenchmarkDotNet
对这些操作进行基准测试。
以下是具体的代码实现。
项目准备
- 项目信息
<Project Sdk="Microsoft.NET.Sdk"><PropertyGroup><OutputType>Exe</OutputType><TargetFramework>net9.0</TargetFramework><ImplicitUsings>enable</ImplicitUsings><Nullable>enable</Nullable><PublishAot>true</PublishAot><InvariantGlobalization>true</InvariantGlobalization></PropertyGroup><ItemGroup><PackageReference Include="Datadog.Trace.BenchmarkDotNet" Version="2.61.0" /></ItemGroup></Project>
StringConcatenationOperations
// ====================================================
// 字符串拼接实现:在 .NET 中,字符串拼接的常见方式
// ====================================================using System.Text;namespace BenchmarkTest.examples.StringConcatenation;internal static class StringConcatenationOperations
{// 使用 + 运算符public static string UsePlusOperator(string a, string b, string c){return a + b + c;}// 使用 string.Concatpublic static string UseStringConcat(string a, string b, string c){return string.Concat(a, b, c);}// 使用 string.Formatpublic static string UseStringFormat(string a, string b, string c){return string.Format("{0}{1}{2}", a, b, c);}// 使用插值字符串 $"{variable}"public static string UseStringInterpolation(string a, string b, string c){return $"{a}{b}{c}";}// 使用 StringBuilderpublic static string UseStringBuilder(string a, string b, string c){var sb = new StringBuilder();sb.Append(a);sb.Append(b);sb.Append(c);return sb.ToString();}
}
StringConcatenationBenchmark
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Running;
using Datadog.Trace.BenchmarkDotNet;namespace BenchmarkTest.examples.StringConcatenation;[DatadogDiagnoser]
[MemoryDiagnoser]
public class StringConcatenationBenchmark
{private const string A = "Hello";private const string B = " ";private const string C = "World";[Benchmark]public string PlusOperator(){return StringConcatenationOperations.UsePlusOperator(A, B, C);}[Benchmark]public string StringConcat(){return StringConcatenationOperations.UseStringConcat(A, B, C);}[Benchmark]public string StringFormat(){return StringConcatenationOperations.UseStringFormat(A, B, C);}[Benchmark]public string StringInterpolation(){return StringConcatenationOperations.UseStringInterpolation(A, B, C);}[Benchmark]public string StringBuilder(){return StringConcatenationOperations.UseStringBuilder(A, B, C);}public static void Run(IConfig config){var summary = BenchmarkRunner.Run<StringConcatenationBenchmark>(config);Console.WriteLine(summary);}
}
- 在
Program.cs
中运行基准测试
using BenchmarkDotNet.Configs;
using Datadog.Trace.BenchmarkDotNet;
using BenchmarkTest.examples.StringConcatenation;Console.WriteLine("Hello, BenchmarkDotNetTest!");var config = DefaultConfig.Instance.WithDatadog();
StringConcatenationBenchmark.Run(config);
- 运行测试
在项目根目录,使用 pwsh
终端输入命令:
dotnet run -c Release
输出信息:
以下是对 BenchmarkDotNet
测试结果的详细分析:
🧪 测试环境信息
- BenchmarkDotNet 版本:
v0.13.2
- 操作系统:
Windows 11 (10.0.26100.4484)
- .NET SDK:
9.0.301
- 运行时:
.NET 9.0.6 (9.0.625.26613), X64 AOT AVX2
- JIT 编译器:
RyuJIT AVX2
📊 基准测试结果
方法名 | 平均耗时(Mean) | 误差范围(Error) | 标准差(StdDev) | GC Gen0 次数 | 内存分配 |
---|---|---|---|---|---|
PlusOperator | 21.01 ns | 0.233 ns | 0.207 ns | 0.0306 | 48 B |
StringConcat | 21.06 ns | 0.493 ns | 0.606 ns | 0.0306 | 48 B |
StringFormat | 78.65 ns | 1.389 ns | 1.300 ns | 0.0305 | 48 B |
StringInterpolation | 20.88 ns | 0.460 ns | 0.529 ns | 0.0306 | 48 B |
StringBuilder | 35.37 ns | 0.769 ns | 1.175 ns | 0.0969 | 152 B |
🔍 逐项分析
1. +
运算符拼接 (PlusOperator
)
- 平均耗时: 21.01 ns
- 内存分配: 48 字节
- 特点:
- 简洁直观,适合少量字符串拼接。
- 在编译时会自动优化为
string.Concat
。
- 结论: 性能优秀,适合简单拼接场景。
2. string.Concat
(StringConcat
)
- 平均耗时: 21.06 ns
- 内存分配: 48 字节
- 特点:
- 直接调用底层方法,性能与
+
相当。 - 更加显式地表达意图,适合对性能敏感或需要明确控制拼接逻辑的代码。
- 直接调用底层方法,性能与
- 结论: 和
+
类似,但更推荐用于多参数拼接。
3. string.Format
(StringFormat
)
- 平均耗时: 78.65 ns
- 内存分配: 48 字节
- 特点:
- 支持格式化字符串,适合需要插入变量和格式控制的场景。
- 由于涉及解析格式字符串,性能较差。
- 结论: 如果需要格式化,可以使用;否则建议避免。
4. 插值字符串 (StringInterpolation
)
- 平均耗时: 20.88 ns
- 内存分配: 48 字节
- 特点:
C# 6
引入的新特性,语法简洁、可读性高。- 实际上会被编译为
string.Format
或直接内联为string.Concat
。
- 结论: 性能优异,同时具备良好的可读性,推荐使用。
5. StringBuilder
(StringBuilder
)
- 平均耗时: 35.37 ns
- 内存分配: 152 字节
- 特点:
- 适用于频繁修改或大量拼接的场景(如循环中拼接字符串)。
- 初始化和操作有一定开销,不适合一次性小规模拼接。
- 结论: 多次拼接时优势明显,单次拼接不推荐。
⚠️ 警告信息
- MultimodalDistribution(多峰分布)
StringBuilder
方法的执行时间分布呈现双峰(bimodal
),说明其性能波动较大。- 可能原因:
GC
影响、缓存命中率变化或其他外部因素。
⚠️ 提示信息
- Outliers(异常值)
PlusOperator
、StringFormat
和StringInterpolation
都检测到了一些性能异常值。- 通常由系统负载、
GC
操作或其他临时干扰引起。 - 建议多次运行以确保数据稳定性。
📌 总结建议
使用场景 | 推荐方式 | 原因 |
---|---|---|
简单拼接 | + 或 string.Concat | 性能最佳,语法简洁 |
需要格式化输出 | string.Format | 功能强大,但性能略差 |
提高可读性和现代写法 | 插值字符串 $"{a}{b}" | 性能接近最优,语法清晰 |
循环拼接或大量拼接 | StringBuilder | 减少内存分配,避免频繁创建新字符串 |
高频调用的小规模拼接 | 避免使用 StringBuilder | 初始开销大,小规模拼接时不划算 |
如果你希望进一步优化测试,比如增加样本数量、调整测试参数,或者测试不同长度的字符串拼接,我可以帮助你修改基准测试代码。欢迎继续提问!