0Pricing
C# Academy · บทเรียน

การวัดประสิทธิภาพด้วย BenchmarkDotNet

เขียนการวัดประสิทธิภาพระดับย่อยที่แม่นยำด้วย BenchmarkDotNet วิเคราะห์อัตราการประมวลผลและการจัดสรรหน่วยความจำ พร้อมหลีกเลี่ยงข้อผิดพลาดที่พบบ่อย

การวัดประสิทธิภาพด้วย BenchmarkDotNet เป็นบทเรียน C# Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน C# Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส C# Academy มีบทเรียนทั้งหมด 4 บทเรียน

เหตุใดจึงต้องใช้ BenchmarkDotNet

การวัดประสิทธิภาพระดับย่อยทำได้ยากกว่าที่คิด: การอุ่นเครื่องของ JIT การหยุดชั่วคราวของ GC แคชของ CPU และการจัดตารางเวลาของระบบปฏิบัติการล้วนทำให้เกิดสัญญาณรบกวน BenchmarkDotNet จัดการทั้งหมดนี้โดยอัตโนมัติและสร้างผลลัพธ์ที่เชื่อถือได้ทางสถิติ

การติดตั้งและการวัดประสิทธิภาพครั้งแรก

เพิ่มแพ็กเกจ NuGet สร้างคลาสที่มีเมธอด [Benchmark] และเรียกใช้ด้วย BenchmarkRunner.Run โปรดเรียกใช้การวัดประสิทธิภาพในโหมด Release เสมอ

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

การอ่านตารางผลลัพธ์

BenchmarkDotNet จะแสดงตารางผลลัพธ์ที่มีเวลาเฉลี่ย ค่าคลาดเคลื่อน ส่วนเบี่ยงเบนมาตรฐาน อัตราส่วน และการจัดสรรหน่วยความจำ ต่อไปนี้คือวิธีตีความแต่ละคอลัมน์

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

การวัดประสิทธิภาพแบบมีพารามิเตอร์

ใช้ [Params] เพื่อเรียกใช้การวัดประสิทธิภาพกับขนาดอินพุตหลายค่า BenchmarkDotNet จะเรียกใช้ทุกชุดผสมและสร้างแถวแยกสำหรับค่าพารามิเตอร์แต่ละค่า

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

การเตรียมและการล้างข้อมูล

ใช้แอตทริบิวต์วงจรชีวิตเพื่อเรียกใช้การเตรียมข้อมูลครั้งเดียว หรือการเตรียมและล้างข้อมูลในแต่ละรอบ การทำเช่นนี้ช่วยไม่นับต้นทุนการเตรียมข้อมูลรวมอยู่ในเวลาที่วัด

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

การวินิจฉัยหน่วยความจำ

[MemoryDiagnoser] รายงานการจัดสรรบนฮีปที่มีการจัดการต่อการดำเนินการ ใช้ร่วมกับตัวเลือก [GcForce] เพื่อควบคุมพฤติกรรมของ GC ระหว่างการวัดประสิทธิภาพ

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

ตัววินิจฉัย: ETW และการถอดแยกโค้ด

BenchmarkDotNet รองรับตัววินิจฉัยหลายแบบ DisassemblyDiagnoser จะแสดงผลลัพธ์แอสเซมบลีเนทีฟจริง เหมาะอย่างยิ่งสำหรับตรวจสอบการปรับให้เหมาะสมของ JIT

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

การกำหนดค่างาน

งานจะควบคุมสภาพแวดล้อมในการเรียกใช้การวัดประสิทธิภาพ — จำนวนรอบอุ่นเครื่อง จำนวนครั้งที่เรียกใช้ต่อรอบ เวอร์ชันรันไทม์ของ .NET และการตั้งค่า JIT คุณสามารถใช้งานที่กำหนดไว้ล่วงหน้าหรือสร้างงานแบบกำหนดเองได้

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

การส่งออกผลลัพธ์

BenchmarkDotNet ส่งออกผลลัพธ์ได้หลายรูปแบบ — Markdown (สำหรับคำขอเปลี่ยนแปลงบน GitHub), CSV (สำหรับสเปรดชีต), JSON และแผนภูมิ HTML ตัวเลือกการส่งออกจะกำหนดไว้ในการกำหนดค่างาน

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)

การใช้งานจริง: การปรับให้เหมาะสมก่อนและหลัง

ใช้ BenchmarkDotNet เพื่อตรวจสอบการปรับปรุงประสิทธิภาพ — เปรียบเทียบแนวทางแบบตรงไปตรงมา แบบใช้พูล และแบบอิง Span

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

ตรวจสอบความเข้าใจ

เหตุใดจึงต้องเรียกใช้การวัดประสิทธิภาพของ BenchmarkDotNet ด้วย dotnet run -c Release

สรุป: การวัดประสิทธิภาพด้วย BenchmarkDotNet

ประเด็นสำคัญ:

  • ติดตั้งผ่าน NuGet กำกับเมธอดด้วย [Benchmark] และเรียกใช้ด้วย dotnet run -c Release
  • [MemoryDiagnoser]: เพิ่มคอลัมน์การจัดสรรหน่วยความจำ [RankColumn]: จัดอันดับตามความเร็ว
  • [Params]: เรียกใช้กับขนาดอินพุตหลายค่าเพื่อค้นหาความซับซ้อนของอัลกอริทึม
  • [GlobalSetup] / [GlobalCleanup]: การเตรียมและล้างข้อมูลครั้งเดียวนอกเวลาที่วัด
  • [DisassemblyDiagnoser]: ตรวจสอบ ASM เนทีฟที่สร้างขึ้นเพื่อยืนยันการปรับให้เหมาะสมของ JIT
  • ใช้การกำหนดค่างานเพื่อเปรียบเทียบ JIT ของ .NET 9 กับ Native AOT และเปรียบเทียบจำนวนรอบที่แตกต่างกัน

คำถามที่พบบ่อย

บทเรียน “การวัดประสิทธิภาพด้วย BenchmarkDotNet” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “การวัดประสิทธิภาพด้วย BenchmarkDotNet” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส C# Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส C# Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “การวัดประสิทธิภาพด้วย BenchmarkDotNet”

เขียนการวัดประสิทธิภาพระดับย่อยที่แม่นยำด้วย BenchmarkDotNet วิเคราะห์อัตราการประมวลผลและการจัดสรรหน่วยความจำ พร้อมหลีกเลี่ยงข้อผิดพลาดที่พบบ่อย คุณปฏิบัติ C# Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน C# Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน C# Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน

บทเรียน “การวัดประสิทธิภาพด้วย BenchmarkDotNet” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน C# Academy นี้ได้ไหม

ได้ บทเรียน C# Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. การคอมไพล์ Native AOT
  2. ข้อจำกัดของการตัดโค้ดและ Reflection
  3. ReadyToRun และการคอมไพล์แบบแบ่งระดับ
  4. การวัดประสิทธิภาพด้วย BenchmarkDotNet
← กลับไปที่ C# Academy