C++: تحسين الأداء

آخر تحديث: 2026-08-26

في الدرس 48، تعلمنا عن ميزات C++17/20 الجديدة.

الآن، سنتعلم عن تحسين الأداء — جعل برامج C++ تعمل بشكل أسرع.

ميزة C++ هي الأداء، لكن استخدامها بشكل جيد ليس سهلاً.


1. نظرة عامة على تحسين الأداء

(1) 1.1 لماذا نحسّن؟

المبادئ:

  1. لا تُحسّن مبكرًا (Premature Optimization)
  2. قِس أولاً، ثم حسّن
  3. حسّن الخوارزمية أولاً، ثم الكود

(2) 1.2 اختناقات الأداء

الاختناق النسبة
الخوارزمية 70%
الوصول للذاكرة 20%
أخرى 10%

الاستنتاج: تحسين الخوارزمية أهم من تحسين الكود.



2. محاذاة الذاكرة

(1) 2.1 ما هي محاذاة الذاكرة؟

محاذاة الذاكرة تعني تخزين البيانات في عناوين ذاكرة هي مضاعفات لقيمة معينة (عادة قوة 2).

لماذا هي مهمة؟


(2) 2.2 مثال: تأثير محاذاة الذاكرة (الصعوبة ⭐⭐)

▶ مثال 1: عرض البرمجة كائنية التوجه (الصعوبة ⭐)

TEXT 📖 للعرض فقط
#include <iostream>

struct BadAlignment {
	char c; // 1 بايت
	int i; // 4 بايت (قد تُمحى إلى الإزاحة 4)
};

struct GoodAlignment {
	int i; // 4 بايت
	char c; // 1 بايت
};

int main() {
	std::cout << "Bad: " << sizeof(BadAlignment) << " bytes" << std::endl;
	std::cout << "Good: " << sizeof(GoodAlignment) << " bytes" << std::endl;
	
	return 0;
}

الخرج:

TEXT 📖 للعرض فقط
Bad: 8 bytes
Good: 8 bytes

نتيجة تشغيل محتملة:

TEXT 📖 للعرض فقط
Bad: 8 bytes
Good: 8 bytes

💡 نصيحة:



3. وداقة التخزين المؤقت

(1) 3.1 تخزين وحدة المعالجة المركزية المؤقت

تخزين وحدة المعالجة المركزية المؤقت أسرع بـ 100 مرة من الذاكرة الرئيسية.

مبادئ التحسين:

  1. مبدأ الموضعية: الوصول إلى ذاكرة متجاورة
  2. الوصول المتسلسل: أسرع من الوصول العشوائي
  3. تجنب أخطاء التخزين المؤقت: تقليل الوصول للذاكرة غير المتجاورة

(2) 3.2 مثال: الترتيب حسب الصف مقابل الترتيب حسب العمود (الصعوبة ⭐⭐⭐)

CPP
#include <iostream>
### ▶ مثال 2: استخدام حاويات STL (الصعوبة ⭐)

#include <vector>
#include <chrono>

int main() {
	const int N = 1000;
	std::vector<std::vector<int>> matrix(N, std::vector<int>(N));
	
	// الترتيب حسب الصف (وديق للتخزين المؤقت)
	auto start = std::chrono::high_resolution_clock::now();
	for (int i = 0; i < N; i++) {
		for (int j = 0; j < N; j++) {
			matrix[i][j] = 1;
		}
	}
	auto end = std::chrono::high_resolution_clock::now();
	auto row_time = std::chrono::duration<double>(end - start).count();
	
	// الترتيب حسب العمود (غير وديق للتخزين المؤقت)
	start = std::chrono::high_resolution_clock::now();
	for (int j = 0; j < N; j++) {
		for (int i = 0; i < N; i++) {
			matrix[i][j] = 1;
		}
	}
	end = std::chrono::high_resolution_clock::now();
	auto col_time = std::chrono::duration<double>(end - start).count();
	
	std::cout << "Row-major time: " << row_time << " seconds" << std::endl;
	std::cout << "Column-major time: " << col_time << " seconds" << std::endl;
	
	return 0;
}

الخرج:

TEXT 📖 للعرض فقط
Row-major time: 0.001 seconds
Column-major time: 0.003 seconds


4. تحسين المترجم

(1) 4.1 مستويات التحسين

المستوى العلامة الوصف
O0 لا شيء بدون تحسين (للتصحيح)
O1 -O1 تحسين أساسي
O2 -O2 موصى به (متوازن)
O3 -O3 تحسين عدواني
Os -Os تحسين للحجم

(2) 4.2 مثال: تأثير تحسين المترجم (الصعوبة ⭐)

TEXT 📖 للعرض فقط
#include <iostream>

int main() {
	int sum = 0;
	for (int i = 0; i < 1000000; i++) {
		sum += i;
	}
	std::cout << sum << std::endl;
	
	return 0;
}

