مقدمة: لماذا تحليل الأداء ليس ترفًا بل ضرورة
\nكل مطور C++ واجه موقفًا يعمل فيه البرنامج ولكنه بطيء. التخمينات البديهية حول أي جزء من الكود "يُبطئ" غالبًا ما تكون خاطئة. بدون بيانات دقيقة، فإنك تخاطر بتحسين ما يعمل بسرعة بالفعل، متجاهلاً "الاختناقات" الحقيقية. تحليل الأداء هو الطريقة العلمية الوحيدة لقياس الأداء وإيجاد نقاط الاختناق.
\nفي هذه المقالة، سنستعرض ثلاث أدوات قوية لتحليل أداء كود C++: gprof (إحصائي)، Perf (عتادي)، وValgrind/Callgrind (محاكاة). ستتعلم كيفية استخدامها عمليًا، وتفسير النتائج، وإيجاد الأجزاء الإشكالية بسرعة.
1. تحليل الأداء الإحصائي باستخدام gprof: بداية سريعة
\ngprof هو محلل أداء GNU كلاسيكي يعمل بناءً على أخذ عينات إحصائية. لا يتطلب صلاحيات خاصة وهو مثالي للتحليل الأولي.
كيفية تجهيز الكود
\nلكي يعمل gprof، يجب ترجمة البرنامج باستخدام العلامات -pg و-g:
g++ -pg -g main.cpp -o my_program\n./my_program\nبعد تنفيذ البرنامج، سيظهر ملف gmon.out في المجلد الحالي. للحصول على تقرير قابل للقراءة، نفذ:
gprof my_program gmon.out > report.txt\n\nمثال: إيجاد خوارزمية بطيئة
\nلننظر إلى برنامج بسيط يقوم بفرز مصفوفة بطريقتين:
\n#include <iostream>\n#include <vector>\n#include <algorithm>\n#include <chrono>\n\nvoid bubble_sort(std::vector<int>& arr) {\n for (size_t i = 0; i < arr.size(); ++i)\n for (size_t j = 0; j < arr.size() - i - 1; ++j)\n if (arr[j] > arr[j+1])\n std::swap(arr[j], arr[j+1]);\n}\n\nvoid quick_sort_wrapper(std::vector<int>& arr) {\n std::sort(arr.begin(), arr.end());\n}\n\nint main() {\n std::vector<int> data(50000);\n std::generate(data.begin(), data.end(), std::rand);\n \n auto copy1 = data;\n auto copy2 = data;\n \n bubble_sort(copy1);\n quick_sort_wrapper(copy2);\n \n return 0;\n}\nقم بتشغيل تحليل الأداء. في تقرير gprof، سترى جدول الاستدعاءات. ستستغرق bubble_sort 95-99% من الوقت. هذه نقطة اختناق واضحة - استبدال فرز الفقاعات بـ std::sort سيعطي تحسنًا فوريًا.
تفسير النتائج
\nفي التقرير، ابحث عن عمود % time - فهو يوضح نسبة الوقت المستغرق في الدالة. الدوال ذات النسبة العالية هي مرشحاتك للتحسين.
\n\n2. تحليل الأداء العتادي باستخدام Perf: دقة على مستوى النبضة
\nPerf هي أداة حديثة لنظام Linux تستخدم عدادات CPU العتادية. توفر تكاليف إضافية ضئيلة وتسمح بقياس ليس فقط الوقت، بل أيضًا أخطاء التخزين المؤقت، وأخطاء التفرع، ومقاييس أخرى.
التثبيت والاستخدام الأساسي
\nsudo apt install linux-tools-common linux-tools-generic # Ubuntu/Debian\nperf stat ./my_program # إحصائيات عامة\nperf record ./my_program # تسجيل الملف الشخصي\nperf report # عرض التقرير\n\nمثال: إيجاد أخطاء التخزين المؤقت
\nلنفترض أن لدينا دالة تعالج مصفوفة صفًا تلو الآخر:
\nconst int N = 4096;\nint matrix[N][N];\n\nvoid process_rows() {\n for (int i = 0; i < N; ++i)\n for (int j = 0; j < N; ++j)\n matrix[i][j] *= 2;\n}\n\nvoid process_cols() {\n for (int j = 0; j < N; ++j)\n for (int i = 0; i < N; ++i)\n matrix[i][j] *= 2;\n}\nقم بتشغيل perf stat لكل دالة. ستلاحظ أن process_cols لديها عدد أكبر بكثير من cache-misses. السبب