C# 基准测试
最后修改于 2023 年 7 月 5 日
在本文中,我们使用 BenchmarkDotNet 库对 C# 代码进行基准测试。
基准测试是衡量代码性能的过程。它使我们能够确定程序中的性能瓶颈。
BenchmarkDotNet 是一个强大的 .NET 库,用于执行基准测试。我们可以测量 C#、F# 和 VB 代码。
$ dotnet add package BenchmarkDotNet
我们安装 BenchmarkDotNet 包。
$ dotnet run --project SimpleEx.csproj -c Release
这是我们运行基准测试的方法。
C# 基准测试简单示例
在以下示例中,我们测量字符串连接的各种方法的性能。
Program.cs
using System.Text; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Running; [MemoryDiagnoser] public class Program { int n = 10_000; [Benchmark] public string Builder() { StringBuilder output = new StringBuilder(); for (int i = 0; i < n; i++) { output.Append("falcon").Append(i); } return output.ToString(); } [Benchmark] public string Interpolation() { string output = string.Empty; for (int i = 0; i < n; i++) { output = $"{output}falcon{i}"; } return output; } [Benchmark] public string Addition() { string output = string.Empty; for (int i = 0; i < n; i++) { output += "falcon" + i; } return output.ToString(); } static void Main(string[] args) { var summary = BenchmarkRunner.Run<Program>(); } }
在该程序中,我们比较了三种字符串连接方法:使用 StringBuilder、字符串插值和加法运算。
[MemoryDiagnoser] public class Program { ... }
使用 [MemoryDiagnoser]
,我们还可以测量内存使用情况。
[Benchmark] public string Builder() { StringBuilder output = new StringBuilder(); for (int i = 0; i < n; i++) { output.Append("falcon").Append(i); } return output.ToString(); }
此方法使用 StringBuilder
添加字符串。该方法使用 [Benchmark]
进行装饰。
var summary = BenchmarkRunner.Run<Program>();
我们运行基准测试。
// * Summary * BenchmarkDotNet=v0.13.2, OS=ubuntu 22.04 11th Gen Intel Core i5-1135G7 2.40GHz, 1 CPU, 8 logical and 4 physical cores .NET SDK=6.0.104 [Host] : .NET 6.0.4 (6.0.422.16404), X64 RyuJIT AVX2 DefaultJob : .NET 6.0.4 (6.0.422.16404), X64 RyuJIT AVX2 | Method | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated | |-------------- |-------------:|----------:|----------:|------------:|------------:|------------:|-------------:| | Builder | 120.6 us | 0.30 us | 0.27 us | 62.3779 | 62.3779 | 62.3779 | 398.29 KB | | Interpolation | 120,826.4 us | 354.51 us | 331.61 us | 290600.0000 | 247600.0000 | 247600.0000 | 956382.51 KB | | Addition | 73,354.2 us | 448.96 us | 419.96 us | 290714.2857 | 249000.0000 | 247714.2857 | 956694.4 KB |
输出包括操作系统和硬件摘要,以及显示基准测试统计信息的表格。从输出中我们可以看到,加法运算速度最快,而 builder 的内存效率最高。
C# 基准测试排序算法
在下一个示例中,我们对排序算法进行基准测试。
Program.cs
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Running; [MemoryDiagnoser] public class Program { const int n = 100_000; int[] vals = new int[n]; [GlobalSetup] public void GlobalSetup() { var rnd = new Random(); for (int i = 0; i < n; i++) { vals[i] = rnd.Next(1, 100); } } [Benchmark] public void SelectionSort() { int len = vals.Length; for (int i = 0; i < len - 1; i++) { int min_idx = i; for (int j = i + 1; j < len; j++) { if (vals[j] < vals[min_idx]) { min_idx = j; } } int temp = vals[min_idx]; vals[min_idx] = vals[i]; vals[i] = temp; } } [Benchmark] public void BubbleSort() { int len = vals.Length; for (int i = 0; i < len - 1; i++) { for (int j = 0; j < len - i - 1; j++) { if (vals[j] > vals[j + 1]) { int temp = vals[j]; vals[j] = vals[j + 1]; vals[j + 1] = temp; } } } } static void Main(string[] args) { var summary = BenchmarkRunner.Run<Program>(); } }
我们将选择排序与冒泡排序算法进行比较。
const int n = 100_000; int[] vals = new int[n]; [GlobalSetup] public void GlobalSetup() { var rnd = new Random(); for (int i = 0; i < n; i++) { vals[i] = rnd.Next(1, n); } }
使用 [GlobalSetup]
属性,我们准备一个包含 100000 个随机选择的介于 1 和 100000 之间的整数值的数组。此代码仅执行一次。
[Benchmark] public void SelectionSort() { int len = vals.Length; for (int i = 0; i < len - 1; i++) { int min_idx = i; for (int j = i + 1; j < len; j++) { if (vals[j] < vals[min_idx]) { min_idx = j; } } int temp = vals[min_idx]; vals[min_idx] = vals[i]; vals[i] = temp; } }
我们有选择排序算法;它对准备好的整数数组进行排序。
| Method | Mean | Error | StdDev | Allocated | |-------------- |--------:|---------:|---------:|----------:| | SelectionSort | 3.646 s | 0.0569 s | 0.0532 s | 1.38 KB | | BubbleSort | 4.124 s | 0.0136 s | 0.0127 s | 3.28 KB |
选择排序在内存和速度方面都略胜一筹。
来源
在本文中,我们使用 BenchmarkDotNet 库测量了 C# 代码的性能。
作者
列出所有 C# 教程。