What Irregularity Costs: CUDA C++, Rust, and Triton on a Hash-Blocked GPU Workload
تُظهر هذه الورقة أنه بينما تؤدي لغات CUDA C++ وRust وTriton بشكل متماثل في أعباء عمل وحدة معالجة الرسومات المنتظمة، فإن كفاءتها تتباعد بشكل جذري في مهام التجزئة بالهاش (hash-blocked) غير المنتظمة بسبب قيود خاصة بكل لغة في التعبير عن العمليات الذرية (atomic operations) وحدود الحلقات التكرارية، حيث تعاني Rust من مشكلات في تماسك الذاكرة المخبئية (cache coherence)، بينما تعاني Triton من العمليات الذرية غير القابلة للقناع (unmaskable atomics) وقيود الحلقات التكرارية عند وقت التجميع.
البحث الأصلي مرخَّص بموجب CC BY 4.0 (http://creativecommons.org/licenses/by/4.0/). هذا شرح مولَّده بالذكاء الاصطناعي للبحث أدناه. لم يكتبه المؤلفون ولم يصادقوا عليه. وللتحقق من الدقة التقنية، يرجى الرجوع إلى البحث الأصلي. اقرأ إخلاء المسؤولية الكامل
المسرح واللاعبون
تخيل أنك تحاول بناء نموذج ثلاثي الأبعاد لغرفة باستخدام كاميرا تلتقط آلاف الصور. لجعل هذا الأمر ممكنًا، يحتاج حاسوبك إلى تنظيم كمية هائلة من البيانات حول كل جزء صغير من الفضاء في تلك الغرفة. هذا هو عالم برمجة وحدات معالجة الرسومات (GPU)، حيث تُعد "وحدات معالجة الرسومات" هي الرقائق الرسومية فائقة السرعة داخل الحواسيب، وهي أيضًا بارعة في إجراء ملايين العمليات الحسابية في وقت واحد.
عادةً، عندما يقارن الناس بين لغات البرمجة المختلفة لهذه الرقائق، فإنهم يختبرونها في مهام منظمة ومتوقعة للغاية، مثل ضرب شبكات ضخمة من الأرقام. يشبه ذلك اختبار سيارة سباق على طريق سريع مستقيم وخالٍ تمامًا؛ حيث كل مسار متماثل، وكل سائق يعرف بالضبط كم سيستغرق السباق. لكن العالم الحقيقي فوضوي. في تطبيقات مثل الواقع الافتراضي أو ملاحة الروبوتات، يتعين على الحاسوب التعامل مع بيانات فوضوية وغير متوقعة. الأمر يشبه إرسال نفس سيارة السباق تلك إلى شوارع مدينة مزدحمة ومتعرجة، حيث تحدث اختناقات مرورية بشكل عشوائي، ويضطر السائقون إلى التوقف والتحرك باستمرار. يسأل هذا البحث سؤالاً بسيطاً ولكن جوهرياً: عندما يصبح الطريق فوضوياً، هل تؤدي جميع لغات البرمجة نفس الأداء، أم أن بعضها سيعلق في الازدحام بينما يمر البعض الآخر بسرعة؟
المواجهة الكبرى للغات برمجة الـ GPU
في هذه الدراسة، أخذ الباحثون ثلاث طرق شائعة للتحدث مع هذه الرقائق فائقة القدرة — وهي CUDA C++ (المعيار التقليدي المكتوب يدوياً)، وRust (لغة حديثة معروفة بالأمان)، وTriton (أداة أحدث صُممت لجعل البرمجة أسهل) — ووضعوها للعمل في مهمة محددة للغاية وفوضوية: بناء خريطة ثلاثية الأبعاد لغرفة باستخدام "جدول هاش" (Hash Table).
فكر في "جدول الهاش" كأنه غرفة خزائن فوضوية ضخمة. لديك آلاف الأشخاص (نقاط البيانات) الذين يحاولون العثور على خزانة (مكان في الذاكرة) لتخزين أغراضهم. أحياناً تكون الخزانة التي يريدونها فارغة، فيأخذونها. ولكن غالباً ما تكون الخزانة محجوزة بالفعل، لذا يتعين عليهم التحقق من الخزانة التالية، والتي تليها، حتى يجدوا مكاناً مفتوحاً. في العالم المثالي، يجد الجميع الخزائن فوراً. أما في هذا العمل "غير المنتظم"، فيجد بعض الناس الخزائن على الفور، بينما يضطر آخرون للبحث لفترة طويلة، ويتصارع الجميع على نفس الخزائن القليلة في نفس الوقت.
قام الباحثون بتشغيل المهمة نفسها تماماً على اللغات الثلاث وقاسوا سرعة انتهائها. كانت النتائج صادمة: لقد تصرفت اللغات بشكل مختلف تماماً اعتماداً على نوع العمل.
الجزء "المنتظم": تعادل تام
أولاً، اختبروا الجزء "المنتظم" من المهمة، وهو ما يشبه المشي في ممر وطلاء كل جدار تراه. هذا الجزء يمكن التنبؤ به. في هذه المهمة، كانت اللغات الثلاث متطابقة تقريباً. سواء استخدمت CUDA التقليدية، أو Rust الحديثة، أو Triton سهلة الاستخدام، فقد انتهوا في وقت متقارب جداً. إذا نظرت فقط إلى هذه الاختبارات المنظمة والمتوقعة (وهو ما تفعله معظم الدراسات الأخرى)، فستعتقد أنه لا يهم أي لغة تختار.
الجزء "غير المنتظم": الانقسام الكبير
بعد ذلك، اختبروا الجزء "غير المنتظم": البحث الفوضوي في غرفة الخزائن. هنا تتغير القصة بشكل جذري.
- Rust مقابل CUDA C++: أدت لغة Rust أداءً يماثل تقريباً أداء CUDA C++ المكتوبة يدوياً. كانت أبطأ قليلاً فقط (حوالي 1% إلى 3% في بعض الحالات)، وهو ما يعتبر تعادلاً عملياً. أثبتت Rust قدرتها على التعامل مع حركة المرور الفوضوية وغير المتوقعة بنفس كفاءة اللغة المخضرمة.
- معاناة Triton: ومع ذلك، اصطدم Triton بحائط ضخم. في مهمة البحث الفوضوية، كان أبطأ بأكثر من 10 مرات من اللغتين الأخريين. وفي بعض الاختبارات الواقعية لعمليات مسح الغرف الفعلية، كان أبطأ بنحو 30 مرة.
لماذا علق Triton؟
لم يكتفِ الباحثون بالقول إن "Triton بطيء"؛ بل اكتشفوا بالضبط لماذا كان عالقاً، ولم يكن السبب هو كتابة الكود بشكل سيئ، بل بسبب طريقة بناء اللغة نفسها.
تخيل أن Triton معلم صارم يصر على أن يظل كل طالب في مقعده لفترة زمنية محددة، حتى لو أنهى عمله مبكراً. في غرفة الخزائن الفوضوية، يجد بعض "الخيوط" (Threads/الطلاب) خزانة في ثانية واحدة، بينما يستغرق آخرون عشر ثوانٍ.
- المشكلة: يجبر Triton الخيوط السريعة على الانتظار في حلقة (Loop) حتى ينتهي أبطأ خيط، رغم أنه ليس لديهم ما يفعلونه. إنه يشبه سباقاً يتعين فيه على الفائز أن يقف ساكناً وينتظر آخر شخص يعبر خط النهاية قبل أن يتمكن أي شخص من مغادرة المضمار.
- مشكلة "القناع" (Mask): علاوة على ذلك، يفتقر Triton إلى أداة محددة (تسمى "القناع") تسمح للخيوط السريعة بالتوقف عن العمل تماماً. بدلاً من ذلك، يتعين عليها الاستمرار في تشغيل مهمة وهمية، مما يهدر الطاقة ويسد النظام. وجد الباحثون أن هذا التصميم أجبر الحاسوب على القيام بقدر هائل من العمل غير المجدي، مما أدى إلى إبطاء كل شيء بمقدار 10 إلى 30 ضعفاً.
- خطر خفي: كانت هناك أيضاً مشكلة تتعية بالأمان. نظرًا لأن Triton يفرض حداً زمنياً ثابتاً للبحث، فإذا أصبحت غرفة الخزائن مزدحمة للغاية، فقد يتوقف البحث عن البحث. وهذا يعني أن الحاسوب يتخلص بصمت من أجزاء من الغرفة ثلاثية الأبعاد، مما يخلق ثقوباً غير مرئية في النموذج النهائي. وجد الباحثون أنه عند مستويات ازدحام معينة، يفقد Triton كتلًا كاملة من السطح، بينما تجدها اللغات الأخرى بشكل مثالي.
لماذا كانت Rust أبطأ قليلاً (الفخ غير المرئي)
كانت Rust قريبة جداً من الفوز، لكنها لم تكن بنفس سرعة كود CUDA المكتوب يدوياً. أمضى الباحثون وقتاً طويلاً في محاولة معرفة السبب، حيث فحصوا عدد التعليمات والذاكرة المستخدمة. وجدوا أن Rust كانت تقوم في الواقع بعمل أقل من CUDA، ومع ذلك كانت أبطأ.
الجاني كان فخاً مخفياً في كيفية تعامل Rust مع الأمان. تمتلك Rust ميزة تجعل قراءة البيانات المشتركة "آمنة" من خلال ضمان أن الجميع يرى نفس النسخة. ومع ذلك، في هذه الرقائق المحددة، فإن الطريقة "الآمنة" لقراءة البيانات تجبر الحاسوب على تخطي ذاكرة التخزين المؤقت (Cache) الأسرع والذهاب إلى ذاكرة أبطأ. إنه يشبه حارس أمن يصر على فحص كل طرد عند الباب الأمامي، رغم أن الطرود معروفة بالفعل بأنها آمنة. هذه الخطوة الإضافية أبطأت Rust بنسبة 20-30% تقريباً، لكنه كان ثمناً ضئيلاً مقارنة بتباطؤ Triton الهائل.
الخلاصة
الدرس الرئيسي من هذا البحث هو أنه لا يمكنك الحكم على لغة البرمجة فقط من خلال أدائها في المهام المنظمة والمتوقعة.
إذا اختبرت اللغات فقط على "الطريق السريع" المنتظم، فستبدو Rust وCUDA وTriton جميعها كأبطال. ولكن بمجرد إلقائهم في "حركة مرور المدينة" الفوضوية وغير المنتظمة لخرائط ثلاثية الأبعاد حقيقية، تنقسم النتائج بشكل حاد.
- Rust هي منافس قوي، حيث تظل سريعة تقريباً مثل أفضل كود مكتوب يدوياً.
- Triton، رغم كونه رائعاً للمهام المنتظمة، يعاني بشدة مع العمل الفوضوي وغير المتوقع، حيث يصبح أبطأ بعشرات المرات وقد يفقد البيانات دون أن يلاحظ أحد ذلك.
يخلص الباحثون إلى أنه بالنسبة للمهام التي تتضمن بيانات حقيقية فوضوية، فإن اختيار اللغة الخاطئة ليس مجرد إزعاج بسيط؛ بل يمكن أن يجعل برنامجك بطيئاً بشكل لا يُطاق أو يتسبب في فشله بصمت. كما وجدوا أن العديد من الدراسات السابقة فاتتها هذه النقطة لأنها اختبرت فقط الأجزاء "المنتظمة"، تاركة الأجزاء الخطيرة والفوضوية دون استكشاف.
غارق في أبحاث مجالك؟
تصلك نشرة يومية بأحدث الأبحاث المطابقة لكلماتك البحثية المفتاحية — مع ملخصات تقنية، بلغتك.