العودة
كود C++ فعّال: الحاسم هو ترتيب البيانات في الذاكرة
SiTech AI Team2 წთ. საკითხავი

كود C++ فعّال: الحاسم هو ترتيب البيانات في الذاكرة

يشرح المبرمج Adam Sawicki لماذا لا يكفي اختيار C++ لكتابة برنامج سريع: النتيجة يحددها ترتيب البيانات في الذاكرة، واستخدام الذاكرة المؤقتة، وكلفة تخصيص الذاكرة. نُشر المقال أول مرة بالبولندية في مجلة Programista عام 2013.

اختيار C++ وحده لا يجعل البرنامج سريعاً. في مقال “Writing Efficient C++ Code” يشرح المبرمج Adam Sawicki ما الذي يحدد إن كان الكود يستفيد فعلاً من العتاد: كيف تستقر البيانات في الذاكرة وكم مرة ينتظر المعالج الذاكرة المؤقتة. نُشر النص أول مرة بالبولندية في مجلة Programista عام 2013.

C++ وحدها لا تعني السرعة

C++ لغة عالية المستوى بما يكفي للتصميم الكائني وللحاويات الجاهزة، ومنخفضة المستوى بما يكفي كي لا يقف بين الكود ونظام التشغيل أي آلة افتراضية أو جامع نفايات: المبرمج هو من يحرر الذاكرة.

تصميم موجّه بالبيانات

يبدأ نهج Data-Oriented Design، الشائع بين مطوري الألعاب، من البيانات: من ترتيبها في الذاكرة ومن البنى التي تحفظها، ثم تُختار الخوارزميات لاحقاً. النهج لا يمنع الأصناف، لكنه يحذّر من كثرة الكائنات الصغيرة المتناثرة في الذاكرة والمرتبطة بالمؤشرات: مثل هذه التخطيطات تسبب إخفاقات متكررة في الذاكرة المؤقتة وتصعّب التوازي. أما المصفوفة العادية فتُعالج عنصراً عنصراً ويمكن تقسيم مداها على الخيوط.

هرمية الذاكرة تحدد الكلفة

على معالج بتردد 3 جيجاهرتز تستغرق العملية الحسابية البسيطة نحو 0,33 نانوثانية، أي دورة واحدة. القيمة في ذاكرة L1 المؤقتة تكلف نحو نانوثانية واحدة، وفي L2 نحو 4,7، وفي RAM نحو 83 نانوثانية، أي 250 دورة. تُنقل البيانات في سطور ذاكرة مؤقتة بعرض 64 بايت، لذا يجب أن تتجاور القيم المستخدمة معاً.

هرم كلفة العمليات، من الحساب إلى الإدخال والإخراج

لذلك تتفوق البنى المتصلة، كمصفوفة عادية أو std::vector، على القوائم المترابطة والأشجار. يرسم المقال “هرم الأداء”: الحساب أسرع ما يكون، والعمليات المتسامية كالجيب أو القسمة أبطأ بكثير، والوصول إلى RAM بعد إخفاق في الذاكرة المؤقتة يكلف مئات الدورات، والتخصيص الديناميكي مكلف، والإدخال والإخراج الأبطأ.

AOS وSOA وحدود المصرّف

يوضح نظام الجسيمات هذه المقايضة. يخزّن Array of Structures موضع كل جسيم وسرعته ولونه معاً؛ ويحفظ Structure of Arrays مصفوفة منفصلة لكل خاصية، فتبقى القيم المطلوبة في حلقة واحدة قريبة ولا تدخل الحقول غير المستخدمة الذاكرة المؤقتة.

لا يستطيع المصرّف إصلاح كل شيء. حين قد يشير المؤشر إلى عناصر المصفوفة، تُقرأ القيمة من جديد في كل تكرار، ويساعد نسخها إلى متغير محلي أو استخدام __restrict. أخرج std::string من الحلقة مع تفريغه في كل مرة فاختُصر اختبار من 0,36 إلى 0,27 ثانية، أي 25%، لأن النص يحفظ المخزن المخصص له.

SSiTech

SiTech — تطوير ويب مدعوم بالذكاء الاصطناعي

نبني مواقع سريعة وعصرية وندمج الذكاء الاصطناعي في سير عمل الشركات. لديك مشروع أو سؤال؟ يسعدنا مساعدتك.