الخرج:

TEXT 📖 للعرض فقط
499999500000

التجميع:

BASH
g++ -O0 main.cpp # بطيء
g++ -O2 main.cpp # سريع (المترجم قد يحسب النتيجة مباشرة)


5. أدوات تحليل الأداء

(1) 5.1 الأدوات الشائعة

الأداة المنصة الوصف
gprof Linux محلل أداء GCC
perf Linux أداة تحليل أداء Linux
Valgrind متعدد المنصات تحليل الذاكرة
Visual Studio Profiler ويندوز مدمج في VS

(2) 5.2 مثال: قياس الوقت بـ chrono (الصعوبة ⭐)

CPP
#include <iostream>
#include <chrono>

int main() {
	auto start = std::chrono::high_resolution_clock::now();
	
	// الكود المراد قياسه
	long sum = 0;
	for (int i = 0; i < 100000000; i++) {
		sum += i;
	}
	
	auto end = std::chrono::high_resolution_clock::now();
	auto duration = std::chrono::duration<double>(end - start).count();
	
	std::cout << "Time elapsed: " << duration << " seconds" << std::endl;
	
	return 0;
}

الخرج:

TEXT 📖 للعرض فقط
Time elapsed: 0.05 seconds


6. ملخص تقنيات التحسين

(1) 6.1 على مستوى الكود

التقنية الوصف
استخدم دلالات النقل تقليل النسخ
استخدم emplace_back تجنب الكائنات المؤقتة
استخدم reserve تقليل إعادة تخصيص vector
استخدم unordered_map جدول تجزئة، بحث O(1)

(2) 6.2 على مستوى الخوارزمية

التقنية الوصف
اختر بنية البيانات المناسبة vector مقابل list مقابل map
استخدم خوارزميات مناسبة sort مقابل partial_sort
تجنب النسخ غير الضروري استخدم المراجع، النقل

▶ مثال 3: مقارنة أداء التمرير بالقيمة مقابل التمرير بالمرجع (الصعوبة ⭐)

CPP
#include <iostream>
#include <vector>
#include <chrono>

struct BigData {
    std::vector<int> data;
    BigData() : data(10000, 0) {}
};

// التمرير بالقيمة (نسخ)
void processByValue(BigData d) {
    (void)d;
}

// التمرير بالمرجع (بدون نسخ)
void processByRef(const BigData& d) {
    (void)d;
}

int main() {
    BigData big;

    auto start = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < 10000; i++) {
        processByValue(big);
    }
    auto end = std::chrono::high_resolution_clock::now();
    std::cout << "Pass by value time: " << std::chrono::duration<double>(end - start).count() << " seconds" << std::endl;

    start = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < 10000; i++) {
        processByRef(big);
    }
    end = std::chrono::high_resolution_clock::now();
    std::cout << "Pass by reference time: " << std::chrono::duration<double>(end - start).count() << " seconds" << std::endl;

    return 0;
}
▶ جرّب الكود

الخرج:

TEXT 📖 للعرض فقط
Pass by value time: 0.15 seconds
Pass by reference time: 0.001 seconds

❓ أسئلة شائعة

س هل التحسين فعّال دائمًا؟
ج ليس بالضرورة. قِس أولاً، اعثر على الاختناق، ثم حسّن.

س هل C++ أسرع دائمًا من Python؟
ج ليس بالضرورة. إذا كانت الخوارزمية هي نفسها، فـ C++ عادة أسرع. لكن بخوارزمية سيئة، يمكن أن يكون C++ بطيئًا أيضًا.

س كيف أحدد مكان الاختناق؟
ج استخدم أداة تحليل أداء.

📖 ملخص

النقطة الرئيسية الملخص
محاذاة الذاكرة تقليل الحشو
وداقة التخزين المؤقت وصول متسلسل، موضعية
تحسين المترجم -O2 موصى به
تحليل الأداء قِس أولاً، ثم حسّن
تقنيات التحسين دلالات النقل، emplace_back

📝 تمارين

  1. أساسي (الصعوبة ⭐): خزّن 100,000 عدد صحيح باستخدام كل من vector وlist، وقارن فارق الوقت للإدراج في النهاية والوصول العشوائي.

  2. متوسط (الصعوبة ⭐⭐): اختبر فرق الأداء بين التمرير بالقيمة والتمرير بالمرجع. اكتب دالة تعالج بنية كبيرة (تحتوي vector<int>(10000))، مع قياس الوقت لكل من التمرير بالقيمة والتمرير بالمرجع الثابت.

  3. متقدم (الصعوبة ⭐⭐⭐): استخدم توقيت عالي الدقة std::chrono لمقارنة فروق الأداء بين حلقة for، وfor_each من STL، وfor المدعوم بنطاق عند الاجتياز. حلل النتائج.



الدرس التالي: اختبار الوحدات (#50)

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%