قياس الأداء باستخدام BenchmarkDotNet
اكتبوا اختبارات أداء دقيقة للعمليات الصغيرة باستخدام BenchmarkDotNet، وحلّلوا معدل النقل والتخصيصات، وتجنّبوا الأخطاء الشائعة.
قياس الأداء باستخدام BenchmarkDotNet درس مجاني في C# Academy على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في C# Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة C# Academy 4 دروس في المجموع.
لماذا BenchmarkDotNet؟
اختبار الأداء المصغر أصعب مما يبدو: إذ تؤدي تهيئة JIT، وتوقفات GC، وذاكرات التخزين المؤقت للمعالج، وجدولة نظام التشغيل إلى إدخال ضوضاء في النتائج. ويتولى 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();
}إعدادات Jobs
تتحكم Jobs في بيئة تنفيذ اختبار الأداء، بما في ذلك عدد تكرارات التهيئة، وعدد الاستدعاءات لكل تكرار، وإصدار بيئة تشغيل .NET، وإعدادات JIT. استخدموا Jobs مُعرّفة مسبقًا أو مخصصة.
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. وتُضبط خيارات التصدير في إعدادات Job.
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- استخدموا Jobs لمقارنة JIT في .NET 9 مع Native AOT، أو لمقارنة أعداد مختلفة من التكرارات
الأسئلة الشائعة
هل درس «قياس الأداء باستخدام BenchmarkDotNet» مجاني؟
نعم — نص درس «قياس الأداء باستخدام BenchmarkDotNet» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة C# Academy، انتقل إلى CoddyKit PRO. تتضمن دورة C# Academy 4 دروس في المجموع.
ماذا ستتعلم في «قياس الأداء باستخدام BenchmarkDotNet»؟
اكتبوا اختبارات أداء دقيقة للعمليات الصغيرة باستخدام BenchmarkDotNet، وحلّلوا معدل النقل والتخصيصات، وتجنّبوا الأخطاء الشائعة. تتمرن على C# Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ C# Academy؟
لا تُشترط خبرة سابقة. C# Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.
كم من الوقت يستغرق درس «قياس الأداء باستخدام BenchmarkDotNet»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس C# Academy هذا؟
نعم. كل درس في C# Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- الترجمة إلى Native AOT
- القصّ وقيود Reflection
- ReadyToRun والترجمة متعددة المستويات
- قياس الأداء باستخدام BenchmarkDotNet