تحليل أداء كود C++: إيجاد نقاط الاختناق عمليًا

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مقدمة: لماذا تحليل الأداء ليس ترفًا بل ضرورة

\n

كل مطور C++ واجه موقفًا يعمل فيه البرنامج ولكنه بطيء. التخمينات البديهية حول أي جزء من الكود "يُبطئ" غالبًا ما تكون خاطئة. بدون بيانات دقيقة، فإنك تخاطر بتحسين ما يعمل بسرعة بالفعل، متجاهلاً "الاختناقات" الحقيقية. تحليل الأداء هو الطريقة العلمية الوحيدة لقياس الأداء وإيجاد نقاط الاختناق.

\n

في هذه المقالة، سنستعرض ثلاث أدوات قوية لتحليل أداء كود C++: gprof (إحصائي)، Perf (عتادي)، وValgrind/Callgrind (محاكاة). ستتعلم كيفية استخدامها عمليًا، وتفسير النتائج، وإيجاد الأجزاء الإشكالية بسرعة.

\n\n

1. تحليل الأداء الإحصائي باستخدام gprof: بداية سريعة

\n

gprof هو محلل أداء GNU كلاسيكي يعمل بناءً على أخذ عينات إحصائية. لا يتطلب صلاحيات خاصة وهو مثالي للتحليل الأولي.

\n\n

كيفية تجهيز الكود

\n

لكي يعمل gprof، يجب ترجمة البرنامج باستخدام العلامات -pg و-g:

\n
g++ -pg -g main.cpp -o my_program\n./my_program
\n

بعد تنفيذ البرنامج، سيظهر ملف gmon.out في المجلد الحالي. للحصول على تقرير قابل للقراءة، نفذ:

\n
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\n

تفسير النتائج

\n

في التقرير، ابحث عن عمود % time - فهو يوضح نسبة الوقت المستغرق في الدالة. الدوال ذات النسبة العالية هي مرشحاتك للتحسين.

\n\n

2. تحليل الأداء العتادي باستخدام Perf: دقة على مستوى النبضة

\n

Perf هي أداة حديثة لنظام Linux تستخدم عدادات CPU العتادية. توفر تكاليف إضافية ضئيلة وتسمح بقياس ليس فقط الوقت، بل أيضًا أخطاء التخزين المؤقت، وأخطاء التفرع، ومقاييس أخرى.

\n\n

التثبيت والاستخدام الأساسي

\n
sudo apt install linux-tools-common linux-tools-generic  # Ubuntu/Debian\nperf stat ./my_program   # إحصائيات عامة\nperf record ./my_program # تسجيل الملف الشخصي\nperf report              # عرض التقرير
\n\n

مثال: إيجاد أخطاء التخزين المؤقت

\n

لنفترض أن لدينا دالة تعالج مصفوفة صفًا تلو الآخر:

\n
const 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. السبب

المدونات

توصيات الكتب