عملکرد GPU TensorFlow را با TensorFlow Profiler بهینه کنید

بررسی اجمالی

این راهنما به شما نشان می دهد که چگونه از TensorFlow Profiler با TensorBoard برای به دست آوردن بینش و حداکثر عملکرد GPU های خود استفاده کنید و زمانی که یک یا چند پردازنده گرافیکی شما کمتر مورد استفاده قرار می گیرند اشکال زدایی کنید.

اگر تازه وارد Profiler هستید:

به خاطر داشته باشید که بارگیری محاسبات در GPU ممکن است همیشه مفید نباشد، به خصوص برای مدل های کوچک. ممکن است سربار وجود داشته باشد به دلیل:

  • انتقال داده بین میزبان (CPU) و دستگاه (GPU)؛ و
  • به دلیل تاخیر زمانی که میزبان هسته های GPU را راه اندازی می کند.

گردش کار بهینه سازی عملکرد

این راهنما نحوه اشکال زدایی مشکلات عملکرد را با شروع با یک GPU و سپس انتقال به یک میزبان واحد با چندین GPU نشان می دهد.

توصیه می شود مشکلات عملکرد را به ترتیب زیر اشکال زدایی کنید:

  1. بهینه سازی و رفع اشکال عملکرد در یک GPU:
    1. بررسی کنید که آیا خط لوله ورودی یک گلوگاه است.
    2. اشکال زدایی عملکرد یک GPU
    3. دقت ترکیبی را فعال کنید (با fp16 (float16)) و به صورت اختیاری XLA را فعال کنید.
  2. بهینه سازی و اشکال زدایی عملکرد در میزبان تک چند GPU.

به عنوان مثال، اگر از استراتژی توزیع TensorFlow برای آموزش یک مدل بر روی یک میزبان واحد با چندین پردازنده گرافیکی استفاده می‌کنید و متوجه استفاده از GPU کمتر از حد مطلوب می‌شوید، ابتدا باید عملکرد یک GPU را قبل از اشکال‌زدایی سیستم چند GPU بهینه کرده و اشکال‌زدایی کنید.

این راهنما به‌عنوان پایه‌ای برای دریافت کدهای عملکردی در GPUها فرض می‌کند که از tf.function استفاده می‌کنید. APIهای Keras Model.compile و Model.fit به طور خودکار از tf.function در زیر هود استفاده می کنند. هنگام نوشتن یک حلقه آموزشی سفارشی با tf.GradientTape ، به عملکرد بهتر با tf.function در مورد نحوه فعال کردن tf.function s مراجعه کنید.

بخش‌های بعدی رویکردهای پیشنهادی برای هر یک از سناریوهای بالا را مورد بحث قرار می‌دهند تا به شناسایی و رفع تنگناهای عملکرد کمک کنند.

1. عملکرد را در یک GPU بهینه کنید

در یک حالت ایده‌آل، برنامه شما باید دارای استفاده از GPU بالا، حداقل ارتباط CPU (میزبان) به GPU (دستگاه) و بدون هزینه اضافی از خط لوله ورودی باشد.

اولین گام در تجزیه و تحلیل عملکرد، دریافت نمایه ای برای مدلی است که با یک GPU اجرا می شود.

صفحه نمایه نمایه TensorBoard - که نمای سطح بالایی از عملکرد مدل شما در طول اجرای نمایه را نشان می دهد - می تواند ایده ای از فاصله برنامه شما با سناریوی ایده آل ارائه دهد.

TensorFlow Profiler Overview Page

اعداد کلیدی که باید به صفحه نمای کلی توجه کرد عبارتند از:

  1. چه مقدار از زمان گام از اجرای واقعی دستگاه است
  2. درصد عملیات قرار داده شده روی دستگاه در مقابل میزبان
  3. چند هسته از fp16 استفاده می کنند

دستیابی به عملکرد مطلوب به معنای به حداکثر رساندن این اعداد در هر سه مورد است. برای درک عمیق برنامه خود، باید با نمایشگر ردیابی پروفایل TensorBoard آشنا باشید. بخش‌های زیر برخی از الگوهای رایج بیننده ردیابی را نشان می‌دهد که باید هنگام تشخیص گلوگاه‌های عملکرد به دنبال آنها باشید.

