Ytelsesmåling med BenchmarkDotNet
Skriv nøyaktige mikroytelsestester med BenchmarkDotNet, analyser gjennomstrømning og allokeringer, og unngå vanlige fallgruver.
Ytelsesmåling med BenchmarkDotNet er en gratis leksjon i C# Academy på CoddyKit. Dette er leksjon 4 av 4. Du kan lese hele leksjonen gratis nedenfor – og deretter øve praktisk i nettleseren med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i C# Academy, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i C# Academy inneholder totalt 4 leksjoner.
Hvorfor BenchmarkDotNet
Mikrobenchmarking er overraskende vanskelig: JIT-oppvarming, GC-pauser, CPU-buffere og planlegging i operativsystemet introduserer alle støy. BenchmarkDotNet håndterer alt dette automatisk og produserer statistisk pålitelige resultater.
Installere og kjøre den første benchmarken
Legg til NuGet-pakken, opprett en klasse med metoder merket med [Benchmark], og kjør med BenchmarkRunner.Run. Kjør alltid benchmarker i Release-modus.
// dotnet add package BenchmarkDotNet
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
[MemoryDiagnoser] // report allocations
[RankColumn] // rank methods by speed
public class StringBenchmarks
{
[Benchmark(Baseline = true)]
public string Concatenation()
{
var s = "";
for (int i = 0; i < 100; i++) s += i;
return s;
}
[Benchmark]
public string StringBuilder()
{
var sb = new System.Text.StringBuilder();
for (int i = 0; i < 100; i++) sb.Append(i);
return sb.ToString();
}
}
// Program.cs (must run as Release):
BenchmarkRunner.Run<StringBenchmarks>();Lese resultattabellen
BenchmarkDotNet skriver ut en resultattabell med gjennomsnittstid, feilmargin, standardavvik, forholdstall og minneallokering. Slik tolker du hver kolonne.
// Example output:
// | Method | Mean | Error | StdDev | Ratio | Alloc |
// |-------------- |---------:|--------:|--------:|------:|------:|
// | Concatenation | 4,210 ns | 81 ns | 76 ns | 1.00 | 6.4KB |
// | StringBuilder | 312 ns | 4 ns | 4 ns | 0.07 | 1.6KB |
// Mean: average time per operation
// Error: half of 99.9% confidence interval
// StdDev: standard deviation — high value = noisy environment
// Ratio: relative to Baseline=true method (1.00)
// Alloc: managed heap allocated per operation
// Rule of thumb:
// Error should be < 5% of Mean for reliable results
// Run on an isolated machine (no background tasks)Parameteriserte benchmarker
Bruk [Params] for å kjøre en benchmark med flere inndatastørrelser. BenchmarkDotNet kjører alle kombinasjoner og genererer en egen rad for hver parameterverdi.
[MemoryDiagnoser]
public class SearchBenchmarks
{
[Params(10, 100, 1000, 10000)]
public int N;
private int[] _data = Array.Empty<int>();
[GlobalSetup]
public void Setup()
=> _data = Enumerable.Range(0, N).ToArray();
[Benchmark]
public bool LinearSearch()
=> Array.IndexOf(_data, N - 1) >= 0;
[Benchmark]
public bool BinarySearch()
=> Array.BinarySearch(_data, N - 1) >= 0;
}Oppsett og opprydding
Bruk livssyklusattributter til å kjøre engangsoppsett eller oppsett/opprydding for hver iterasjon. Da holdes kostnaden for oppsettet utenfor den målte benchmarktiden.
[MemoryDiagnoser]
public class DbQueryBenchmarks
{
private NpgsqlConnection _conn = null!;
private NpgsqlCommand _cmd = null!;
[GlobalSetup] // runs once before all benchmarks
public void Setup()
{
_conn = new NpgsqlConnection("...");
_conn.Open();
_cmd = new NpgsqlCommand("SELECT id FROM orders LIMIT 100", _conn);
}
[IterationSetup] // runs before EACH benchmark iteration
public void IterationSetup() { /* per-iteration prep */ }
[Benchmark]
public int QueryOrders() => (int)_cmd.ExecuteScalar()!;
[GlobalCleanup] // runs once after all benchmarks
public void Cleanup() { _cmd.Dispose(); _conn.Dispose(); }
}Minnediagnostikk
[MemoryDiagnoser] rapporterer allokeringer på managed heap per operasjon. Kombiner den med alternativene i [GcForce] for å kontrollere GC-oppførselen under benchmarker.
[MemoryDiagnoser] // adds Alloc column to results
public class AllocationBenchmarks
{
[Benchmark]
public int[] AllocateArray() => new int[1000];
[Benchmark]
public Span<int> UseStackalloc()
{
Span<int> span = stackalloc int[1000];
return span; // WARN: for illustration; don't return stackalloc
}
[Benchmark]
public int[] ArrayPoolRent()
{
var arr = System.Buffers.ArrayPool<int>.Shared.Rent(1000);
System.Buffers.ArrayPool<int>.Shared.Return(arr);
return arr;
}
}Diagnostikkverktøy: ETW og disassemblering
BenchmarkDotNet støtter flere diagnostikkverktøy. DisassemblyDiagnoser viser den faktiske native assembly-utdataen, noe som er perfekt for å kontrollere JIT-optimaliseringer.
using BenchmarkDotNet.Diagnosers;
[DisassemblyDiagnoser( // show generated ASM
maxDepth: 2,
printSource: true,
exportGithubMarkdown: true)]
[MemoryDiagnoser]
public class VectorizationBenchmarks
{
private float[] _data = new float[1024];
[Benchmark]
public float SumScalar()
{
float sum = 0;
foreach (var v in _data) sum += v;
return sum;
}
[Benchmark]
public float SumVectorized()
=> System.Numerics.Vector<float>.Count > 1
? VectorSum(_data)
: SumScalar();
}Jobbkonfigurasjoner
Jobber styrer miljøet benchmarken kjøres i — antall oppvarmingsiterasjoner, kall per iterasjon, .NET-runtimeversjon og JIT-innstillinger. Bruk forhåndsdefinerte eller egendefinerte jobber.
using BenchmarkDotNet.Jobs;
// Compare JIT vs Native AOT performance:
[SimpleJob(RuntimeMoniker.Net90)] // .NET 9 JIT
[SimpleJob(RuntimeMoniker.NativeAot90)] // .NET 9 AOT
[MemoryDiagnoser]
public class RuntimeComparison
{
[Benchmark]
public string SerializeJson()
{
var obj = new { Name = "Alice", Age = 30 };
return System.Text.Json.JsonSerializer.Serialize(obj);
}
}
// Custom job for more warmup:
[Config(typeof(MyConfig))]
public class PreciseBenchmarks
{
private class MyConfig : ManualConfig
{
public MyConfig()
=> AddJob(Job.Default.WithWarmupCount(5).WithIterationCount(20));
}
}Eksportere resultater
BenchmarkDotNet eksporterer resultater i flere formater — Markdown (for GitHub-PR-er), CSV (for regneark), JSON og HTML-diagrammer. Eksportalternativene konfigureres i jobbkonfigurasjonen.
using BenchmarkDotNet.Exporters;
using BenchmarkDotNet.Exporters.Csv;
[Config(typeof(ExportConfig))]
public class MyBenchmarks { /* ... */ }
public class ExportConfig : ManualConfig
{
public ExportConfig()
{
AddExporter(MarkdownExporter.GitHub);
AddExporter(CsvExporter.Default);
AddExporter(HtmlExporter.Default);
// Results in: BenchmarkDotNet.Artifacts/results/
}
}
// CLI usage to run specific benchmarks:
// dotnet run -c Release -- --filter "*StringBenchmarks*"
// dotnet run -c Release -- --list all (list all benchmarks)I praksis: Optimalisering før og etter
Bruk BenchmarkDotNet til å validere en ytelsesoptimalisering — sammenlign naive, pool-baserte og Span-baserte tilnærminger.
[MemoryDiagnoser]
[RankColumn]
public class ParsingBenchmarks
{
private const string Input = "2025-06-15T08:30:00Z";
[Benchmark(Baseline = true)]
public DateTime ParseViaString()
=> DateTime.Parse(Input);
[Benchmark]
public DateTime ParseExact()
=> DateTime.ParseExact(Input,
"yyyy-MM-ddTHH:mm:ssZ",
System.Globalization.CultureInfo.InvariantCulture);
[Benchmark]
public bool TryParse()
=> DateTime.TryParseExact(Input.AsSpan(),
"yyyy-MM-ddTHH:mm:ssZ",
System.Globalization.CultureInfo.InvariantCulture,
System.Globalization.DateTimeStyles.None,
out _);
}Rask kontroll
Hvorfor må BenchmarkDotNet-benchmarkene kjøres med dotnet run -c Release?
Oppsummering: Benchmarking med BenchmarkDotNet
Viktigste punkter:
- Installer via NuGet, merk metoder med
[Benchmark], og kjør meddotnet run -c Release [MemoryDiagnoser]: legger til en kolonne for allokeringer;[RankColumn]: rangerer etter hastighet[Params]: kjør med flere inndatastørrelser for å finne algoritmisk kompleksitet[GlobalSetup]/[GlobalCleanup]: engangsoppsett utenfor den målte tiden[DisassemblyDiagnoser]: undersøk generert native ASM for å kontrollere JIT-optimaliseringer- Bruk jobber til å sammenligne .NET 9 JIT med Native AOT og ulike antall iterasjoner
Lær deg C# med en AI-veileder – gratis
Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.
- Kurs
- 93
- Leksjoner
- 346
Ofte stilte spørsmål
Er leksjonen «Ytelsesmåling med BenchmarkDotNet» gratis?
Ja – hele teksten i «Ytelsesmåling med BenchmarkDotNet» er gratis å lese her på nettet. For å øve interaktivt med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt, og for å låse opp resten av C# Academy-kurset, kan du oppgradere til CoddyKit PRO. Kurset i C# Academy inneholder totalt 4 leksjoner.
Hva lærer jeg i «Ytelsesmåling med BenchmarkDotNet»?
Skriv nøyaktige mikroytelsestester med BenchmarkDotNet, analyser gjennomstrømning og allokeringer, og unngå vanlige fallgruver. Du øver på C# Academy med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.
Trenger jeg erfaring for å begynne med C# Academy?
Ingen tidligere erfaring er nødvendig. C# Academy på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 4 av 4.
Hvor lang tid tar leksjonen «Ytelsesmåling med BenchmarkDotNet»?
De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.
Kan jeg skrive og kjøre kode i denne C# Academy-leksjonen?
Ja. Alle C# Academy-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.
Alle leksjonene i dette kurset
- Native AOT-kompilering
- Trimming og refleksjonsbegrensninger
- ReadyToRun og trinnvis kompilering
- Ytelsesmåling med BenchmarkDotNet