مقدمة: لماذا يهم تحليل الأداء
كل مطور C# واجه موقفاً حيث يعمل التطبيق ببطء، لكن من غير الواضح السبب. التخمينات البديهية غالباً ما تكون خاطئة: الكود الذي يبدو بطيئاً قد يكون سريعاً، والتحسينات "الواضحة" قد تكون عديمة الفائدة. هنا يأتي دور تحليل الأداء — تحليل منهجي لأداء الكود لتحديد الاختناقات.
تحليل الأداء في .NET ليس مجرد قياس وقت التنفيذ. إنه مجموعة كاملة من التقنيات: من Stopwatch البسيط إلى أدوات متقدمة مثل BenchmarkDotNet وPerfView. بدون تحليل الأداء، تخاطر بتحسين الأشياء الخاطئة، وقضاء ساعات في تغييرات غير مفيدة.
في هذه المقالة، سنقوم بتحليل الطرق العملية لتحليل أداء كود C#، ونتعلم كيفية العثور على الاختناقات الحقيقية، ونفهم أي الأدوات يجب استخدامها في كل حالة محددة.
1. تحليل الأداء الأساسي باستخدام Stopwatch
أبسط طريقة لقياس وقت التنفيذ هي استخدام فئة System.Diagnostics.Stopwatch. إنها مناسبة للفحوصات السريعة والسيناريوهات البسيطة، لكن لها حدود: تكلفة إضافية عالية (نانو ثانية) وعدم وجود إحصائيات.
using System.Diagnostics;
public class SimpleProfiler{ public static void Measure() { var sw = Stopwatch.StartNew(); // Code under test var list = Enumerable.Range(0, 10_000_000).ToList(); sw.Stop(); Console.WriteLine($"Time: {sw.ElapsedMilliseconds} ms"); }}متى تستخدم: للتقدير التقريبي، عندما تحتاج إلى إجابة سريعة دون تثبيت حزم إضافية. غير مناسبة للقياسات الدقيقة (مثلاً عند مقارنة الخوارزميات).
العيوب: قياس واحد يمكن أن يكون عشوائياً (جامع القمامة، عمليات الخلفية). تحتاج إلى تشغيله عدة مرات وحساب متوسط النتائج — وهذا ما يفعله BenchmarkDotNet بالفعل.
2. BenchmarkDotNet: المعيار الذهبي للمقاييس
BenchmarkDotNet هي مكتبة لإنشاء مقاييس دقيقة وقابلة للتكرار. تقوم تلقائياً بتسخين JIT، وتتجنب تأثير جامع القمامة، وتوفر إحصائيات مفصلة.
التثبيت ومثال أساسي
// Installation: dotnet add package BenchmarkDotNetusing BenchmarkDotNet.Attributes;using BenchmarkDotNet.Running;
public class StringBenchmark{ private string _text = "Hello, World!"; [Benchmark] public bool ContainsWithOrdinal() => _text.Contains("World", StringComparison.Ordinal); [Benchmark] public bool ContainsWithInvariant() => _text.Contains("World", StringComparison.InvariantCulture);}
class Program{ static void Main() => BenchmarkRunner.Run<StringBenchmark>();}ستظهر النتيجة أن Ordinal أسرع مرتين إلى ثلاث مرات. بدون مقياس، لم تكن لتلاحظ هذا.
ميزات متقدمة
- Params: اختبار بأحجام بيانات مختلفة.
- MemoryDiagnoser: يُظهر تخصيصات الذاكرة.
- Baseline: مقارنة الأداء مقابل مرجع.
[MemoryDiagnoser]public class ListBenchmark{ [Params(100, 1000, 10000)] public int N; private List<int> _list; [GlobalSetup] public void Setup() => _list = Enumerable.Range(0, N).ToList(); [Benchmark(Baseline = true)] public int ForLoop() { int sum = 0; for (int i = 0; i < _list.Count; i++) sum += _list[i]; return sum; } [Benchmark] public int ForeachLoop() { int sum = 0; foreach (var item in _list) sum += item; return sum; }}3. تحليل الذاكرة وجامع القمامة
تسربات الذاكرة وعمليات جمع القمامة المتكررة هي سبب شائع للتباطؤ في .NET. للتحليل، استخدم: