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

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

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

ابدأ الدورة

ملف تعريف كود C++: إيجاد الاختناقات



تطبيقات C++ الحديثة أصبحت أكثر تعقيداً، وحتى المطورون ذوو الخبرة غالباً ما يواجهون موقفاً يكون فيه البرنامج أبطأ من المتوقع. بدلاً من التخمين حول أي جزء من الكود يبطئ التنفيذ، من الضروري استخدام ملف تعريف (profiling). ملف التعريف هو عملية جمع بيانات حول أداء البرنامج أثناء تنفيذه، والتي تسمح بتحديد "الاختناقات" (bottlenecks) بدقة واتخاذ قرارات مدروسة بشأن التحسين.



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



لماذا نحتاج إلى ملف تعريف كود C++؟



بدون ملف تعريف، يتحول التحسين إلى تخمين. قد تقضي ساعات في "تحسين" جزء من الكود لا يشغل في الواقع سوى 1% من وقت التنفيذ. ملف التعريف يوفر بيانات موضوعية:



  • تحديد النقاط الساخنة (hot spots) — الدوال أو الأسطر التي يقضي فيها البرنامج معظم الوقت.
  • تحليل تكرار الاستدعاءات — كم مرة يتم استدعاء كل دالة.
  • اكتشاف الخوارزميات غير الفعالة — على سبيل المثال، تعقيد تربيعي حيث كان متوقعاً تعقيداً خطياً.
  • كشف مشاكل التخزين المؤقت والذاكرة — مهم بشكل خاص للأنظمة عالية التحميل.


الأداة 1: gprof — ملف التعريف الكلاسيكي من GNU



gprof هي واحدة من أقدم وأسهل أدوات ملفات التعريف في الاستخدام. تعمل على مرحلتين: جمع الإحصائيات أثناء تنفيذ البرنامج، ثم تحليل البيانات المجمعة لاحقاً.



كيفية استخدام gprof



للبدء، يجب ترجمة البرنامج باستخدام العلم -pg:



g++ -pg -O2 myprogram.cpp -o myprogram


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



gprof myprogram gmon.out > analysis.txt


مثال للتحليل



لننظر إلى برنامج بسيط يقوم بترتيب مصفوفة وإجراء بحث:



#include <algorithm>#include <vector>#include <chrono>#include <iostream>

void slowSearch(const std::vector<int>& data, int target) { for (size_t i = 0; i < data.size(); ++i) { if (data[i] == target) { // تم العثور } }}

int main() { std::vector<int> vec(100000); for (int i = 0; i < 100000; ++i) vec[i] = rand() % 100000; std::sort(vec.begin(), vec.end()); slowSearch(vec, 50000); return 0;}


بعد ملف التعريف، سيظهر gprof أن الدالة slowSearch تستهلك وقتاً كبيراً بسبب البحث الخطي. الحل هو استبدالها بالبحث الثنائي (std::binary_search).



مزايا gprof: سهولة الاستخدام، مدمج في GCC.

عيوب: لا يدعم تعدد الخيوط، يتطلب إعادة الترجمة، قد يشوه النتائج بسبب التكاليف الإضافية.



الأداة 2: Valgrind (Callgrind) — تحليل تفصيلي



Valgrind مع أداة Callgrind يوفر معلومات أكثر تفصيلاً بكثير، بما في ذلك محاكاة التخزين المؤقت وعد التعليمات. هذا مفيد بشكل خاص لإيجاد التحسينات الدقيقة.



تشغيل Callgrind



لملف التعريف، يكفي تنفيذ:



valgrind --tool=callgrind ./myprogram


بعد الانتهاء، سيظهر ملف باسم callgrind.out.PID. لتصور النتائج، استخدم الأداة المساعدة kcachegrind:



kcachegrind callgrind.out.12345


مثال: اكتشاف التخصيصات الزائدة



لننظر إلى كود يقوم بإنشاء

المدونات

توصيات الكتب