ملخص
سيوضح لك هذا الدليل كيفية استخدام TensorFlow Profiler مع TensorBoard للحصول على نظرة ثاقبة والحصول على أقصى أداء من وحدات معالجة الرسومات الخاصة بك، وتصحيح الأخطاء عندما تكون واحدة أو أكثر من وحدات معالجة الرسومات الخاصة بك غير مستغلة بشكل كافٍ.
إذا كنت جديدًا في ملف التعريف:
- ابدأ باستخدام TensorFlow Profiler: دفتر أداء نموذج الملف الشخصي مع مثال Keras و TensorBoard .
- تعرف على أدوات وأساليب ملفات التعريف المختلفة المتاحة لتحسين أداء TensorFlow على المضيف (CPU) من خلال تحسين أداء TensorFlow باستخدام دليل Profiler .
ضع في اعتبارك أن تفريغ العمليات الحسابية إلى وحدة معالجة الرسومات قد لا يكون مفيدًا دائمًا، خاصة بالنسبة للنماذج الصغيرة. يمكن أن يكون هناك النفقات العامة بسبب:
- نقل البيانات بين المضيف (CPU) والجهاز (GPU)؛ و
- نظرًا لوقت الاستجابة الذي يحدث عندما يقوم المضيف بتشغيل نواة GPU.
سير عمل تحسين الأداء
يوضح هذا الدليل كيفية تصحيح مشكلات الأداء بدءًا من وحدة معالجة رسومات واحدة، ثم الانتقال إلى مضيف واحد مزود بوحدات معالجة رسومات متعددة.
يوصى بتصحيح مشكلات الأداء بالترتيب التالي:
- تحسين الأداء وتصحيحه على وحدة معالجة رسومات واحدة:
- تحقق مما إذا كان خط أنابيب الإدخال يمثل عنق الزجاجة.
- تصحيح أداء وحدة معالجة الرسومات (GPU) واحدة.
- تمكين الدقة المختلطة (باستخدام
fp16
(float16)) وتمكين XLA اختياريًا.
- قم بتحسين وتصحيح الأداء على المضيف الفردي متعدد وحدات معالجة الرسومات.
على سبيل المثال، إذا كنت تستخدم إستراتيجية توزيع TensorFlow لتدريب نموذج على مضيف واحد باستخدام وحدات معالجة رسومات متعددة ولاحظت استخدام وحدة معالجة الرسومات دون المستوى الأمثل، فيجب عليك أولاً تحسين وتصحيح الأداء لوحدة معالجة رسومات واحدة قبل تصحيح أخطاء نظام وحدات معالجة الرسومات المتعددة.
كخط أساسي للحصول على كود الأداء على وحدات معالجة الرسومات، يفترض هذا الدليل أنك تستخدم tf.function
بالفعل. سوف تستخدم واجهات برمجة تطبيقات Keras Model.compile
و Model.fit
tf.function
تلقائيًا تحت الغطاء. عند كتابة حلقة تدريب مخصصة باستخدام tf.GradientTape
، راجع الأداء الأفضل مع وظيفة tf. للتعرف على كيفية تمكين tf.function
.
تناقش الأقسام التالية الأساليب المقترحة لكل من السيناريوهات المذكورة أعلاه للمساعدة في تحديد اختناقات الأداء وإصلاحها.
1. تحسين الأداء على وحدة معالجة رسومات واحدة
في الحالة المثالية، يجب أن يتمتع برنامجك باستخدام عالي لوحدة معالجة الرسومات، والحد الأدنى من اتصال وحدة المعالجة المركزية (المضيف) بوحدة معالجة الرسومات (الجهاز)، وعدم وجود أي حمل من خط أنابيب الإدخال.
الخطوة الأولى في تحليل الأداء هي الحصول على ملف تعريف لنموذج يعمل باستخدام وحدة معالجة رسومات واحدة.
يمكن لصفحة النظرة العامة على ملف تعريف TensorBoard - والتي تعرض عرضًا عالي المستوى لكيفية أداء النموذج الخاص بك أثناء تشغيل ملف التعريف - أن توفر فكرة عن مدى بعد برنامجك عن السيناريو المثالي.
الأرقام الرئيسية التي يجب الانتباه إليها في صفحة النظرة العامة هي:
- ما مقدار وقت الخطوة من التنفيذ الفعلي للجهاز
- النسبة المئوية للعمليات الموضوعة على الجهاز مقابل المضيف
- كم عدد النوى التي تستخدم
fp16
إن تحقيق الأداء الأمثل يعني تعظيم هذه الأرقام في الحالات الثلاث. للحصول على فهم متعمق لبرنامجك، ستحتاج إلى أن تكون على دراية بعارض التتبع الخاص بـ TensorBoard's Profiler. تعرض الأقسام أدناه بعض أنماط عارض التتبع الشائعة التي يجب عليك البحث عنها عند تشخيص اختناقات الأداء.
يوجد أدناه صورة لعرض تتبع النموذج الذي يعمل على وحدة معالجة رسومات واحدة. من قسمي TensorFlow Name Scope و TensorFlow Ops ، يمكنك تحديد أجزاء مختلفة من النموذج، مثل التمرير الأمامي، ووظيفة الخسارة، وحساب التمرير الخلفي/التدرج، وتحديث وزن المُحسِّن. يمكنك أيضًا تشغيل العمليات على وحدة معالجة الرسومات بجوار كل Stream ، والتي تشير إلى تدفقات CUDA. يتم استخدام كل تيار لمهام محددة. في هذا التتبع، يتم استخدام Stream#118 لإطلاق النواة الحاسوبية والنسخ من جهاز إلى جهاز. يتم استخدام Stream#119 للنسخ من المضيف إلى الجهاز و Stream#120 للنسخ من الجهاز إلى المضيف.
يوضح التتبع أدناه الخصائص المشتركة للنموذج المؤدي.
على سبيل المثال، يبدو المخطط الزمني لحساب وحدة معالجة الرسومات ( Stream#118 ) "مشغولًا" مع وجود فجوات قليلة جدًا. هناك عدد قليل من النسخ من المضيف إلى الجهاز ( الدفق #119 ) ومن جهاز إلى المضيف ( الدفق #120 )، بالإضافة إلى الحد الأدنى من الفجوات بين الخطوات. عند تشغيل ملف التعريف لبرنامجك، قد لا تتمكن من تحديد هذه الخصائص المثالية في طريقة عرض التتبع الخاصة بك. يغطي الجزء المتبقي من هذا الدليل السيناريوهات الشائعة وكيفية إصلاحها.
1. تصحيح خط أنابيب الإدخال
الخطوة الأولى في تصحيح أخطاء أداء وحدة معالجة الرسومات هي تحديد ما إذا كان برنامجك مرتبطًا بالإدخال. أسهل طريقة لمعرفة ذلك هي استخدام محلل خط الإدخال الخاص بملف التعريف، على TensorBoard، والذي يوفر نظرة عامة على الوقت الذي يقضيه في مسار الإدخال.
يمكنك اتخاذ الإجراءات المحتملة التالية إذا كان مسار الإدخال الخاص بك يساهم بشكل كبير في وقت الخطوة:
- يمكنك استخدام الدليل الخاص بـ
tf.data
لمعرفة كيفية تصحيح أخطاء مسار الإدخال الخاص بك. - هناك طريقة سريعة أخرى للتحقق مما إذا كان خط أنابيب الإدخال يمثل عنق الزجاجة، وهي استخدام بيانات الإدخال التي تم إنشاؤها عشوائيًا والتي لا تحتاج إلى أي معالجة مسبقة. فيما يلي مثال لاستخدام هذه التقنية لنموذج ResNet. إذا كان مسار الإدخال هو الأمثل، فيجب أن تحصل على أداء مماثل مع البيانات الحقيقية والبيانات العشوائية/الاصطناعية التي تم إنشاؤها. سيكون الحمل الوحيد في حالة البيانات الاصطناعية بسبب نسخة بيانات الإدخال التي يمكن جلبها مسبقًا وتحسينها مرة أخرى.
بالإضافة إلى ذلك، راجع أفضل الممارسات لتحسين مسار البيانات المدخلة .
2. تصحيح أداء وحدة معالجة الرسومات (GPU) واحدة
هناك العديد من العوامل التي يمكن أن تساهم في انخفاض استخدام وحدة معالجة الرسومات. فيما يلي بعض السيناريوهات التي تتم ملاحظتها بشكل شائع عند النظر إلى عارض التتبع والحلول المحتملة.
1. تحليل الفجوات بين الخطوات
الملاحظة الشائعة عندما لا يعمل برنامجك على النحو الأمثل هي وجود فجوات بين خطوات التدريب. في صورة عرض التتبع أدناه، توجد فجوة كبيرة بين الخطوتين 8 و9، مما يعني أن وحدة معالجة الرسومات تكون في وضع الخمول خلال تلك الفترة.
إذا أظهر عارض التتبع فجوات كبيرة بين الخطوات، فقد يكون هذا مؤشرًا على أن برنامجك مقيد بالإدخال. في هذه الحالة، يجب عليك الرجوع إلى القسم السابق حول تصحيح أخطاء مسار الإدخال الخاص بك إذا لم تكن قد قمت بذلك بالفعل.
ومع ذلك، حتى مع وجود مسار إدخال محسّن، لا يزال من الممكن وجود فجوات بين نهاية خطوة واحدة وبداية خطوة أخرى بسبب تنافس مؤشر ترابط وحدة المعالجة المركزية. يستخدم tf.data
مؤشرات الترابط الخلفية لموازاة معالجة خطوط الأنابيب. قد تتداخل هذه الخيوط مع نشاط مضيف وحدة معالجة الرسومات الذي يحدث في بداية كل خطوة، مثل نسخ البيانات أو جدولة عمليات وحدة معالجة الرسومات.
إذا لاحظت وجود فجوات كبيرة على الجانب المضيف، الذي يقوم بجدولة هذه العمليات على وحدة معالجة الرسومات، فيمكنك تعيين متغير البيئة TF_GPU_THREAD_MODE=gpu_private
. وهذا يضمن إطلاق نواة وحدة معالجة الرسومات من سلاسل العمليات المخصصة لها، وعدم وضعها في قائمة الانتظار خلف عمل tf.data
.
يمكن أن تحدث الفجوات بين الخطوات أيضًا بسبب الحسابات المترية، أو عمليات رد اتصال Keras، أو العمليات خارج tf.function
التي يتم تشغيلها على المضيف. لا تتمتع هذه العمليات بأداء جيد مثل العمليات الموجودة داخل الرسم البياني TensorFlow. بالإضافة إلى ذلك، تعمل بعض هذه العمليات على وحدة المعالجة المركزية وتنسخ الموترات ذهابًا وإيابًا من وحدة معالجة الرسومات.
إذا كنت لا تزال تلاحظ وجود فجوات بين الخطوات في عارض التتبع، بعد تحسين مسار الإدخال الخاص بك، فيجب عليك إلقاء نظرة على رمز النموذج بين الخطوات والتحقق مما إذا كان تعطيل عمليات الاسترجاعات/المقاييس يؤدي إلى تحسين الأداء. بعض تفاصيل هذه العمليات موجودة أيضًا في عارض التتبع (كل من الجهاز والمضيف). التوصية في هذا السيناريو هي استهلاك النفقات العامة لهذه العمليات عن طريق تنفيذها بعد عدد محدد من الخطوات بدلاً من كل خطوة. عند استخدام الأسلوب Model.compile
في tf.keras
API، يؤدي تعيين علامة steps_per_execution
إلى القيام بذلك تلقائيًا. للحصول على حلقات تدريب مخصصة، استخدم tf.while_loop
.
2. تحقيق استخدام أعلى للجهاز
1. حبات GPU الصغيرة وتأخير إطلاق النواة المضيفة
يقوم المضيف بوضع النواة في قائمة الانتظار ليتم تشغيلها على وحدة معالجة الرسومات، ولكن هناك زمن وصول (حوالي 20-40 ميكروثانية) قبل تنفيذ النواة فعليًا على وحدة معالجة الرسومات. في الحالة المثالية، يقوم المضيف بوضع ما يكفي من النوى على وحدة معالجة الرسومات بحيث تقضي وحدة معالجة الرسومات معظم وقتها في التنفيذ، بدلاً من انتظار المضيف لوضع المزيد من النوى في قائمة الانتظار.
تُظهر صفحة النظرة العامة الخاصة بملف التعريف على TensorBoard مقدار الوقت الذي كانت فيه وحدة معالجة الرسومات في وضع الخمول بسبب انتظار المضيف لتشغيل النواة. في الصورة أدناه، تكون وحدة معالجة الرسومات في وضع الخمول لمدة 10% تقريبًا من وقت الخطوة في انتظار إطلاق النواة.
يُظهر عارض التتبع لهذا البرنامج نفسه فجوات صغيرة بين النوى حيث يكون المضيف مشغولاً بإطلاق النوى على وحدة معالجة الرسومات.
من خلال إطلاق الكثير من العمليات الصغيرة على وحدة معالجة الرسومات (مثل الإضافة العددية، على سبيل المثال)، قد لا يتمكن المضيف من مواكبة وحدة معالجة الرسومات. تُظهر أداة TensorFlow Stats في TensorBoard لنفس ملف التعريف 126,224 عملية متعددة تستغرق 2.77 ثانية. وبالتالي، يبلغ طول كل نواة حوالي 21.9 ميكروثانية، وهو صغير جدًا (في نفس وقت زمن الوصول للإطلاق تقريبًا) ويمكن أن يؤدي إلى تأخير إطلاق النواة المضيفة.
إذا أظهر عارض التتبع الخاص بك العديد من الفجوات الصغيرة بين العمليات على وحدة معالجة الرسومات كما في الصورة أعلاه، فيمكنك:
- قم بتسلسل الموترات الصغيرة واستخدم العمليات الموجهة أو استخدم حجم دفعة أكبر لجعل كل نواة تم إطلاقها تقوم بمزيد من العمل، مما سيبقي وحدة معالجة الرسومات مشغولة لفترة أطول.
- تأكد من أنك تستخدم
tf.function
لإنشاء الرسوم البيانية TensorFlow، بحيث لا تقوم بتشغيل العمليات في وضع حريص تمامًا. إذا كنت تستخدمModel.fit
(على عكس حلقة التدريب المخصصة معtf.GradientTape
)، فإنtf.keras.Model.compile
سيقوم بذلك تلقائيًا نيابةً عنك. - دمج النوى باستخدام XLA مع
tf.function(jit_compile=True)
أو التجميع التلقائي. لمزيد من التفاصيل، انتقل إلى قسم تمكين الدقة المختلطة وXLA أدناه لمعرفة كيفية تمكين XLA للحصول على أداء أعلى. يمكن أن تؤدي هذه الميزة إلى استخدام عالي للجهاز.
2. التنسيب المرجعي لـ TensorFlow
تعرض لك صفحة نظرة عامة على ملف التعريف النسبة المئوية للعمليات التي تم إجراؤها على المضيف مقابل الجهاز (يمكنك أيضًا التحقق من موضع عمليات معينة من خلال النظر إلى عارض التتبع . كما هو الحال في الصورة أدناه، تريد النسبة المئوية للعمليات على المضيف أن يكون صغيراً جداً مقارنة بالجهاز.
من الناحية المثالية، ينبغي وضع معظم العمليات الحسابية المكثفة على وحدة معالجة الرسومات.
لمعرفة الأجهزة التي تم تعيين العمليات والموترات في نموذجك لها، قم بتعيين tf.debugging.set_log_device_placement(True)
كالبيان الأول لبرنامجك.
لاحظ أنه في بعض الحالات، حتى لو قمت بتحديد عملية لوضعها على جهاز معين، فإن تنفيذها قد يتجاوز هذا الشرط (على سبيل المثال: tf.unique
). حتى بالنسبة للتدريب الفردي على وحدة معالجة الرسومات، فإن تحديد إستراتيجية توزيع، مثل tf.distribute.OneDeviceStrategy
، يمكن أن يؤدي إلى وضع أكثر تحديدًا للعمليات على جهازك.
أحد أسباب وضع غالبية العمليات على وحدة معالجة الرسومات هو منع نسخ الذاكرة المفرطة بين المضيف والجهاز (من المتوقع نسخ الذاكرة لبيانات الإدخال/الإخراج النموذجية بين المضيف والجهاز). يتم توضيح مثال على النسخ المفرط في عرض التتبع أدناه على تدفقات GPU #167 و #168 و #169 .
قد تؤدي هذه النسخ أحيانًا إلى الإضرار بالأداء إذا منعت تنفيذ نواة وحدة معالجة الرسومات. تحتوي عمليات نسخ الذاكرة في عارض التتبع على مزيد من المعلومات حول العمليات التي تمثل مصدر هذه الموترات المنسوخة، ولكن قد لا يكون من السهل دائمًا ربط نسخة memCopy بعملية تشغيلية. في هذه الحالات، من المفيد إلقاء نظرة على العمليات القريبة للتحقق مما إذا كانت نسخة الذاكرة تحدث في نفس الموقع في كل خطوة.
3. حبات أكثر كفاءة على وحدات معالجة الرسومات
بمجرد أن يصبح استخدام وحدة معالجة الرسومات (GPU) لبرنامجك مقبولاً، فإن الخطوة التالية هي النظر في زيادة كفاءة نواة وحدة معالجة الرسومات (GPU) من خلال استخدام Tensor Cores أو عمليات الدمج.
1. الاستفادة من النوى الموتر
تحتوي وحدات معالجة الرسومات NVIDIA® الحديثة على Tensor Cores التي يمكنها تحسين أداء النوى المؤهلة بشكل كبير.
يمكنك استخدام إحصائيات نواة GPU الخاصة بـ TensorBoard لتصور أي نواة GPU مؤهلة لـ Tensor Core، وأي النوى تستخدم Tensor Cores. يعد تمكين fp16
(راجع قسم تمكين الدقة المختلطة أدناه) إحدى الطرق لجعل نواة مضاعفة المصفوفة العامة (GEMM) الخاصة ببرنامجك (matmul ops) تستخدم Tensor Core. تستخدم نواة وحدة معالجة الرسومات Tensor Cores بكفاءة عندما تكون الدقة fp16 وتكون أبعاد موتر الإدخال/الإخراج قابلة للقسمة على 8 أو 16 (لـ int8
).
للحصول على توصيات تفصيلية أخرى حول كيفية جعل النواة فعالة لوحدات معالجة الرسومات، راجع دليل أداء التعلم العميق لـ NVIDIA® .
2. فيوز العمليات
استخدم tf.function(jit_compile=True)
لدمج العمليات الصغيرة لتكوين نواة أكبر تؤدي إلى مكاسب كبيرة في الأداء. لمعرفة المزيد، راجع دليل XLA .
3. تمكين الدقة المختلطة وXLA
بعد اتباع الخطوات المذكورة أعلاه، يعد تمكين الدقة المختلطة وXLA خطوتين اختياريتين يمكنك اتخاذهما لتحسين الأداء بشكل أكبر. ويتمثل الأسلوب المقترح في تمكينها واحدة تلو الأخرى والتحقق من أن فوائد الأداء كما هو متوقع.
1. تمكين الدقة المختلطة
يوضح دليل الدقة TensorFlow Mixed كيفية تمكين دقة fp16
على وحدات معالجة الرسومات. قم بتمكين AMP على وحدات معالجة الرسومات NVIDIA® لاستخدام Tensor Cores وتحقيق ما يصل إلى 3x عمليات تسريع إجمالية عند مقارنتها باستخدام دقة fp32
(float32) فقط على Volta وبنيات GPU الأحدث.
تأكد من أن أبعاد المصفوفة/الموتر تلبي متطلبات استدعاء النوى التي تستخدم Tensor Cores. تستخدم نواة وحدة معالجة الرسومات Tensor Cores بكفاءة عندما تكون الدقة fp16 وتكون أبعاد الإدخال/الإخراج قابلة للقسمة على 8 أو 16 (لـ int8).
لاحظ أنه مع الإصدار 7.6.3 من cuDNN والإصدارات الأحدث، سيتم تلقائيًا تعبئة أبعاد الالتفاف عند الضرورة للاستفادة من Tensor Cores.
اتبع أفضل الممارسات الموضحة أدناه لتحقيق أقصى قدر من فوائد الأداء لدقة fp16
.
1. استخدم حبات FP16 الأمثل
مع تمكين fp16
، يجب أن تستخدم نواة مضاعفات المصفوفة (GEMM) لبرنامجك إصدار fp16
المقابل الذي يستخدم Tensor Cores. ومع ذلك، في بعض الحالات، لا يحدث هذا ولا تواجه التسريع المتوقع من تمكين fp16
، حيث يتراجع برنامجك إلى التنفيذ غير الفعال بدلاً من ذلك.
تعرض صفحة إحصائيات kernel GPU العمليات المؤهلة لـ Tensor Core وأي النوى تستخدم فعليًا Tensor Core الفعال. يحتوي دليل NVIDIA® حول أداء التعلم العميق على اقتراحات إضافية حول كيفية الاستفادة من Tensor Cores. بالإضافة إلى ذلك، ستظهر فوائد استخدام fp16
أيضًا في النوى التي كانت مرتبطة بالذاكرة سابقًا، حيث ستستغرق العمليات الآن نصف الوقت.
2. قياس الخسارة الديناميكي مقابل الثابت
يعد قياس الخسارة ضروريًا عند استخدام fp16
لمنع التدفق السفلي بسبب الدقة المنخفضة. هناك نوعان من قياس الخسارة، ديناميكي وثابت، وكلاهما موضح بمزيد من التفصيل في دليل الدقة المختلطة . يمكنك استخدام سياسة mixed_float16
لتمكين تحجيم الخسارة تلقائيًا داخل مُحسِّن Keras.
عند محاولة تحسين الأداء، من المهم أن تتذكر أن قياس الخسارة الديناميكي يمكن أن يقدم عمليات مشروطة إضافية يتم تشغيلها على المضيف، وتؤدي إلى فجوات ستكون مرئية بين الخطوات في عارض التتبع. من ناحية أخرى، لا يحتوي مقياس الخسارة الثابتة على مثل هذه النفقات العامة ويمكن أن يكون خيارًا أفضل من حيث الأداء من خلال المصيد الذي تحتاجه لتحديد القيمة الصحيحة لمقياس الخسارة الثابتة.
2. قم بتمكين XLA باستخدام tf.function(jit_compile=True) أو التجميع التلقائي
كخطوة أخيرة للحصول على أفضل أداء باستخدام وحدة معالجة رسومات واحدة، يمكنك تجربة تمكين XLA، والذي سيؤدي إلى دمج العمليات ويؤدي إلى استخدام أفضل للجهاز ومساحة أقل للذاكرة. للحصول على تفاصيل حول كيفية تمكين XLA في برنامجك باستخدام tf.function(jit_compile=True)
أو التجميع التلقائي، راجع دليل XLA .
يمكنك ضبط مستوى JIT العام على -1
(إيقاف) أو 1
أو 2
. المستوى الأعلى هو أكثر عدوانية وقد يقلل من التوازي ويستخدم المزيد من الذاكرة. قم بتعيين القيمة إلى 1
إذا كان لديك قيود على الذاكرة. لاحظ أن XLA لا يؤدي أداءً جيدًا للنماذج ذات أشكال موتر الإدخال المتغيرة حيث سيتعين على مترجم XLA الاستمرار في تجميع النواة كلما واجه أشكالًا جديدة.
2. قم بتحسين الأداء على المضيف الفردي متعدد وحدات معالجة الرسومات
يمكن استخدام tf.distribute.MirroredStrategy
API لتوسيع نطاق تدريب النموذج من وحدة معالجة رسومات واحدة إلى وحدات معالجة رسومات متعددة على مضيف واحد. (لمعرفة المزيد حول كيفية القيام بالتدريب الموزع باستخدام TensorFlow، راجع التدريب الموزع باستخدام TensorFlow ، واستخدام وحدة معالجة الرسومات (GPU) ، واستخدام أدلة TPU والتدريب الموزع باستخدام البرنامج التعليمي Keras .)
على الرغم من أن الانتقال من وحدة معالجة رسومات واحدة إلى وحدات معالجة رسومات متعددة يجب أن يكون قابلاً للتطوير بشكل مثالي خارج الصندوق، إلا أنه قد تواجه أحيانًا مشكلات في الأداء.
عند الانتقال من التدريب باستخدام وحدة معالجة رسومات واحدة إلى وحدات معالجة رسوميات متعددة على نفس المضيف، من الناحية المثالية، يجب أن تجرب قياس الأداء مع الحمل الإضافي فقط للاتصال التدرجي وزيادة استخدام مؤشر ترابط المضيف. وبسبب هذا الحمل، لن يكون لديك سرعة 2x دقيقة إذا انتقلت من 1 إلى 2 وحدة معالجة رسوميات، على سبيل المثال.
يعرض عرض التتبع أدناه مثالاً على الحمل الإضافي للاتصالات عند التدريب على وحدات معالجة الرسومات المتعددة. هناك بعض النفقات العامة لتسلسل التدرجات، وتوصيلها عبر النسخ المتماثلة، وتقسيمها قبل إجراء تحديث الوزن.
ستساعدك قائمة التحقق التالية على تحقيق أداء أفضل عند تحسين الأداء في سيناريو وحدات معالجة الرسومات المتعددة:
- حاول تعظيم حجم الدفعة، مما سيؤدي إلى زيادة استخدام الجهاز وإطفاء تكاليف الاتصال عبر وحدات معالجة الرسومات المتعددة. يساعد استخدام ملف تعريف الذاكرة في التعرف على مدى اقتراب برنامجك من ذروة استخدام الذاكرة. لاحظ أنه على الرغم من أن حجم الدفعة الأكبر يمكن أن يؤثر على التقارب، إلا أن فوائد الأداء تفوق ذلك عادةً.
- عند الانتقال من وحدة معالجة رسومات واحدة إلى وحدات معالجة رسوميات متعددة، يتعين على نفس المضيف الآن معالجة المزيد من بيانات الإدخال. لذا، بعد (1)، يوصى بإعادة التحقق من أداء خط أنابيب الإدخال والتأكد من أنه ليس عنق الزجاجة.
- تحقق من المخطط الزمني لوحدة معالجة الرسومات في عرض التتبع لبرنامجك بحثًا عن أي مكالمات AllReduce غير ضرورية، حيث يؤدي ذلك إلى مزامنة عبر جميع الأجهزة. في عرض التتبع الموضح أعلاه، يتم إجراء AllReduce عبر نواة NCCL ، ويوجد استدعاء NCCL واحد فقط على كل وحدة معالجة رسومات للتدرجات اللونية في كل خطوة.
- تحقق من وجود عمليات نسخ D2H وH2D وD2D غير الضرورية والتي يمكن تقليلها.
- تحقق من وقت الخطوة للتأكد من أن كل نسخة متماثلة تقوم بنفس العمل. على سبيل المثال، يمكن أن يحدث أن يتم تجاوز الاشتراك في وحدة GPU واحدة (عادةً
GPU0
) لأن المضيف ينتهي عن طريق الخطأ إلى بذل المزيد من العمل عليها. - وأخيرًا، تحقق من خطوة التدريب عبر جميع وحدات معالجة الرسومات في عرض التتبع الخاص بك بحثًا عن أي عمليات يتم تنفيذها بشكل تسلسلي. يحدث هذا عادةً عندما يشتمل برنامجك على تبعيات تحكم من وحدة معالجة رسومات (GPU) إلى أخرى. في الماضي، كان تصحيح الأداء في هذه الحالة يتم حله على أساس كل حالة على حدة. إذا لاحظت هذا السلوك في برنامجك، فأبلغ عن مشكلة GitHub باستخدام صور عرض التتبع الخاص بك.
1. تحسين التدرج AllReduce
عند التدريب باستخدام إستراتيجية متزامنة، يتلقى كل جهاز جزءًا من بيانات الإدخال.
بعد حساب التمريرات الأمامية والخلفية عبر النموذج، يجب تجميع التدرجات المحسوبة على كل جهاز وتقليلها. يحدث هذا التدرج AllReduce بعد حساب التدرج على كل جهاز، وقبل أن يقوم المحسن بتحديث أوزان النموذج.
تقوم كل وحدة معالجة رسومات (GPU) أولاً بتسلسل التدرجات عبر طبقات النموذج، وتوصيلها عبر وحدات معالجة الرسومات باستخدام tf.distribute.CrossDeviceOps
( tf.distribute.NcclAllReduce
هو الإعداد الافتراضي)، ثم تقوم بإرجاع التدرجات بعد التخفيض لكل طبقة.
سيستخدم المُحسِّن هذه التدرجات المنخفضة لتحديث أوزان النموذج الخاص بك. من الناحية المثالية، يجب أن تحدث هذه العملية في نفس الوقت على جميع وحدات معالجة الرسومات لمنع أي أعباء إضافية.
يجب أن يكون وقت AllReduce هو نفس الوقت تقريبًا:
(number of parameters * 4bytes)/ (communication bandwidth)
يعد هذا الحساب مفيدًا كاختبار سريع لفهم ما إذا كان الأداء الذي تتمتع به عند تشغيل مهمة تدريب موزعة كما هو متوقع، أو إذا كنت بحاجة إلى إجراء المزيد من تصحيح أخطاء الأداء. يمكنك الحصول على عدد المعلمات في النموذج الخاص بك من Model.summary
.
لاحظ أن حجم كل معلمة نموذج يبلغ 4 بايت نظرًا لأن TensorFlow يستخدم fp32
(float32) لتوصيل التدرجات. حتى عند تمكين fp16
، يستخدم NCCL AllReduce معلمات fp32
.
للحصول على فوائد القياس، يجب أن يكون وقت الخطوة أعلى بكثير مقارنة بهذه النفقات العامة. تتمثل إحدى طرق تحقيق ذلك في استخدام حجم دفعة أكبر، حيث يؤثر حجم الدُفعة على وقت الخطوة، ولكنه لا يؤثر على حمل الاتصالات.
2. التنافس على موضوع مضيف GPU
عند تشغيل وحدات معالجة رسومات متعددة، فإن مهمة وحدة المعالجة المركزية هي إبقاء جميع الأجهزة مشغولة من خلال تشغيل نواة وحدة معالجة الرسومات بكفاءة عبر الأجهزة.
ومع ذلك، عندما يكون هناك الكثير من العمليات المستقلة التي يمكن لوحدة المعالجة المركزية جدولتها على وحدة معالجة رسومات واحدة، يمكن لوحدة المعالجة المركزية أن تقرر استخدام الكثير من سلاسل العمليات المضيفة الخاصة بها لإبقاء وحدة معالجة الرسومات مشغولة، ثم إطلاق النواة على وحدة معالجة رسومات أخرى بترتيب غير حتمي . يمكن أن يتسبب هذا في انحراف أو قياس سلبي، مما قد يؤثر سلبًا على الأداء.
يُظهر عارض التتبع أدناه مقدار الحمل عندما تتعثر وحدة المعالجة المركزية (CPU) في تشغيل نواة وحدة معالجة الرسومات (GPU) بشكل غير فعال، حيث يكون GPU1
خاملاً ثم يبدأ في تشغيل العمليات بعد بدء تشغيل GPU2
.
يوضح عرض التتبع للمضيف أن المضيف يقوم بتشغيل النواة على GPU2
قبل تشغيلها على GPU1
(لاحظ أن عمليات tf_Compute*
أدناه لا تشير إلى مؤشرات ترابط وحدة المعالجة المركزية).
إذا واجهت هذا النوع من الترتيب المذهل لنواة وحدة معالجة الرسومات في عرض التتبع لبرنامجك، فإن الإجراء الموصى به هو:
- قم بتعيين متغير البيئة TensorFlow
TF_GPU_THREAD_MODE
علىgpu_private
. سيخبر متغير البيئة هذا المضيف بالحفاظ على خصوصية سلاسل العمليات الخاصة بوحدة معالجة الرسومات. - افتراضيًا، يقوم
TF_GPU_THREAD_MODE=gpu_private
بتعيين عدد سلاسل الرسائل على 2، وهو ما يكفي في معظم الحالات. ومع ذلك، يمكن تغيير هذا الرقم عن طريق تعيين متغير البيئة TensorFlowTF_GPU_THREAD_COUNT
على العدد المطلوب من سلاسل الرسائل.