قم بتحسين أداء وحدة معالجة الرسومات TensorFlow باستخدام TensorFlow Profiler

ملخص

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

إذا كنت جديدًا في ملف التعريف:

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

  • نقل البيانات بين المضيف (CPU) والجهاز (GPU) ؛ و
  • نظرًا للكمون الذي ينطوي عليه الأمر عند قيام المضيف بتشغيل نواة GPU.

سير عمل تحسين الأداء

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

يوصى بتصحيح مشكلات الأداء بالترتيب التالي:

  1. تحسين الأداء وتصحيحه على وحدة معالجة رسومات واحدة:
    1. تحقق مما إذا كان خط أنابيب الإدخال يمثل عنق الزجاجة.
    2. تصحيح أداء وحدة معالجة الرسومات.
    3. قم بتمكين الدقة المختلطة (باستخدام fp16 (float16)) وتمكين XLA اختياريًا.
  2. قم بتحسين الأداء وتصحيحه على مضيف واحد متعدد وحدات معالجة الرسومات.

على سبيل المثال ، إذا كنت تستخدم إستراتيجية توزيع TensorFlow لتدريب نموذج على مضيف واحد مع العديد من وحدات معالجة الرسومات ولاحظ استخدام GPU دون المستوى الأمثل ، فيجب عليك أولاً تحسين وتصحيح الأداء لوحدة معالجة الرسومات قبل تصحيح أخطاء نظام GPU المتعدد.

كخط أساس للحصول على رمز أداء على وحدات معالجة الرسومات ، يفترض هذا الدليل أنك تستخدم بالفعل tf.function . ستستخدم واجهات برمجة تطبيقات Keras Model.compile و Model.fit tf.function تلقائيًا تحت الغطاء. عند كتابة حلقة تدريب مخصصة باستخدام tf.GradientTape ، ارجع إلى الأداء الأفضل باستخدام وظيفة tf حول كيفية تمكين tf.function .

تناقش الأقسام التالية الأساليب المقترحة لكل من السيناريوهات المذكورة أعلاه للمساعدة في تحديد معوقات الأداء وإصلاحها.

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

في الحالة المثالية ، يجب أن يتمتع برنامجك باستخدام GPU مرتفعًا ، وحد أدنى من وحدة المعالجة المركزية (المضيف) لاتصال GPU (الجهاز) ، وعدم وجود حمل من خط أنابيب الإدخال.

تتمثل الخطوة الأولى في تحليل الأداء في الحصول على ملف تعريف لنموذج يعمل باستخدام وحدة معالجة رسومات واحدة.

يمكن أن توفر صفحة نظرة عامة على ملف التعريف في TensorBoard - التي تعرض طريقة عرض أعلى مستوى لكيفية أداء نموذجك أثناء تشغيل ملف التعريف - فكرة عن مدى بُعد برنامجك عن السيناريو المثالي.

TensorFlow Profiler Overview Page

الأرقام الرئيسية التي يجب الانتباه إليها في صفحة النظرة العامة هي:

  1. مقدار الوقت المستغرق من تنفيذ الجهاز الفعلي
  2. النسبة المئوية للعمليات التي تم إجراؤها على الجهاز مقابل المضيف
  3. كم عدد الحبات التي تستخدم fp16

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

يوجد أدناه صورة لعرض تتبع نموذج يعمل على وحدة معالجة رسومات واحدة. من قسم TensorFlow Name Scope و TensorFlow Ops ، يمكنك تحديد أجزاء مختلفة من النموذج ، مثل المرور الأمامي ، ووظيفة الخسارة ، وحساب التمرير / التدرج الخلفي ، وتحديث وزن المُحسِّن. يمكنك أيضًا تشغيل العمليات على وحدة معالجة الرسومات بجوار كل دفق ، والتي تشير إلى تدفقات CUDA. يتم استخدام كل تيار لمهام محددة. في هذا التتبع ، يتم استخدام Stream # 118 لتشغيل نواة الحوسبة والنسخ من جهاز إلى جهاز. يتم استخدام Stream # 119 للنسخ من مضيف إلى جهاز و Stream # 120 للجهاز لاستضافة النسخة.

