C# Academy · Les

Benchmarking met BenchmarkDotNet

Schrijf nauwkeurige microbenchmarks met BenchmarkDotNet, analyseer doorvoer en allocaties en vermijd veelvoorkomende valkuilen.

Les 4 van 412 stappen

Benchmarking met BenchmarkDotNet is een gratis C# Academy-les op CoddyKit. Dit is les 4 van 4. Je kunt de volledige les hieronder gratis lezen en daarna in de browser praktisch oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject C# Academy. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus C# Academy bevat in totaal 4 lessen.

Waarom BenchmarkDotNet

Microbenchmarking is verrassend moeilijk: JIT-opwarming, GC-pauzes, CPU-caches en planning door het besturingssysteem veroorzaken allemaal ruis. BenchmarkDotNet handelt dit allemaal automatisch af en produceert statistisch betrouwbare resultaten.

Installatie en eerste benchmark

Voeg het NuGet-pakket toe, maak een klasse met methoden met [Benchmark] en voer deze uit met BenchmarkRunner.Run. Voer benchmarks altijd uit in de 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>();

De resultatentabel lezen

BenchmarkDotNet drukt een resultatentabel af met gemiddelde tijd, fout, standaardafwijking, verhouding en geheugentoewijzing. Zo interpreteer je elke kolom.

// 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)

Geparametriseerde benchmarks

Gebruik [Params] om een benchmark uit te voeren voor meerdere invoergrootten. BenchmarkDotNet voert elke combinatie uit en genereert een afzonderlijke rij per parameterwaarde.

[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;
}

Configuratie en opruiming

Gebruik levenscycluskenmerken om eenmalige configuratie of configuratie en opruiming per iteratie uit te voeren. Zo blijven de configuratiekosten buiten de gemeten benchmarktijd.

[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(); }
}

Geheugendiagnostiek

[MemoryDiagnoser] rapporteert toewijzingen op de beheerde heap per bewerking. Combineer dit met [GcForce]-opties om het GC-gedrag tijdens benchmarks te beheren.

[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;
    }
}

Diagnostische hulpmiddelen: ETW en disassemblage

BenchmarkDotNet ondersteunt meerdere diagnostische hulpmiddelen. De DisassemblyDiagnoser toont de daadwerkelijke systeemeigen assembleruitvoer, ideaal om JIT-optimalisaties te controleren.

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();
}

Taakconfiguraties

Taken bepalen de uitvoeringsomgeving van benchmarks — het aantal opwarmiteraties, het aantal aanroepen per iteratie, de .NET-runtimeversie en de JIT-instellingen. Gebruik vooraf gedefinieerde of aangepaste taken.

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));
    }
}

Resultaten exporteren

BenchmarkDotNet exporteert resultaten in meerdere indelingen — Markdown (voor GitHub-pullaanvragen), CSV (voor spreadsheets), JSON en HTML-grafieken. Exportopties worden in de taakconfiguratie ingesteld.

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)

Praktijkvoorbeeld: optimalisatie voor en na

BenchmarkDotNet gebruiken om een prestatieoptimalisatie te valideren — door naïeve, gepoolde en op Span gebaseerde aanpakken te vergelijken.

[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 _);
}

Korte controle

Waarom moeten BenchmarkDotNet-benchmarks worden uitgevoerd met dotnet run -c Release?

Samenvatting: benchmarken met BenchmarkDotNet

Belangrijkste punten:

  • Installeer via NuGet, markeer methoden met [Benchmark] en voer ze uit met dotnet run -c Release
  • [MemoryDiagnoser]: voegt een kolom voor geheugentoewijzingen toe; [RankColumn]: rangschikt op snelheid
  • [Params]: voer de benchmark uit voor meerdere invoergrootten om de algoritmische complexiteit te bepalen
  • [GlobalSetup] / [GlobalCleanup]: eenmalige configuratie buiten de gemeten tijd
  • [DisassemblyDiagnoser]: inspecteer de gegenereerde systeemeigen ASM om JIT-optimalisaties te controleren
  • Gebruik taken om de JIT van .NET 9 te vergelijken met Native AOT en met verschillende aantallen iteraties
Gratis beginnen

Leer C# met een AI-tutor — gratis

Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.

Cursussen
93
Lessen
346

Veelgestelde vragen

Is de les “Benchmarking met BenchmarkDotNet” gratis?

Ja — de volledige tekst van “Benchmarking met BenchmarkDotNet” kun je hier gratis op het web lezen. Als je interactief wilt oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is, en de rest van de cursus C# Academy wilt ontgrendelen, kun je upgraden naar CoddyKit PRO. De cursus C# Academy bevat in totaal 4 lessen.

Wat leer ik in “Benchmarking met BenchmarkDotNet”?

Schrijf nauwkeurige microbenchmarks met BenchmarkDotNet, analyseer doorvoer en allocaties en vermijd veelvoorkomende valkuilen. Je oefent met C# Academy door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.

Heb ik ervaring nodig om met C# Academy te beginnen?

Ervaring vooraf is niet nodig. C# Academy op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 4 van 4.

Hoe lang duurt de les “Benchmarking met BenchmarkDotNet”?

De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.

Kan ik code schrijven en uitvoeren in deze les over C# Academy?

Ja. Elke les over C# Academy bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.

Alle lessen in deze cursus

  1. Native AOT-compilatie
  2. Trimming en beperkingen van reflection
  3. ReadyToRun en tiered compilation
  4. Benchmarking met BenchmarkDotNet
← Terug naar C# Academy