C# Academy · Oppitunti

Benchmarking BenchmarkDotNetillä

Kirjoittakaa tarkkoja mikrovertailuja BenchmarkDotNetillä, analysoikaa läpimenoa ja allokointeja sekä välttäkää yleiset sudenkuopat.

Oppitunti 4/412 vaihetta

Benchmarking BenchmarkDotNetillä on ilmainen C# Academy-oppitunti CoddyKitissä. Tämä on oppitunti 4/4. Voit lukea koko oppitunnin alta ilmaiseksi ja harjoitella sen jälkeen käytännössä selaimessa sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla. Oppitunti kuuluu C# Academy-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. C# Academy-kurssilla on yhteensä 4 oppituntia.

Miksi BenchmarkDotNet?

Mikrobenchmarkkaus on yllättävän vaikeaa: JIT:n lämmitys, roskienkeruun tauot, suorittimen välimuistit ja käyttöjärjestelmän ajoitus aiheuttavat kaikki kohinaa. BenchmarkDotNet käsittelee kaiken tämän automaattisesti ja tuottaa tilastollisesti luotettavia tuloksia.

Asennus ja ensimmäinen suorituskykymittaus

Lisätkää NuGet-paketti, luokaa luokka, jossa on [Benchmark]-menetelmiä, ja suorittakaa se komennolla BenchmarkRunner.Run. Suorittakaa suorituskykymittaukset aina Release-tilassa.

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

Tulostaulukon lukeminen

BenchmarkDotNet tulostaa tulostaulukon, jossa ovat keskimääräinen aika, virhe, keskihajonta, suhde ja muistin varaus. Näin tulkitsette jokaisen sarakkeen.

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

Parametrisoidut suorituskykymittaukset

Käyttäkää [Params]-attribuuttia suorituskykymittauksen suorittamiseen useilla syöteko'oilla. BenchmarkDotNet suorittaa kaikki yhdistelmät ja luo jokaiselle parametriarvolle oman rivin.

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

Valmistelu ja siivous

Käyttäkää elinkaarimääritteitä kertaluonteisen valmistelun tai iteraatiokohtaisen valmistelun ja siivouksen suorittamiseen. Näin valmistelun kustannus jää mitatun suorituskykymittausajan ulkopuolelle.

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

Muistidiagnostiikka

[MemoryDiagnoser] raportoi hallitun keon varaukset operaatiota kohti. Yhdistäkää se [GcForce]-asetuksiin roskienkeruun toiminnan hallitsemiseksi suorituskykymittausten aikana.

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

Diagnosoijat: ETW ja disassembly

BenchmarkDotNet tukee useita diagnosoijia. DisassemblyDiagnoser näyttää todellisen natiivin assembly-koodin, joten se sopii erinomaisesti JIT-optimointien tarkistamiseen.

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

Job-määritykset

Jobit hallitsevat suorituskykymittauksen suoritusympäristöä: lämmittelyiteraatioiden määrää, kutsujen määrää iteraatiota kohden, .NET-suoritusympäristön versiota ja JIT-asetuksia. Käyttäkää esimääritettyjä tai mukautettuja jobeja.

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

Tulosten vieminen

BenchmarkDotNet vie tuloksia useissa muodoissa: Markdown-muodossa (GitHub PR:iä varten), CSV-muodossa (taulukkolaskentaa varten), JSON-muodossa ja HTML-kaavioina. Vientiasetukset määritetään job-määrityksissä.

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)

Tosielämän esimerkki: optimointia ennen ja sen jälkeen

BenchmarkDotNetin käyttäminen suorituskykyoptimoinnin vahvistamiseen vertaamalla naiivia, poolattua ja Span-pohjaista lähestymistapaa.

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

Pikatarkistus

Miksi BenchmarkDotNet-suorituskykymittaukset on suoritettava komennolla dotnet run -c Release?

Kertaus: suorituskyvyn mittaaminen BenchmarkDotNetillä

Keskeiset opit:

  • Asenna NuGetin kautta, merkitse menetelmät [Benchmark]-attribuutilla ja suorita komennolla dotnet run -c Release
  • [MemoryDiagnoser]: lisää varauksia sisältävän sarakkeen; [RankColumn]: järjestää tulokset nopeuden mukaan
  • [Params]: suorita mittaukset useilla syöteko'oilla algoritmisen vaativuuden selvittämiseksi
  • [GlobalSetup] / [GlobalCleanup]: kertaluonteinen valmistelu mitatun ajan ulkopuolella
  • [DisassemblyDiagnoser]: tarkastele tuotettua natiivikoodia JIT-optimointien vahvistamiseksi
  • Käyttäkää jobeja vertaillaksenne .NET 9:n JIT:iä, Native AOT:tä ja erilaisia iteraatiomääriä
Aloita maksutta

Opi C# tekoälytuutorin avulla — ilmaiseksi

Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.

Kurssit
93
Oppitunnit
346

Usein kysytyt kysymykset

Onko oppitunti ”Benchmarking BenchmarkDotNetillä” ilmainen?

Kyllä – oppitunnin ”Benchmarking BenchmarkDotNetillä” koko tekstin voi lukea täällä verkossa ilmaiseksi. Jos haluat harjoitella interaktiivisesti sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla sekä avata koko C# Academy-kurssin, päivitä CoddyKit PROhon. C# Academy-kurssilla on yhteensä 4 oppituntia.

Mitä opin oppitunnilla ”Benchmarking BenchmarkDotNetillä”?

Kirjoittakaa tarkkoja mikrovertailuja BenchmarkDotNetillä, analysoikaa läpimenoa ja allokointeja sekä välttäkää yleiset sudenkuopat. Harjoittelet C# Academy-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.

Tarvitsenko kokemusta aloittaakseni C# Academy-opiskelun?

Aiempi kokemus ei ole tarpeen. CoddyKitin C# Academy-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 4/4.

Kuinka kauan ”Benchmarking BenchmarkDotNetillä”-oppitunnin suorittaminen kestää?

Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.

Voinko kirjoittaa ja suorittaa koodia tällä C# Academy-oppitunnilla?

Kyllä. Jokainen C# Academy-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.

Kaikki tämän kurssin oppitunnit

  1. Native AOT -käännös
  2. Trimmaus ja reflektion rajoitukset
  3. ReadyToRun ja tasokohtainen käännös
  4. Benchmarking BenchmarkDotNetillä
← Takaisin: C# Academy