Optimalkan performa GPU TensorFlow dengan TensorFlow Profiler

Ringkasan

Panduan ini akan menunjukkan cara menggunakan TensorFlow Profiler dengan TensorBoard untuk mendapatkan wawasan dan mendapatkan performa maksimal dari GPU Anda, serta melakukan debug ketika satu atau beberapa GPU Anda kurang dimanfaatkan.

Jika Anda baru mengenal Profiler:

Perlu diingat bahwa memindahkan komputasi ke GPU mungkin tidak selalu bermanfaat, terutama untuk model kecil. Mungkin ada overhead karena:

  • Transfer data antara host (CPU) dan perangkat (GPU); Dan
  • Karena latensi yang terjadi saat host meluncurkan kernel GPU.

Alur kerja pengoptimalan kinerja

Panduan ini menguraikan cara men-debug masalah kinerja dimulai dengan satu GPU, lalu berpindah ke satu host dengan banyak GPU.

Disarankan untuk men-debug masalah kinerja dalam urutan berikut:

  1. Optimalkan dan debug kinerja pada satu GPU:
    1. Periksa apakah jalur pipa masukan mengalami kemacetan.
    2. Debug kinerja satu GPU.
    3. Aktifkan presisi campuran (dengan fp16 (float16)) dan aktifkan XLA secara opsional.
  2. Optimalkan dan debug kinerja pada host tunggal multi-GPU.

Misalnya, jika Anda menggunakan strategi distribusi TensorFlow untuk melatih model pada satu host dengan beberapa GPU dan melihat penggunaan GPU yang kurang optimal, Anda harus terlebih dahulu mengoptimalkan dan men-debug performa untuk satu GPU sebelum melakukan debug pada sistem multi-GPU.

Sebagai dasar untuk mendapatkan kode performa pada GPU, panduan ini mengasumsikan Anda sudah menggunakan tf.function . API Keras Model.compile dan Model.fit akan menggunakan tf.function secara otomatis. Saat menulis loop pelatihan khusus dengan tf.GradientTape , lihat Performa lebih baik dengan tf.function tentang cara mengaktifkan tf.function s.

Bagian berikutnya membahas pendekatan yang disarankan untuk setiap skenario di atas guna membantu mengidentifikasi dan memperbaiki hambatan kinerja.

1. Optimalkan kinerja pada satu GPU

Idealnya, program Anda harus memiliki penggunaan GPU yang tinggi, komunikasi CPU (host) ke GPU (perangkat) yang minimal, dan tidak ada overhead dari pipeline input.

Langkah pertama dalam menganalisis performa adalah mendapatkan profil untuk model yang berjalan dengan satu GPU.

Halaman ringkasan Profiler TensorBoard —yang menampilkan tampilan tingkat atas tentang performa model Anda selama menjalankan profil—dapat memberikan gambaran tentang seberapa jauh program Anda dari skenario ideal.

TensorFlow Profiler Overview Page

Angka-angka penting yang perlu diperhatikan pada halaman ikhtisar adalah:

  1. Berapa banyak waktu langkah dari eksekusi perangkat sebenarnya
  2. Persentase operasi yang ditempatkan pada perangkat vs host
  3. Berapa banyak kernel yang menggunakan fp16

Mencapai kinerja optimal berarti memaksimalkan angka-angka ini dalam ketiga kasus tersebut. Untuk mendapatkan pemahaman mendalam tentang program Anda, Anda harus memahami penampil jejak Profiler TensorBoard. Bagian di bawah ini menunjukkan beberapa pola penampil jejak umum yang harus Anda perhatikan saat mendiagnosis hambatan kinerja.