يظهر التتبع أدناه الخصائص المشتركة لنموذج الأداء.

image

على سبيل المثال ، يبدو المخطط الزمني لحساب GPU ( Stream # 118 ) "مشغولًا" مع وجود فجوات قليلة جدًا. توجد نسخ قليلة من مضيف إلى جهاز ( Stream # 119 ) ومن جهاز إلى مضيف ( Stream # 120 ) ، بالإضافة إلى الحد الأدنى من الفجوات بين الخطوات. عند تشغيل منشئ ملفات التعريف لبرنامجك ، قد لا تتمكن من تحديد هذه الخصائص المثالية في عرض التتبع الخاص بك. يغطي الجزء المتبقي من هذا الدليل السيناريوهات الشائعة وكيفية إصلاحها.

1. تصحيح خط أنابيب الإدخال

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

image

يمكنك اتخاذ الإجراءات المحتملة التالية إذا كان خط أنابيب الإدخال الخاص بك يساهم بشكل كبير في وقت الخطوة:

  • يمكنك استخدام الدليل الخاص بـ tf.data لمعرفة كيفية تصحيح أخطاء خط أنابيب الإدخال.
  • هناك طريقة سريعة أخرى للتحقق مما إذا كان خط أنابيب الإدخال هو عنق الزجاجة وهي استخدام بيانات الإدخال التي تم إنشاؤها عشوائيًا والتي لا تحتاج إلى أي معالجة مسبقة. فيما يلي مثال على استخدام هذه التقنية لنموذج ResNet. إذا كان خط أنابيب الإدخال هو الأمثل ، فيجب أن تواجه أداءً مشابهًا مع البيانات الحقيقية والبيانات العشوائية / الاصطناعية التي تم إنشاؤها. سيكون الحمل الزائد الوحيد في حالة البيانات التركيبية بسبب نسخ بيانات الإدخال التي يمكن جلبها مسبقًا وتحسينها مرة أخرى.

بالإضافة إلى ذلك ، راجع أفضل الممارسات لتحسين خط أنابيب بيانات الإدخال .

2. تصحيح أداء وحدة معالجة الرسومات

هناك العديد من العوامل التي يمكن أن تسهم في انخفاض استخدام وحدة معالجة الرسومات. فيما يلي بعض السيناريوهات التي يتم ملاحظتها بشكل شائع عند النظر إلى عارض التتبع والحلول المحتملة.

1. تحليل الفجوات بين الخطوات

الملاحظة الشائعة عندما لا يعمل برنامجك بالشكل الأمثل هي الفجوات بين خطوات التدريب. في صورة عرض التتبع أدناه ، توجد فجوة كبيرة بين الخطوتين 8 و 9 ، مما يعني أن وحدة معالجة الرسومات (GPU) في وضع الخمول خلال ذلك الوقت.

image

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

ومع ذلك ، حتى مع وجود خط إدخال محسن ، لا يزال بإمكانك وجود فجوات بين نهاية خطوة واحدة وبداية أخرى بسبب تنازع مؤشر ترابط وحدة المعالجة المركزية. tf.data من خيوط الخلفية لموازاة معالجة خطوط الأنابيب. قد تتداخل هذه الخيوط مع نشاط جانب مضيف GPU الذي يحدث في بداية كل خطوة ، مثل نسخ البيانات أو جدولة عمليات GPU.

إذا لاحظت وجود فجوات كبيرة على جانب المضيف ، والتي تقوم بجدولة هذه العمليات على وحدة معالجة الرسومات ، فيمكنك تعيين متغير البيئة TF_GPU_THREAD_MODE=gpu_private . يضمن ذلك إطلاق نواة GPU من سلاسل الرسائل المخصصة الخاصة بها ، وعدم وضعها في قائمة الانتظار خلف عمل tf.data .

يمكن أن تحدث الفجوات بين الخطوات أيضًا بسبب الحسابات المترية أو عمليات رد نداء Keras أو العمليات خارج tf.function التي تعمل على المضيف. لا تتمتع هذه العمليات بأداء جيد مثل العمليات داخل الرسم البياني TensorFlow. بالإضافة إلى ذلك ، تعمل بعض هذه العمليات على وحدة المعالجة المركزية ونسخ الموترات ذهابًا وإيابًا من وحدة معالجة الرسومات.

إذا كنت لا تزال تلاحظ وجود فجوات بين الخطوات في عارض التتبع بعد تحسين خط أنابيب الإدخال ، فيجب أن تنظر في رمز النموذج بين الخطوات وتحقق مما إذا كان تعطيل عمليات الاسترجاعات / المقاييس يحسن الأداء. بعض تفاصيل هذه العمليات موجودة أيضًا على عارض التتبع (كل من الجهاز والجانب المضيف). التوصية في هذا السيناريو هي إطفاء النفقات العامة لهذه العمليات من خلال تنفيذها بعد عدد ثابت من الخطوات بدلاً من كل خطوة. عند استخدام طريقة Model.compile في واجهة برمجة تطبيقات tf.keras ، يؤدي تعيين علامة steps_per_execution إلى القيام بذلك تلقائيًا. لحلقات التدريب المخصصة ، استخدم tf. tf.while_loop .

2. تحقيق استخدام أعلى للجهاز

1. نواة GPU الصغيرة وتأخيرات إطلاق نواة المضيف

يقوم المضيف بإدراج النواة في قائمة الانتظار ليتم تشغيلها على وحدة معالجة الرسومات ، ولكن هناك زمن انتقال (حوالي 20-40 ميكرو ثانية) متضمن قبل تنفيذ النواة فعليًا على وحدة معالجة الرسومات. في الحالة المثالية ، يقوم المضيف بإدراج عدد كافٍ من الحبات على وحدة معالجة الرسومات (GPU) بحيث تقضي وحدة معالجة الرسومات معظم وقتها في التنفيذ ، بدلاً من الانتظار على المضيف لإدراج المزيد من النوى.

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

image

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

image

من خلال إطلاق الكثير من العمليات الصغيرة على وحدة معالجة الرسومات (مثل إضافة عددية ، على سبيل المثال) ، قد لا يواكب المضيف وحدة معالجة الرسومات. تُظهر أداة TensorFlow Stats في TensorBoard لنفس الملف الشخصي 126،224 عملية Mul تستغرق 2.77 ثانية. وبالتالي ، يبلغ حجم كل نواة حوالي 21.9 ميكرو ثانية ، وهي صغيرة جدًا (في نفس الوقت تقريبًا مثل زمن انتقال الإطلاق) ويمكن أن تؤدي إلى تأخيرات في إطلاق نواة المضيف.

image

إذا أظهر عارض التتبع لديك العديد من الفجوات الصغيرة بين العمليات على وحدة معالجة الرسومات كما في الصورة أعلاه ، فيمكنك:

  • اربط الموترات الصغيرة واستخدم العمليات الموجهة أو استخدم حجم دفعة أكبر لجعل كل نواة تم إطلاقها تقوم بمزيد من العمل ، مما سيبقي وحدة معالجة الرسومات مشغولة لفترة أطول.
  • تأكد من أنك تستخدم tf.function لإنشاء رسوم بيانية TensorFlow ، بحيث لا تقوم بتشغيل العمليات في وضع شغوف خالص. إذا كنت تستخدم Model.fit (على عكس حلقة تدريب مخصصة مع tf.GradientTape ) ، فإن tf.keras.Model.compile سيقوم بذلك تلقائيًا نيابة عنك.
  • صهر النواة باستخدام XLA مع tf.function(jit_compile=True) أو التجميع التلقائي. لمزيد من التفاصيل ، انتقل إلى قسم تمكين الدقة المختلطة و XLA أدناه لمعرفة كيفية تمكين XLA للحصول على أداء أعلى. يمكن أن تؤدي هذه الميزة إلى استخدام مرتفع للجهاز.
2. التنسيب TensorFlow op

تعرض لك صفحة نظرة عامة على ملف التعريف النسبة المئوية للعمليات الموضوعة على المضيف مقابل الجهاز (يمكنك أيضًا التحقق من موضع عمليات معينة من خلال النظر إلى عارض التتبع . كما هو الحال في الصورة أدناه ، تريد النسبة المئوية للعمليات على المضيف ليكون صغيرا جدا مقارنة بالجهاز.

image

من الناحية المثالية ، يجب وضع معظم عمليات الحوسبة المكثفة على وحدة معالجة الرسومات.

لمعرفة الأجهزة التي تم تعيين العمليات والموترات في نموذجك لها ، قم بتعيين tf.debugging.set_log_device_placement(True) كأول بيان من برنامجك.

لاحظ أنه في بعض الحالات ، حتى إذا قمت بتحديد op ليتم وضعه على جهاز معين ، فإن تنفيذه قد يتجاوز هذا الشرط (على سبيل المثال: tf.unique ). حتى بالنسبة لتدريب GPU الفردي ، فإن تحديد استراتيجية التوزيع ، مثل tf.distribute.OneDeviceStrategy ، يمكن أن يؤدي إلى وضع أكثر حتمية للعمليات على جهازك.

أحد أسباب وضع غالبية العمليات على وحدة معالجة الرسومات هو منع نسخ الذاكرة الزائدة بين المضيف والجهاز (يُتوقع نسخ ذاكرة لبيانات إدخال / إخراج النموذج بين المضيف والجهاز). يتم عرض مثال على النسخ المفرط في عرض التتبع أدناه في تدفقات GPU # 167 و # 168 و # 169 .

image

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

3. نواة أكثر كفاءة على وحدات معالجة الرسومات

بمجرد قبول استخدام GPU لبرنامجك ، فإن الخطوة التالية هي النظر في زيادة كفاءة نواة GPU من خلال استخدام Tensor Cores أو دمج العمليات.

1. استخدام موتر النوى

تحتوي وحدات معالجة الرسومات NVIDIA® الحديثة على أنوية Tensor متخصصة يمكنها تحسين أداء النوى المؤهلة بشكل كبير.

يمكنك استخدام إحصائيات نواة GPU الخاصة بـ TensorBoard لتصور نواة GPU المؤهلة لـ Tensor Core وأيها تستخدم Tensor Cores. يعد تمكين fp16 (راجع قسم تمكين الدقة المختلطة أدناه) إحدى الطرق لجعل نواة المصفوفة العامة المضاعفة (GEMM) لبرنامجك (عمليات matmul) تستخدم Tensor Core. تستخدم نوى GPU نوى Tensor بكفاءة عندما تكون الدقة 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. تستخدم نوى GPU نوى Tensor بكفاءة عندما تكون الدقة fp16 وأبعاد الإدخال / الإخراج قابلة للقسمة على 8 أو 16 (لـ int8).

لاحظ أنه مع cuDNN v7.6.3 والإصدارات الأحدث ، سيتم تغليف أبعاد الالتواء تلقائيًا عند الضرورة للاستفادة من Tensor Cores.

اتبع أفضل الممارسات أدناه لتعظيم فوائد أداء دقة fp16 .

1. استخدم أفضل نواة fp16

مع تمكين fp16 ، يجب أن تستخدم نوى مضاعفات المصفوفة (GEMM) الخاصة بالبرنامج إصدار fp16 المقابل الذي يستخدم Tensor Cores. ومع ذلك ، في بعض الحالات ، لا يحدث هذا ولا تواجه التسريع المتوقع من تمكين fp16 ، حيث يعود برنامجك إلى التنفيذ غير الفعال بدلاً من ذلك.

image

تعرض صفحة إحصائيات نواة GPU العمليات المؤهلة لـ Tensor Core وأي النوى تستخدم بالفعل Tensor Core الفعال. يحتوي دليل NVIDIA® حول أداء التعلم العميق على اقتراحات إضافية حول كيفية الاستفادة من Tensor Cores. بالإضافة إلى ذلك ، ستظهر فوائد استخدام fp16 أيضًا في النواة التي كانت مقيدة في السابق بالذاكرة ، حيث ستستغرق عمليات التشغيل الآن نصف الوقت.

2. تحجيم الخسارة الديناميكي مقابل الثابت

يعد قياس الفقد ضروريًا عند استخدام fp16 لمنع التدفق السفلي بسبب الدقة المنخفضة. هناك نوعان من قياس الخسارة ، ديناميكي وثابت ، وكلاهما موضح بمزيد من التفصيل في دليل الدقة المختلطة . يمكنك استخدام سياسة mixed_float16 لتمكين قياس الخسارة تلقائيًا داخل مُحسِّن Keras.

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

2. قم بتمكين XLA باستخدام وظيفة tf (jit_compile = True) أو التجميع التلقائي

كخطوة أخيرة في الحصول على أفضل أداء باستخدام وحدة معالجة رسومات واحدة ، يمكنك تجربة تمكين XLA ، والذي سيدمج العمليات ويؤدي إلى استخدام أفضل للجهاز وتقليل حجم الذاكرة. للحصول على تفاصيل حول كيفية تمكين XLA في برنامجك باستخدام tf.function(jit_compile=True) أو التجميع التلقائي ، راجع دليل XLA .

يمكنك ضبط مستوى JIT العام على -1 (إيقاف) أو 1 أو 2 . المستوى الأعلى هو أكثر عدوانية وقد يقلل من التوازي ويستخدم المزيد من الذاكرة. اضبط القيمة على 1 إذا كانت لديك قيود على الذاكرة. لاحظ أن XLA لا يعمل بشكل جيد مع النماذج ذات أشكال موتر الإدخال المتغيرة حيث أن مترجم XLA يجب أن يستمر في تجميع النوى كلما واجه أشكالًا جديدة.

2. تحسين الأداء على مضيف واحد متعدد GPU

يمكن استخدام tf.distribute.MirroredStrategy API لتوسيع نطاق تدريب النموذج من وحدة معالجة رسومات واحدة إلى وحدات معالجة رسومات متعددة على مضيف واحد. (لمعرفة المزيد حول كيفية القيام بالتدريب الموزع باستخدام TensorFlow ، ارجع إلى التدريب الموزع باستخدام TensorFlow ، واستخدام GPU ، واستخدام أدلة TPU والتدريب الموزع باستخدام Keras التعليمي.)

على الرغم من أن الانتقال من وحدة معالجة الرسومات إلى وحدات معالجة رسومات متعددة يجب أن يكون قابلاً للتطوير بشكل مثالي ، إلا أنه قد تواجه أحيانًا مشكلات في الأداء.

عند الانتقال من التدريب باستخدام وحدة معالجة رسومات واحدة إلى وحدات معالجة رسومات متعددة على نفس المضيف ، من الناحية المثالية ، يجب أن تختبر قياس الأداء من خلال الحمل الإضافي لاتصالات التدرج وزيادة استخدام مؤشر ترابط المضيف. بسبب هذا الحمل الزائد ، لن يكون لديك تسريع دقيق 2x إذا انتقلت من 1 إلى 2 GPU ، على سبيل المثال.

يُظهر عرض التتبع أدناه مثالاً على عبء الاتصال الإضافي عند التدريب على وحدات معالجة رسومات متعددة. هناك بعض الحمل لتسلسل التدرجات ، وتوصيلها عبر النسخ المتماثلة ، وتقسيمها قبل إجراء تحديث الوزن.

image

ستساعدك قائمة التحقق التالية على تحقيق أداء أفضل عند تحسين الأداء في سيناريو تعدد وحدات معالجة الرسومات:

  1. حاول زيادة حجم الدُفعة إلى الحد الأقصى ، مما سيؤدي إلى زيادة استخدام الجهاز واستهلاك تكاليف الاتصال عبر وحدات معالجة رسومات متعددة. يساعد استخدام ملف تعريف الذاكرة في التعرف على مدى قرب برنامجك من الوصول إلى ذروة استخدام الذاكرة. لاحظ أنه على الرغم من أن حجم الدُفعة الأكبر يمكن أن يؤثر على التقارب ، إلا أن مزايا الأداء تفوق هذا عادةً.
  2. عند الانتقال من وحدة معالجة رسومات واحدة إلى عدة وحدات معالجة رسومات ، يتعين على نفس المضيف الآن معالجة المزيد من بيانات الإدخال. لذلك ، بعد (1) ، يوصى بإعادة التحقق من أداء خط أنابيب الإدخال والتأكد من أنه ليس عنق الزجاجة.
  3. تحقق من المخطط الزمني لوحدة معالجة الرسومات في عرض تتبع البرنامج بحثًا عن أي مكالمات غير ضرورية من AllReduce ، حيث ينتج عن ذلك مزامنة عبر جميع الأجهزة. في عرض التتبع الموضح أعلاه ، يتم إجراء AllReduce عبر نواة NCCL ، وهناك استدعاء NCCL واحد فقط في كل وحدة معالجة رسومات (GPU) للتدرجات اللونية في كل خطوة.
  4. تحقق من عمليات نسخ D2H و H2D و D2D غير الضرورية التي يمكن تصغيرها.
  5. تحقق من وقت الخطوة للتأكد من أن كل نسخة متماثلة تقوم بنفس العمل. على سبيل المثال ، يمكن أن يحدث أن أحد GPU (عادةً ، GPU0 ) مكتظ لأن المضيف ينتهي عن طريق الخطأ بمزيد من العمل عليه.
  6. أخيرًا ، تحقق من خطوة التدريب عبر جميع وحدات معالجة الرسومات في عرض التتبع الخاص بك بحثًا عن أي عمليات يتم تنفيذها بالتسلسل. يحدث هذا عادةً عندما يتضمن برنامجك تبعيات تحكم من وحدة معالجة رسومات إلى أخرى. في الماضي ، تم حل تصحيح أخطاء الأداء في هذه الحالة على أساس كل حالة على حدة. إذا لاحظت هذا السلوك في برنامجك ، فقم بإبلاغ مشكلة 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 fp32 معلمات fp32.

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

2. الخيط مضيف GPU الخلاف

عند تشغيل العديد من وحدات معالجة الرسومات ، تتمثل مهمة وحدة المعالجة المركزية في إبقاء جميع الأجهزة مشغولة عن طريق تشغيل نوى GPU بكفاءة عبر الأجهزة.

ومع ذلك ، عندما يكون هناك الكثير من العمليات المستقلة التي يمكن لوحدة المعالجة المركزية جدولتها على وحدة معالجة رسومات واحدة ، يمكن لوحدة المعالجة المركزية أن تقرر استخدام الكثير من خيوط مضيفها لإبقاء وحدة معالجة الرسومات واحدة مشغولة ، ثم تشغيل النوى على وحدة معالجة رسومات أخرى بترتيب غير حتمي . يمكن أن يتسبب هذا في حدوث انحراف أو تحجيم سلبي ، مما قد يؤثر سلبًا على الأداء.

يُظهر عارض التتبع أدناه الحمل الزائد عندما تقوم وحدة المعالجة المركزية بإطلاق نواة GPU بشكل غير فعال ، حيث يكون GPU1 خاملاً ثم يبدأ تشغيل العمليات بعد بدء GPU2 .

image

يُظهر عرض التتبع للمضيف أن المضيف يقوم بتشغيل النواة على GPU2 قبل تشغيلها على GPU1 (لاحظ أن عمليات tf_Compute* ops أدناه لا تدل على سلاسل CPU).

image

إذا واجهت هذا النوع من المذهل لنواة GPU في عرض تتبع البرنامج الخاص بك ، فإن الإجراء الموصى به هو:

  • اضبط متغير بيئة TF_GPU_THREAD_MODE على gpu_private . سيخبر متغير البيئة هذا المضيف بالحفاظ على خصوصية مؤشرات الترابط الخاصة بوحدة معالجة الرسومات.
  • بشكل افتراضي ، TF_GPU_THREAD_MODE=gpu_private عدد سلاسل الرسائل إلى 2 ، وهو ما يكفي في معظم الحالات. ومع ذلك ، يمكن تغيير هذا الرقم عن طريق تعيين متغير بيئة TF_GPU_THREAD_COUNT إلى العدد المطلوب من مؤشرات الترابط.