در زیر تصویری از نمای ردیابی مدل در حال اجرا بر روی یک GPU است. از بخش‌های TensorFlow Name Scope و TensorFlow Ops ، می‌توانید بخش‌های مختلف مدل مانند پاس رو به جلو، تابع تلفات، محاسبه پاس/گرید به عقب و به‌روزرسانی وزن بهینه‌ساز را شناسایی کنید. همچنین می‌توانید عملیات‌ها را روی GPU در کنار هر Stream اجرا کنید که به جریان‌های CUDA اشاره دارد. هر جریان برای وظایف خاصی استفاده می شود. در این ردیابی، Stream#118 برای راه‌اندازی هسته‌های محاسباتی و کپی‌های دستگاه به دستگاه استفاده می‌شود. Stream#119 برای کپی میزبان به دستگاه و Stream#120 برای کپی از دستگاه به میزبان استفاده می شود.

ردیابی زیر ویژگی های مشترک یک مدل عملکردی را نشان می دهد.

image

به عنوان مثال، جدول زمانی محاسبه GPU ( Stream#118 ) با فاصله های بسیار کمی "مشغول" به نظر می رسد. حداقل نسخه ها از میزبانی به دستگاه دیگر ( جریان شماره 119 ) و از دستگاهی به میزبان دیگر ( جریان شماره 120 ) و همچنین حداقل فاصله بین مراحل وجود دارد. هنگامی که Profiler را برای برنامه خود اجرا می کنید، ممکن است نتوانید این ویژگی های ایده آل را در نمای ردیابی خود شناسایی کنید. بقیه این راهنما سناریوهای رایج و نحوه رفع آنها را پوشش می دهد.

1. خط لوله ورودی را اشکال زدایی کنید

اولین قدم در اشکال زدایی عملکرد GPU این است که تعیین کنید آیا برنامه شما محدود به ورودی است یا خیر. ساده ترین راه برای فهمیدن این موضوع استفاده از تحلیلگر خط لوله ورودی Profiler در TensorBoard است که نمای کلی از زمان صرف شده در خط لوله ورودی را ارائه می دهد.

image

اگر خط لوله ورودی شما کمک قابل توجهی به زمان گام داشته باشد، می توانید اقدامات بالقوه زیر را انجام دهید:

  • می توانید از راهنمای tf.data -specific برای یادگیری نحوه اشکال زدایی خط لوله ورودی خود استفاده کنید.
  • یک راه سریع دیگر برای بررسی اینکه آیا خط لوله ورودی گلوگاه است یا خیر، استفاده از داده های ورودی تصادفی تولید شده است که نیازی به پیش پردازش ندارد. در اینجا مثالی از استفاده از این تکنیک برای مدل ResNet آورده شده است . اگر خط لوله ورودی بهینه باشد، باید عملکرد مشابهی را با داده های واقعی و با داده های تصادفی/مصنوعی تولید شده تجربه کنید. تنها سربار در مورد داده های مصنوعی به دلیل کپی داده های ورودی است که دوباره می تواند از قبل واکشی و بهینه شود.

علاوه بر این، به بهترین روش ها برای بهینه سازی خط لوله داده های ورودی مراجعه کنید.

2. اشکال زدایی عملکرد یک GPU

عوامل متعددی وجود دارد که می تواند در استفاده کم از GPU نقش داشته باشد. در زیر چند سناریو که معمولاً هنگام نگاه کردن به نمایشگر ردیابی مشاهده می‌شوند و راه‌حل‌های بالقوه وجود دارد.

1. شکاف های بین مراحل را تجزیه و تحلیل کنید

یکی از مشاهدات رایج زمانی که برنامه شما به خوبی اجرا نمی شود، فاصله بین مراحل آموزشی است. در تصویر نمای ردیابی زیر، فاصله زیادی بین مراحل 8 و 9 وجود دارد، به این معنی که GPU در این مدت بیکار است.

image

اگر نمایشگر ردیابی شما فاصله های زیادی بین مراحل نشان می دهد، این می تواند نشانه ای باشد که برنامه شما محدود به ورودی است. در این صورت، اگر قبلاً این کار را نکرده اید، باید به بخش قبلی در مورد اشکال زدایی خط لوله ورودی خود مراجعه کنید.

با این حال، حتی با یک خط لوله ورودی بهینه شده، همچنان می توانید بین انتهای یک مرحله و شروع مرحله دیگر به دلیل اختلاف موضوع CPU شکاف داشته باشید. tf.data از رشته های پس زمینه برای موازی سازی پردازش خط لوله استفاده می کند. این رشته‌ها ممکن است در فعالیت‌های سمت میزبان GPU که در ابتدای هر مرحله اتفاق می‌افتد، مانند کپی کردن داده‌ها یا زمان‌بندی عملیات GPU، تداخل ایجاد کنند.

اگر متوجه شکاف‌های بزرگی در سمت میزبان شدید، که این عملیات‌ها را در GPU زمان‌بندی می‌کند، می‌توانید متغیر محیطی TF_GPU_THREAD_MODE=gpu_private تنظیم کنید. این تضمین می‌کند که هسته‌های GPU از رشته‌های اختصاصی خودشان راه‌اندازی می‌شوند و پشت کار tf.data در صف قرار نمی‌گیرند.

شکاف بین مراحل همچنین می‌تواند ناشی از محاسبات متریک، تماس‌های Keras یا عملیات خارج از tf.function باشد که روی میزبان اجرا می‌شوند. این عملیات ها به اندازه عملیات های داخل نمودار TensorFlow عملکرد خوبی ندارند. به‌علاوه، برخی از این عملیات‌ها بر روی CPU اجرا می‌شوند و تانسورها را به عقب و جلو از GPU کپی می‌کنند.

اگر پس از بهینه‌سازی خط لوله ورودی‌تان همچنان شکاف بین مراحل را در نمایشگر ردیابی مشاهده کردید، باید به کد مدل بین مراحل نگاه کنید و بررسی کنید که آیا غیرفعال کردن تماس‌ها/متریک‌ها عملکرد را بهبود می‌بخشد. برخی از جزئیات این عملیات ها نیز در نمایشگر ردیابی (هم در سمت دستگاه و هم در سمت میزبان) قرار دارند. توصیه در این سناریو این است که سربار این عملیات ها را با اجرای آنها پس از تعداد ثابتی از مراحل به جای هر مرحله، مستهلک کنید. هنگام استفاده از متد Model.compile در tf.keras API، تنظیم پرچم steps_per_execution این کار را به طور خودکار انجام می دهد. برای حلقه های آموزشی سفارشی، از tf.while_loop استفاده کنید.

2. دستیابی به استفاده بالاتر از دستگاه

1. هسته های گرافیکی کوچک و تاخیر در راه اندازی هسته میزبان

میزبان، هسته‌ها را در صف قرار می‌دهد تا روی GPU اجرا شوند، اما قبل از اینکه هسته‌ها واقعاً روی GPU اجرا شوند، یک تأخیر (حدود 20 تا 40 میکرو ثانیه) وجود دارد. در یک حالت ایده آل، میزبان به اندازه کافی هسته روی GPU قرار می دهد به طوری که GPU بیشتر زمان خود را صرف اجرا می کند، به جای اینکه منتظر بماند تا میزبان هسته های بیشتری را در صف قرار دهد.

صفحه نمای کلی Profiler در TensorBoard نشان می‌دهد که GPU به دلیل انتظار میزبان برای راه‌اندازی هسته‌ها، چقدر زمان بی‌کار بوده است. در تصویر زیر، GPU حدود 10 درصد از زمان گام در انتظار راه‌اندازی هسته‌ها، بیکار است.

image

نمایشگر ردیابی برای همین برنامه، شکاف‌های کوچکی را بین هسته‌ها نشان می‌دهد که میزبان مشغول راه‌اندازی هسته‌ها در GPU است.

image

با راه‌اندازی بسیاری از عملیات‌های کوچک روی GPU (مثلاً یک افزودن اسکالر)، میزبان ممکن است با GPU هماهنگ نباشد. ابزار TensorFlow Stats در TensorBoard برای همان Profile 126224 عملیات Mul را نشان می دهد که 2.77 ثانیه طول می کشد. بنابراین، هر هسته حدود 21.9 میکرو ثانیه است که بسیار کوچک است (تقریباً همزمان با تأخیر راه‌اندازی) و به طور بالقوه می‌تواند منجر به تأخیر راه‌اندازی هسته میزبان شود.

image

اگر نمایشگر ردیابی شما شکاف‌های کوچک زیادی بین عملیات گرافیکی مانند تصویر بالا نشان می‌دهد، می‌توانید:

  • تانسورهای کوچک را به هم متصل کنید و از عملیات بردار استفاده کنید یا از یک اندازه دسته بزرگتر استفاده کنید تا هر هسته راه اندازی شده کار بیشتری انجام دهد، که باعث می شود GPU برای مدت بیشتری مشغول بماند.
  • مطمئن شوید که tf.function برای ایجاد نمودارهای TensorFlow استفاده می‌کنید تا عملیات‌ها را در حالت مشتاق خالص اجرا نکنید. اگر از Model.fit استفاده می کنید (برخلاف یک حلقه آموزشی سفارشی با tf.GradientTape )، سپس tf.keras.Model.compile به طور خودکار این کار را برای شما انجام می دهد.
  • هسته‌ها را با استفاده از XLA با tf.function(jit_compile=True) یا خوشه‌بندی خودکار فیوز کنید. برای جزئیات بیشتر، به بخش Enable mixed precision and XLA در زیر بروید تا یاد بگیرید چگونه XLA را برای دستیابی به عملکرد بالاتر فعال کنید. این ویژگی می تواند منجر به استفاده زیاد از دستگاه شود.
2. قرار دادن عملیات TensorFlow

صفحه نمای کلی Profiler درصد عملیات انجام شده بر روی هاست در مقابل دستگاه را به شما نشان می دهد (همچنین می توانید با نگاه کردن به نمایشگر ردیابی ، قرار گرفتن عملیات های خاص را تأیید کنید. مانند تصویر زیر، درصد عملیات روی هاست را می خواهید نسبت به دستگاه بسیار کوچک باشد.

image

در حالت ایده آل، بیشتر عملیات فشرده محاسباتی باید روی GPU قرار گیرد.

برای اینکه بدانید عملیات و تانسورهای مدل شما به کدام دستگاه ها اختصاص داده شده اند، tf.debugging.set_log_device_placement(True) به عنوان اولین دستور برنامه خود تنظیم کنید.

توجه داشته باشید که در برخی موارد، حتی اگر یک op را برای قرار دادن در یک دستگاه خاص مشخص کنید، اجرای آن ممکن است این شرط را لغو کند (مثال: tf.unique ). حتی برای آموزش تک GPU، مشخص کردن یک استراتژی توزیع، مانند tf.distribute.OneDeviceStrategy ، می‌تواند منجر به قرار دادن قطعی‌تر عملیات در دستگاه شما شود.

یکی از دلایلی که اکثر عملیات‌ها روی GPU قرار می‌گیرند، جلوگیری از کپی‌های حافظه بیش از حد بین میزبان و دستگاه است (انتظار می‌رود نسخه‌های حافظه برای داده‌های ورودی/خروجی مدل بین میزبان و دستگاه وجود داشته باشد). نمونه ای از کپی بیش از حد در نمای ردیابی زیر در جریان های GPU #167 ، #168 و #169 نشان داده شده است.

image

این کپی ها در صورتی که اجرای هسته های GPU را مسدود کنند، گاهی اوقات می توانند به عملکرد آسیب بزنند. عملیات کپی حافظه در نمایشگر ردیابی اطلاعات بیشتری در مورد عملیاتی دارد که منبع این تانسورهای کپی شده هستند، اما ممکن است همیشه آسان نباشد که یک memCopy را با یک op مرتبط کنیم. در این موارد، نگاه کردن به عملیات نزدیک برای بررسی اینکه آیا کپی حافظه در هر مرحله در همان مکان اتفاق می‌افتد، مفید است.

3. هسته های کارآمدتر در پردازنده های گرافیکی

هنگامی که استفاده از GPU برنامه شما قابل قبول است، گام بعدی این است که با استفاده از Tensor Cores یا ترکیب عملیات به افزایش کارایی هسته‌های GPU توجه کنید.

1. از Tensor Cores استفاده کنید

پردازنده‌های گرافیکی مدرن NVIDIA دارای هسته‌های Tensor تخصصی هستند که می‌توانند عملکرد هسته‌های واجد شرایط را به میزان قابل توجهی بهبود بخشند.

می‌توانید از آمار هسته GPU TensorBoard برای تجسم اینکه کدام هسته‌های GPU واجد شرایط Tensor Core هستند و کدام هسته‌ها از Tensor Cores استفاده می‌کنند استفاده کنید. فعال کردن fp16 (به بخش فعال کردن دقت ترکیبی در زیر مراجعه کنید) یکی از راه‌هایی است که می‌توانید هسته‌های ضرب ماتریس عمومی (GEMM) برنامه شما (matmul ops) از 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 استفاده کنند و در مقایسه با استفاده از دقت fp32 (float32) در Volta و معماری‌های جدیدتر GPU، تا 3 برابر سرعت کلی را افزایش دهند.

اطمینان حاصل کنید که ابعاد ماتریس/تانسور الزامات فراخوانی هسته‌هایی را که از Tensor Cores استفاده می‌کنند برآورده می‌کند. هسته‌های GPU از هسته‌های Tensor زمانی که دقت fp16 است و ابعاد ورودی/خروجی بر 8 یا 16 (برای int8) تقسیم می‌شوند، به طور موثر استفاده می‌کنند.

توجه داشته باشید که با cuDNN نسخه 7.6.3 و نسخه های جدیدتر، ابعاد پیچیدگی به طور خودکار در صورت لزوم برای استفاده از هسته های تانسور اضافه می شود.

برای به حداکثر رساندن مزایای عملکرد دقت fp16 ، بهترین شیوه های زیر را دنبال کنید.

1. از هسته های بهینه fp16 استفاده کنید

با فعال بودن fp16 ، هسته های ضرب ماتریس (GEMM) برنامه شما باید از نسخه fp16 مربوطه استفاده کند که از هسته های Tensor استفاده می کند. با این حال، در برخی موارد، این اتفاق نمی‌افتد و سرعت مورد انتظار را از فعال کردن fp16 تجربه نمی‌کنید، زیرا برنامه شما به جای اجرای ناکارآمد بازمی‌گردد.

image

صفحه آمار هسته GPU نشان می دهد که کدام عملیات Tensor Core واجد شرایط هستند و کدام هسته ها واقعاً از Tensor Core کارآمد استفاده می کنند. راهنمای NVIDIA® در مورد عملکرد یادگیری عمیق حاوی پیشنهادهای اضافی در مورد نحوه استفاده از Tensor Cores است. علاوه بر این، مزایای استفاده از fp16 در هسته هایی که قبلاً به حافظه محدود شده بودند نیز نشان داده می شود، زیرا اکنون عملیات نیمی از زمان را می گیرد.

2. مقیاس بندی تلفات پویا در مقابل استاتیک

هنگام استفاده از fp16 برای جلوگیری از ریزش به دلیل دقت کم، پوسته پوسته شدن از دست دادن ضروری است. دو نوع مقیاس بندی تلفات وجود دارد، پویا و ایستا، که هر دو با جزئیات بیشتر در راهنمای دقت ترکیبی توضیح داده شده اند. می توانید از خط مشی mixed_float16 برای فعال کردن خودکار مقیاس ضرر در بهینه ساز Keras استفاده کنید.

هنگام تلاش برای بهینه‌سازی عملکرد، مهم است که به یاد داشته باشید که مقیاس‌گذاری پویا از دست دادن می‌تواند عملیات شرطی اضافی را که روی میزبان اجرا می‌شوند، معرفی کند و منجر به شکاف‌هایی شود که بین مراحل در نمایشگر ردیابی قابل مشاهده است. از طرفی مقیاس تلفات استاتیک چنین سرباری ندارد و می تواند از نظر کارایی گزینه بهتری با گیره باشد که برای تعیین مقدار صحیح مقیاس تلفات استاتیکی نیاز دارید.

2. XLA را با tf.function (jit_compile=True) یا خوشه بندی خودکار فعال کنید

به عنوان آخرین گام برای به دست آوردن بهترین عملکرد با یک GPU، می‌توانید XLA را فعال کنید، که عملیات را ترکیب می‌کند و منجر به استفاده بهتر از دستگاه و ردپای حافظه کمتر می‌شود. برای جزئیات در مورد نحوه فعال کردن XLA در برنامه خود با tf.function(jit_compile=True) یا خوشه بندی خودکار، به راهنمای XLA مراجعه کنید.

می توانید سطح JIT جهانی را روی -1 (خاموش)، 1 یا 2 تنظیم کنید. سطح بالاتر تهاجمی تر است و ممکن است موازی بودن را کاهش دهد و از حافظه بیشتر استفاده کند. اگر محدودیت حافظه دارید، مقدار را روی 1 تنظیم کنید. توجه داشته باشید که XLA برای مدل‌هایی با اشکال تانسور ورودی متغیر عملکرد خوبی ندارد زیرا کامپایلر XLA باید هر زمان که با اشکال جدید مواجه می‌شود، هسته‌ها را کامپایل کند.

2. عملکرد را در هاست تک چند GPU بهینه کنید

tf.distribute.MirroredStrategy API را می توان برای مقیاس بندی آموزش مدل از یک GPU به چندین GPU در یک میزبان استفاده کرد. (برای کسب اطلاعات بیشتر در مورد نحوه انجام آموزش توزیع شده با TensorFlow، به آموزش Distributed with TensorFlow ، راهنمای استفاده از GPU و استفاده از TPUs و آموزش توزیع شده با Keras مراجعه کنید.)

اگرچه انتقال از یک GPU به چندین GPU در حالت ایده آل باید مقیاس پذیر باشد، اما گاهی اوقات ممکن است با مشکلات عملکردی مواجه شوید.

هنگامی که از آموزش با یک GPU واحد به چندین GPU در یک میزبان می روید، در حالت ایده آل باید مقیاس عملکرد را تنها با هزینه های اضافی ارتباط گرادیان و افزایش استفاده از رشته میزبان تجربه کنید. به دلیل این سربار، اگر مثلاً از 1 به 2 GPU جابجا شوید، سرعت دقیق 2 برابری نخواهید داشت.

نمای ردیابی زیر نمونه‌ای از هزینه‌های ارتباطی اضافی را هنگام آموزش روی چندین GPU نشان می‌دهد. مقداری سربار برای به هم پیوستن شیب ها، برقراری ارتباط بین ماکت ها و تقسیم آنها قبل از انجام به روز رسانی وزن وجود دارد.

image

چک لیست زیر به شما کمک می کند هنگام بهینه سازی عملکرد در سناریوی چند GPU به عملکرد بهتری دست یابید:

  1. سعی کنید اندازه دسته ای را به حداکثر برسانید، که منجر به استفاده بیشتر از دستگاه و کاهش هزینه های ارتباط بین چندین GPU می شود. استفاده از نمایه ساز حافظه کمک می کند تا متوجه شوید برنامه شما تا چه حد به حداکثر استفاده از حافظه نزدیک است. توجه داشته باشید که در حالی که اندازه دسته‌ای بالاتر می‌تواند بر همگرایی تأثیر بگذارد، معمولاً با مزایای عملکرد غلبه می‌کند.
  2. هنگامی که از یک GPU واحد به چندین GPU منتقل می شوید، همان میزبان اکنون باید داده های ورودی بسیار بیشتری را پردازش کند. بنابراین، پس از (1)، توصیه می شود عملکرد خط لوله ورودی را دوباره بررسی کنید و مطمئن شوید که گلوگاه نیست.
  3. جدول زمانی GPU را در نمای ردیابی برنامه خود برای تماس های غیرضروری AllReduce بررسی کنید، زیرا این امر منجر به همگام سازی در همه دستگاه ها می شود. در نمای ردیابی که در بالا نشان داده شده است، AllReduce از طریق هسته NCCL انجام می شود و تنها یک فراخوانی NCCL در هر GPU برای گرادیان های هر مرحله وجود دارد.
  4. عملیات کپی غیرضروری D2H، H2D و D2D را که می توانند به حداقل برسانند، بررسی کنید.
  5. زمان مرحله را بررسی کنید تا مطمئن شوید که هر ماکت همان کار را انجام می دهد. برای مثال، ممکن است اتفاق بیفتد که یک GPU (معمولاً GPU0 ) بیش از حد اشتراک داشته باشد زیرا میزبان به اشتباه کار بیشتری روی آن انجام می دهد.
  6. در نهایت، مرحله آموزش را در تمام پردازنده‌های گرافیکی در نمای ردیابی خود برای هر عملیاتی که به صورت متوالی اجرا می‌شوند بررسی کنید. این معمولا زمانی اتفاق می افتد که برنامه شما شامل وابستگی های کنترلی از یک GPU به GPU دیگر باشد. در گذشته، اشکال زدایی عملکرد در این شرایط به صورت موردی حل شده است. اگر این رفتار را در برنامه خود مشاهده کردید، مشکل GitHub را با تصاویر نمای ردیابی خود ثبت کنید .

1. گرادیان AllReduce را بهینه کنید

هنگام آموزش با یک استراتژی همزمان، هر دستگاه بخشی از داده های ورودی را دریافت می کند.

پس از محاسبه گذرهای جلو و عقب از طریق مدل، گرادیان های محاسبه شده در هر دستگاه باید جمع و کاهش یابد. این گرادیان AllReduce پس از محاسبه گرادیان در هر دستگاه و قبل از به‌روزرسانی وزن‌های مدل توسط بهینه‌ساز انجام می‌شود.

هر GPU ابتدا گرادیان ها را در لایه های مدل به هم متصل می کند، آنها را با استفاده از tf.distribute.CrossDeviceOps در میان GPU ها ارتباط برقرار می کند ( tf.distribute.NcclAllReduce پیش فرض است)، و سپس پس از کاهش در هر لایه، گرادیان ها را برمی گرداند.

بهینه ساز از این شیب های کاهش یافته برای به روز رسانی وزن مدل شما استفاده می کند. در حالت ایده‌آل، این فرآیند باید به طور همزمان در همه پردازنده‌های گرافیکی اتفاق بیفتد تا از هر گونه سربار جلوگیری شود.

زمان AllReduce باید تقریباً برابر با:

(number of parameters * 4bytes)/ (communication bandwidth)

این محاسبه به عنوان یک بررسی سریع برای درک اینکه آیا عملکردی که هنگام اجرای یک کار آموزشی توزیع شده دارید، مطابق انتظار است یا اینکه نیاز به انجام اشکال زدایی عملکرد بیشتر دارید مفید است. می توانید تعداد پارامترهای مدل خود را از Model.summary دریافت کنید.

توجه داشته باشید که اندازه هر پارامتر مدل 4 بایت است زیرا TensorFlow از fp32 (float32) برای برقراری ارتباط با گرادیان ها استفاده می کند. حتی زمانی که fp16 فعال کرده اید، NCCL AllReduce از پارامترهای fp32 استفاده می کند.

برای به دست آوردن مزایای مقیاس بندی، زمان گام باید در مقایسه با این هزینه ها بسیار بیشتر باشد. یکی از راه‌های رسیدن به این هدف، استفاده از اندازه دسته بالاتر است، زیرا اندازه دسته بر زمان گام تأثیر می‌گذارد، اما بر سربار ارتباط تأثیر نمی‌گذارد.

2. اختلاف موضوع میزبان GPU

هنگام اجرای چندین GPU، وظیفه CPU این است که با راه‌اندازی مؤثر هسته‌های GPU در سراسر دستگاه‌ها، همه دستگاه‌ها را مشغول نگه دارد.

با این حال، هنگامی که تعداد زیادی عملیات مستقل وجود دارد که CPU می تواند روی یک GPU برنامه ریزی کند، CPU می تواند تصمیم بگیرد که از بسیاری از رشته های میزبان خود برای مشغول نگه داشتن یک GPU استفاده کند و سپس هسته ها را روی GPU دیگری به ترتیب غیر قطعی راه اندازی کند. . این می تواند باعث کج شدن یا پوسته پوسته شدن منفی شود که می تواند بر عملکرد تأثیر منفی بگذارد.

نمایشگر ردیابی زیر سربار را نشان می‌دهد زمانی که CPU به‌طور ناکارآمد راه‌اندازی هسته GPU را تکان می‌دهد، زیرا GPU1 بی‌حرکت است و پس از شروع GPU2 شروع به اجرای عملیات می‌کند.

image

نمای ردیابی هاست نشان می‌دهد که میزبان هسته‌ها را قبل از راه‌اندازی در GPU2 روی GPU1 راه‌اندازی می‌کند (توجه داشته باشید که عملیات tf_Compute* زیر نشان‌دهنده رشته‌های CPU نیست).

image

اگر در نمای ردیابی برنامه خود با این نوع تلنبار شدن هسته های GPU مواجه شدید، اقدام توصیه شده این است:

  • متغیر محیطی TensorFlow TF_GPU_THREAD_MODE را روی gpu_private تنظیم کنید. این متغیر محیطی به میزبان می‌گوید که رشته‌های یک GPU را خصوصی نگه دارد.
  • به طور پیش فرض، TF_GPU_THREAD_MODE=gpu_private تعداد thread ها را 2 تنظیم می کند که در بیشتر موارد کافی است. با این حال، این عدد را می توان با تنظیم متغیر محیطی TensorFlow TF_GPU_THREAD_COUNT روی تعداد نخ های دلخواه تغییر داد.