Di bawah ini adalah gambar tampilan jejak model yang berjalan pada satu GPU. Dari bagian TensorFlow Name Scope dan TensorFlow Ops , Anda dapat mengidentifikasi berbagai bagian model, seperti forward pass, fungsi kerugian, penghitungan backward pass/gradien, dan pembaruan bobot pengoptimal. Anda juga dapat menjalankan operasi pada GPU di samping setiap Stream , yang mengacu pada aliran CUDA. Setiap aliran digunakan untuk tugas tertentu. Dalam jejak ini, Stream#118 digunakan untuk meluncurkan kernel komputasi dan salinan perangkat-ke-perangkat. Stream#119 digunakan untuk penyalinan host-ke-perangkat dan Stream#120 untuk penyalinan perangkat ke host.

Jejak di bawah menunjukkan karakteristik umum model berperforma tinggi.

image

Misalnya, garis waktu komputasi GPU ( Stream#118 ) terlihat "sibuk" dengan sedikit celah. Terdapat salinan minimal dari host ke perangkat ( Stream #119 ) dan dari perangkat ke host ( Stream #120 ), serta kesenjangan minimal antar langkah. Saat Anda menjalankan Profiler untuk program Anda, Anda mungkin tidak dapat mengidentifikasi karakteristik ideal ini dalam tampilan jejak Anda. Panduan selanjutnya mencakup skenario umum dan cara memperbaikinya.

1. Debug jalur masukan

Langkah pertama dalam debugging kinerja GPU adalah menentukan apakah program Anda terikat pada input. Cara termudah untuk mengetahuinya adalah dengan menggunakan penganalisa Input-pipeline Profiler, di TensorBoard, yang memberikan gambaran umum tentang waktu yang dihabiskan dalam pipeline input.

image

Anda dapat mengambil tindakan potensial berikut jika saluran masukan Anda berkontribusi secara signifikan terhadap waktu langkah:

  • Anda dapat menggunakan panduan khusus tf.data untuk mempelajari cara men-debug pipeline input Anda.
  • Cara cepat lainnya untuk memeriksa apakah jalur pipa masukan mengalami hambatan adalah dengan menggunakan data masukan yang dihasilkan secara acak yang tidak memerlukan pra-pemrosesan apa pun. Berikut adalah contoh penggunaan teknik ini untuk model ResNet. Jika saluran masukan optimal, Anda akan merasakan performa serupa dengan data nyata dan dengan data acak/sintetis yang dihasilkan. Satu-satunya overhead dalam kasus data sintetis adalah karena salinan data masukan yang sekali lagi dapat diambil sebelumnya dan dioptimalkan.

Selain itu, lihat praktik terbaik untuk mengoptimalkan saluran data masukan .

2. Debug kinerja salah satu GPU

Ada beberapa faktor yang dapat menyebabkan rendahnya pemanfaatan GPU. Berikut adalah beberapa skenario yang umum diamati saat melihat penampil jejak dan solusi potensial.

1. Analisis kesenjangan antar langkah

Pengamatan umum ketika program Anda tidak berjalan optimal adalah kesenjangan antar langkah pelatihan. Pada gambar tampilan jejak di bawah, terdapat celah besar antara langkah 8 dan 9, artinya GPU dalam keadaan idle selama waktu tersebut.

image

Jika penampil jejak Anda menunjukkan kesenjangan yang besar antar langkah, ini bisa menjadi indikasi bahwa program Anda terikat pada input. Dalam hal ini, Anda harus merujuk ke bagian sebelumnya tentang men-debug pipeline input Anda jika Anda belum melakukannya.

Namun, bahkan dengan saluran masukan yang dioptimalkan, Anda masih dapat memiliki kesenjangan antara akhir satu langkah dan awal langkah lainnya karena pertentangan thread CPU. tf.data memanfaatkan thread latar belakang untuk memparalelkan pemrosesan pipeline. Thread ini dapat mengganggu aktivitas sisi host GPU yang terjadi di awal setiap langkah, seperti menyalin data atau menjadwalkan operasi GPU.

Jika Anda melihat kesenjangan besar di sisi host, yang menjadwalkan operasi ini pada GPU, Anda dapat mengatur variabel lingkungan TF_GPU_THREAD_MODE=gpu_private . Hal ini memastikan bahwa kernel GPU diluncurkan dari thread khusus mereka sendiri, dan tidak antri di belakang pekerjaan tf.data .

Kesenjangan antar langkah juga dapat disebabkan oleh penghitungan metrik, callback Keras, atau operasi di luar tf.function yang berjalan di host. Operasi ini tidak memiliki performa sebaik operasi di dalam grafik TensorFlow. Selain itu, beberapa operasi ini berjalan di CPU dan menyalin tensor bolak-balik dari GPU.

Jika setelah mengoptimalkan saluran masukan Anda masih melihat kesenjangan antar langkah di penampil jejak, Anda harus melihat kode model di antara langkah-langkah dan memeriksa apakah menonaktifkan callback/metrik akan meningkatkan kinerja. Beberapa detail operasi ini juga ada di penampil jejak (baik sisi perangkat maupun host). Rekomendasi dalam skenario ini adalah mengamortisasi overhead operasi ini dengan mengeksekusinya setelah sejumlah langkah tetap, bukan setiap langkah. Saat menggunakan metode Model.compile di tf.keras API, menyetel steps_per_execution akan melakukannya secara otomatis. Untuk loop pelatihan khusus, gunakan tf.while_loop .

2. Mencapai pemanfaatan perangkat yang lebih tinggi

1. Kernel GPU kecil dan penundaan peluncuran kernel host

Host mengantrekan kernel untuk dijalankan di GPU, namun terdapat latensi (sekitar 20-40 μs) sebelum kernel benar-benar dieksekusi di GPU. Idealnya, host mengantrekan cukup banyak kernel pada GPU sehingga GPU menghabiskan sebagian besar waktunya untuk mengeksekusi, dibandingkan menunggu host mengantrekan lebih banyak kernel.

Halaman ikhtisar Profiler di TensorBoard menunjukkan berapa lama GPU menganggur karena menunggu host meluncurkan kernel. Pada gambar di bawah, GPU menganggur sekitar 10% dari waktu menunggu kernel diluncurkan.

image

Penampil jejak untuk program yang sama ini menunjukkan celah kecil antar kernel saat host sibuk meluncurkan kernel pada GPU.

image

Dengan meluncurkan banyak operasi kecil pada GPU (seperti penambahan skalar, misalnya), host mungkin tidak dapat mengimbangi GPU tersebut. Alat TensorFlow Stats di TensorBoard untuk Profil yang sama menunjukkan operasi 126.224 Mul yang membutuhkan waktu 2,77 detik. Jadi, setiap kernel memiliki waktu sekitar 21,9 μs, yang merupakan angka yang sangat kecil (kira-kira sama dengan latensi peluncuran) dan berpotensi mengakibatkan penundaan peluncuran kernel host.

image

Jika penampil jejak Anda menunjukkan banyak celah kecil antar operasi pada GPU seperti pada gambar di atas, Anda dapat:

  • Gabungkan tensor kecil dan gunakan operasi vektor atau gunakan ukuran batch yang lebih besar untuk membuat setiap kernel yang diluncurkan melakukan lebih banyak pekerjaan, yang akan membuat GPU sibuk lebih lama.
  • Pastikan Anda menggunakan tf.function untuk membuat grafik TensorFlow, sehingga Anda tidak menjalankan operasi dalam mode yang sangat bersemangat. Jika Anda menggunakan Model.fit (sebagai lawan dari loop pelatihan khusus dengan tf.GradientTape ), maka tf.keras.Model.compile akan secara otomatis melakukan ini untuk Anda.
  • Gabungkan kernel menggunakan XLA dengan tf.function(jit_compile=True) atau pengelompokan otomatis. Untuk detail lebih lanjut, buka bagian Aktifkan presisi campuran dan XLA di bawah untuk mempelajari cara mengaktifkan XLA untuk mendapatkan kinerja yang lebih tinggi. Fitur ini dapat menyebabkan pemanfaatan perangkat yang tinggi.
2. Penempatan operasi TensorFlow

Halaman ikhtisar Profiler menunjukkan persentase operasi yang ditempatkan pada host vs. perangkat (Anda juga dapat memverifikasi penempatan operasi tertentu dengan melihat penampil jejak . Seperti pada gambar di bawah, Anda menginginkan persentase operasi pada host menjadi sangat kecil dibandingkan dengan perangkat.

image

Idealnya, sebagian besar operasi komputasi intensif harus ditempatkan pada GPU.

Untuk mengetahui perangkat mana yang ditugaskan untuk operasi dan tensor dalam model Anda, tetapkan tf.debugging.set_log_device_placement(True) sebagai pernyataan pertama program Anda.

Perhatikan bahwa dalam beberapa kasus, meskipun Anda menentukan operasi untuk ditempatkan pada perangkat tertentu, penerapannya mungkin mengesampingkan kondisi ini (contoh: tf.unique ). Bahkan untuk pelatihan GPU tunggal, menentukan strategi distribusi, seperti tf.distribute.OneDeviceStrategy , dapat menghasilkan penempatan operasi yang lebih deterministik pada perangkat Anda.

Salah satu alasan untuk menempatkan sebagian besar operasi pada GPU adalah untuk mencegah salinan memori yang berlebihan antara host dan perangkat (diharapkan salinan memori untuk data input/output model antara host dan perangkat). Contoh penyalinan berlebihan ditunjukkan pada tampilan jejak di bawah pada aliran GPU #167 , #168 , dan #169 .

image

Salinan ini terkadang dapat mengganggu kinerja jika memblokir eksekusi kernel GPU. Operasi penyalinan memori di penampil jejak memiliki lebih banyak informasi tentang operasi yang merupakan sumber tensor yang disalin ini, namun mungkin tidak selalu mudah untuk mengaitkan memCopy dengan operasi. Dalam kasus ini, akan sangat membantu jika kita melihat operasi terdekat untuk memeriksa apakah penyalinan memori terjadi di lokasi yang sama di setiap langkah.

3. Kernel yang lebih efisien pada GPU

Setelah pemanfaatan GPU program Anda dapat diterima, langkah selanjutnya adalah mempertimbangkan peningkatan efisiensi kernel GPU dengan memanfaatkan Tensor Cores atau operasi fusi.

1. Memanfaatkan Inti Tensor

GPU NVIDIA® modern memiliki Tensor Core khusus yang dapat meningkatkan performa kernel yang memenuhi syarat secara signifikan.

Anda dapat menggunakan statistik kernel GPU TensorBoard untuk memvisualisasikan kernel GPU mana yang memenuhi syarat Tensor Core, dan kernel mana yang menggunakan Tensor Cores. Mengaktifkan fp16 (lihat bagian Mengaktifkan Presisi Campuran di bawah) adalah salah satu cara untuk membuat kernel General Matrix Multiply (GEMM) (matmul ops) program Anda memanfaatkan Tensor Core. Kernel GPU menggunakan Tensor Cores secara efisien ketika presisinya fp16 dan dimensi tensor input/output habis dibagi 8 atau 16 (untuk int8 ).

Untuk rekomendasi mendetail lainnya tentang cara membuat kernel efisien untuk GPU, lihat panduan performa deep learning NVIDIA® .

2. Operasi sekering

Gunakan tf.function(jit_compile=True) untuk menggabungkan operasi yang lebih kecil guna membentuk kernel yang lebih besar sehingga menghasilkan peningkatan kinerja yang signifikan. Untuk mempelajari lebih lanjut, lihat panduan XLA .

3. Aktifkan presisi campuran dan XLA

Setelah mengikuti langkah-langkah di atas, mengaktifkan presisi campuran dan XLA adalah dua langkah opsional yang dapat Anda ambil untuk lebih meningkatkan kinerja. Pendekatan yang disarankan adalah dengan mengaktifkannya satu per satu dan memverifikasi bahwa manfaat kinerjanya sesuai dengan yang diharapkan.

1. Aktifkan presisi campuran

Panduan presisi Campuran TensorFlow menunjukkan cara mengaktifkan presisi fp16 pada GPU. Aktifkan AMP pada GPU NVIDIA® untuk menggunakan Tensor Cores dan mewujudkan percepatan keseluruhan hingga 3x jika dibandingkan dengan hanya menggunakan presisi fp32 (float32) pada Volta dan arsitektur GPU yang lebih baru.

Pastikan dimensi matriks/tensor memenuhi persyaratan untuk memanggil kernel yang menggunakan Tensor Cores. Kernel GPU menggunakan Tensor Cores secara efisien ketika presisinya fp16 dan dimensi input/output habis dibagi 8 atau 16 (untuk int8).

Perhatikan bahwa dengan cuDNN v7.6.3 dan yang lebih baru, dimensi konvolusi akan otomatis diisi jika diperlukan untuk memanfaatkan Tensor Cores.

Ikuti praktik terbaik di bawah ini untuk memaksimalkan manfaat kinerja presisi fp16 .

1. Gunakan kernel fp16 yang optimal

Dengan mengaktifkan fp16 , kernel perkalian matriks (GEMM) program Anda, harus menggunakan versi fp16 yang sesuai yang menggunakan Tensor Cores. Namun, dalam beberapa kasus, hal ini tidak terjadi dan Anda tidak mengalami percepatan yang diharapkan dari pengaktifan fp16 , karena program Anda malah kembali ke implementasi yang tidak efisien.

image

Halaman statistik kernel GPU menunjukkan operasi mana yang memenuhi syarat Tensor Core dan kernel mana yang benar-benar menggunakan Tensor Core yang efisien. Panduan NVIDIA® tentang performa deep learning berisi saran tambahan tentang cara memanfaatkan Tensor Cores. Selain itu, manfaat penggunaan fp16 juga akan terlihat pada kernel yang sebelumnya terikat memori, karena sekarang operasinya akan memakan separuh waktu.

2. Penskalaan kerugian dinamis vs. statis

Penskalaan kerugian diperlukan saat menggunakan fp16 untuk mencegah underflow karena presisi rendah. Ada dua jenis penskalaan kerugian, dinamis dan statis, keduanya dijelaskan lebih detail dalam panduan Presisi Campuran . Anda dapat menggunakan kebijakan mixed_float16 untuk secara otomatis mengaktifkan penskalaan kerugian dalam pengoptimal Keras.

Saat mencoba mengoptimalkan kinerja, penting untuk diingat bahwa penskalaan kerugian dinamis dapat menyebabkan operasi bersyarat tambahan yang berjalan di host, dan menyebabkan kesenjangan yang akan terlihat di antara langkah-langkah di penampil jejak. Di sisi lain, penskalaan kerugian statis tidak memiliki overhead seperti itu dan dapat menjadi pilihan yang lebih baik dalam hal kinerja dengan batasan yang Anda perlukan untuk menentukan nilai skala kerugian statis yang benar.

2. Aktifkan XLA dengan tf.function(jit_compile=True) atau pengelompokan otomatis

Sebagai langkah terakhir untuk mendapatkan kinerja terbaik dengan satu GPU, Anda dapat bereksperimen dengan mengaktifkan XLA, yang akan menggabungkan operasi dan menghasilkan pemanfaatan perangkat yang lebih baik dan penggunaan memori yang lebih rendah. Untuk detail tentang cara mengaktifkan XLA di program Anda dengan tf.function(jit_compile=True) atau pengelompokan otomatis, lihat panduan XLA .

Anda dapat mengatur level JIT global ke -1 (mati), 1 , atau 2 . Level yang lebih tinggi lebih agresif dan dapat mengurangi paralelisme dan menggunakan lebih banyak memori. Tetapkan nilainya menjadi 1 jika Anda memiliki batasan memori. Perhatikan bahwa XLA tidak bekerja dengan baik untuk model dengan bentuk tensor masukan variabel karena kompiler XLA harus terus mengompilasi kernel setiap kali menemukan bentuk baru.

2. Optimalkan kinerja pada host tunggal multi-GPU

API tf.distribute.MirroredStrategy dapat digunakan untuk menskalakan pelatihan model dari satu GPU ke beberapa GPU pada satu host. (Untuk mempelajari lebih lanjut cara melakukan pelatihan terdistribusi dengan TensorFlow, lihat panduan Pelatihan terdistribusi dengan TensorFlow , Menggunakan GPU , dan Menggunakan TPU serta tutorial pelatihan Terdistribusi dengan Keras .)

Meskipun transisi dari satu GPU ke beberapa GPU idealnya dapat diskalakan, terkadang Anda dapat mengalami masalah kinerja.

Saat beralih dari pelatihan dengan satu GPU ke beberapa GPU pada host yang sama, idealnya Anda harus merasakan penskalaan kinerja hanya dengan overhead tambahan komunikasi gradien dan peningkatan pemanfaatan thread host. Karena overhead ini, Anda tidak akan mendapatkan kecepatan 2x yang tepat jika Anda berpindah dari 1 ke 2 GPU, misalnya.

Tampilan jejak di bawah menunjukkan contoh overhead komunikasi tambahan saat berlatih pada beberapa GPU. Ada beberapa biaya tambahan untuk menggabungkan gradien, mengomunikasikannya ke seluruh replika, dan membaginya sebelum melakukan pembaruan bobot.

image

Daftar periksa berikut akan membantu Anda mencapai performa yang lebih baik saat mengoptimalkan performa dalam skenario multi-GPU:

  1. Cobalah untuk memaksimalkan ukuran batch, yang akan menghasilkan pemanfaatan perangkat yang lebih tinggi dan mengamortisasi biaya komunikasi di beberapa GPU. Menggunakan profiler memori membantu mengetahui seberapa dekat program Anda dengan pemanfaatan memori puncak. Perhatikan bahwa meskipun ukuran batch yang lebih tinggi dapat memengaruhi konvergensi, hal ini biasanya tidak sebanding dengan manfaat kinerjanya.
  2. Saat berpindah dari satu GPU ke beberapa GPU, host yang sama kini harus memproses lebih banyak data masukan. Jadi, setelah (1), disarankan untuk memeriksa kembali kinerja pipa masukan dan memastikan tidak ada hambatan.
  3. Periksa garis waktu GPU di tampilan jejak program Anda untuk melihat apakah ada panggilan AllReduce yang tidak perlu, karena ini akan menghasilkan sinkronisasi di semua perangkat. Dalam tampilan jejak yang ditunjukkan di atas, AllReduce dilakukan melalui kernel NCCL , dan hanya ada satu panggilan NCCL pada setiap GPU untuk gradien pada setiap langkah.
  4. Periksa operasi penyalinan D2H, H2D, dan D2D yang tidak perlu yang dapat diminimalkan.
  5. Periksa waktu langkah untuk memastikan setiap replika melakukan pekerjaan yang sama. Misalnya, satu GPU (biasanya GPU0 ) bisa mengalami kelebihan permintaan karena host secara tidak sengaja memberikan lebih banyak pekerjaan pada GPU tersebut.
  6. Terakhir, periksa langkah pelatihan di semua GPU dalam tampilan jejak Anda untuk setiap operasi yang dijalankan secara berurutan. Ini biasanya terjadi ketika program Anda menyertakan ketergantungan kontrol dari satu GPU ke GPU lainnya. Di masa lalu, proses debug kinerja dalam situasi ini telah diselesaikan berdasarkan kasus per kasus. Jika Anda mengamati perilaku ini dalam program Anda, laporkan masalah GitHub dengan gambar tampilan jejak Anda.

1. Optimalkan gradien AllReduce

Saat berlatih dengan strategi sinkron, setiap perangkat menerima sebagian dari data masukan.

Setelah menghitung lintasan maju dan mundur melalui model, gradien yang dihitung pada setiap perangkat perlu dikumpulkan dan dikurangi. Gradien AllReduce ini terjadi setelah penghitungan gradien pada setiap perangkat, dan sebelum pengoptimal memperbarui bobot model.

Setiap GPU pertama-tama menggabungkan gradien di seluruh lapisan model, mengomunikasikannya ke seluruh GPU menggunakan tf.distribute.CrossDeviceOps ( tf.distribute.NcclAllReduce adalah defaultnya), lalu mengembalikan gradien setelah pengurangan per lapisan.

Pengoptimal akan menggunakan gradien yang dikurangi ini untuk memperbarui bobot model Anda. Idealnya, proses ini harus terjadi secara bersamaan di semua GPU untuk mencegah overhead apa pun.

Waktu untuk AllReduce kira-kira sama dengan:

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

Perhitungan ini berguna sebagai pemeriksaan cepat untuk memahami apakah performa yang Anda miliki saat menjalankan tugas pelatihan terdistribusi sesuai dengan yang diharapkan, atau jika Anda perlu melakukan debugging performa lebih lanjut. Anda bisa mendapatkan jumlah parameter dalam model Anda dari Model.summary .

Perhatikan bahwa setiap parameter model berukuran 4 byte karena TensorFlow menggunakan fp32 (float32) untuk mengkomunikasikan gradien. Meskipun Anda mengaktifkan fp16 , NCCL AllReduce menggunakan parameter fp32 .

Untuk mendapatkan manfaat dari penskalaan, waktu langkah harus jauh lebih tinggi dibandingkan dengan biaya tambahan ini. Salah satu cara untuk mencapai hal ini adalah dengan menggunakan ukuran batch yang lebih tinggi karena ukuran batch mempengaruhi waktu langkah, namun tidak mempengaruhi overhead komunikasi.

2. Pertentangan thread host GPU

Saat menjalankan beberapa GPU, tugas CPU adalah membuat semua perangkat sibuk dengan meluncurkan kernel GPU secara efisien di seluruh perangkat.

Namun, ketika ada banyak operasi independen yang dapat dijadwalkan oleh CPU pada satu GPU, CPU dapat memutuskan untuk menggunakan banyak thread hostnya untuk membuat satu GPU sibuk, dan kemudian meluncurkan kernel pada GPU lain dalam urutan non-deterministik. . Hal ini dapat menyebabkan skala miring atau negatif, yang dapat berdampak negatif terhadap kinerja.

Penampil jejak di bawah menunjukkan overhead ketika CPU terhuyung-huyung. Kernel GPU diluncurkan secara tidak efisien, karena GPU1 menganggur dan kemudian mulai menjalankan operasi setelah GPU2 dimulai.

image

Tampilan jejak untuk host menunjukkan bahwa host meluncurkan kernel pada GPU2 sebelum meluncurkannya pada GPU1 (perhatikan bahwa operasi tf_Compute* di bawah ini tidak menunjukkan thread CPU).

image

Jika Anda mengalami kernel GPU yang mengejutkan dalam tampilan jejak program Anda, tindakan yang disarankan adalah:

  • Setel variabel lingkungan TensorFlow TF_GPU_THREAD_MODE ke gpu_private . Variabel lingkungan ini akan memberitahu host untuk merahasiakan thread untuk GPU.
  • Secara default, TF_GPU_THREAD_MODE=gpu_private menyetel jumlah thread menjadi 2, yang pada sebagian besar kasus sudah cukup. Namun, jumlah tersebut dapat diubah dengan menyetel variabel lingkungan TensorFlow TF_GPU_THREAD_COUNT ke jumlah thread yang diinginkan.