Optimalkan kinerja GPU TensorFlow dengan TensorFlow Profiler

Ringkasan

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

Jika Anda baru mengenal Profiler:

Ingatlah bahwa memindahkan komputasi ke GPU mungkin tidak selalu bermanfaat, terutama untuk model kecil. Dapat terjadi overhead karena:

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

Alur kerja pengoptimalan kinerja

Panduan ini menguraikan cara men-debug masalah kinerja yang dimulai dengan satu GPU, lalu pindah ke satu host dengan beberapa GPU.

Disarankan untuk men-debug masalah kinerja dalam urutan berikut:

  1. Optimalkan dan debug kinerja pada satu GPU:
    1. Periksa apakah pipa input mengalami kemacetan.
    2. Debug kinerja satu GPU.
    3. Aktifkan presisi campuran (dengan fp16 (float16)) dan secara opsional aktifkan XLA .
  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 kinerja untuk satu GPU sebelum men-debug sistem multi-GPU.

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

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

1. Optimalkan kinerja pada satu GPU

Dalam kasus yang ideal, program Anda harus memiliki pemanfaatan GPU yang tinggi, komunikasi CPU (host) ke GPU (perangkat) minimal, dan tidak ada overhead dari jalur input.

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

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

TensorFlow Profiler Overview Page

Nomor kunci untuk memperhatikan halaman ikhtisar adalah:

  1. Berapa banyak waktu langkah dari eksekusi perangkat yang 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. Untuk mendapatkan pemahaman mendalam tentang program Anda, Anda harus terbiasa dengan trace viewer Profiler TensorBoard. Bagian di bawah ini menunjukkan beberapa pola penampil pelacakan umum yang harus Anda cari saat mendiagnosis kemacetan 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, loss function, backward pass/gradient penghitungan, dan pembaruan bobot pengoptimal. Anda juga dapat menjalankan operasi pada GPU di sebelah setiap Aliran , yang merujuk ke aliran CUDA. Setiap aliran digunakan untuk tugas-tugas tertentu. Dalam jejak ini, Stream#118 digunakan untuk meluncurkan kernel komputasi dan salinan perangkat-ke-perangkat. Stream#119 digunakan untuk salinan host-to-device dan Stream#120 untuk salinan perangkat ke host.

Jejak di bawah ini menunjukkan karakteristik umum dari model berkinerja.

image

Misalnya, garis waktu komputasi GPU ( Stream#118 ) terlihat "sibuk" dengan jarak yang sangat sedikit. Ada salinan minimal dari host ke perangkat ( Stream #119 ) dan dari perangkat ke host ( Stream #120 ), serta jarak antar langkah minimal. Saat Anda menjalankan Profiler untuk program Anda, Anda mungkin tidak dapat mengidentifikasi karakteristik ideal ini dalam tampilan penelusuran Anda. Sisa panduan ini mencakup skenario umum dan cara memperbaikinya.

1. Debug pipa input

Langkah pertama dalam debugging kinerja GPU adalah menentukan apakah program Anda terikat input. Cara termudah untuk mengetahui hal ini adalah dengan menggunakan penganalisis Input-pipeline Profiler , di TensorBoard, yang memberikan ikhtisar waktu yang dihabiskan di pipeline input.

image

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

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

Selain itu, lihat praktik terbaik untuk mengoptimalkan jalur data input .

2. Debug kinerja satu GPU

Ada beberapa faktor yang dapat berkontribusi pada pemanfaatan GPU yang rendah. Di bawah ini adalah beberapa skenario yang biasa diamati saat melihat penampil jejak dan solusi potensial.

1. Analisis kesenjangan antar langkah

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

image

Jika penampil jejak Anda menunjukkan celah besar di antara langkah-langkah, ini bisa menjadi indikasi bahwa program Anda terikat input. Dalam hal ini Anda harus merujuk ke bagian sebelumnya tentang men-debug saluran input Anda jika Anda belum melakukannya.

Namun, bahkan dengan saluran input yang dioptimalkan, Anda masih dapat memiliki celah antara akhir satu langkah dan awal langkah lainnya karena pertikaian utas CPU. tf.data memanfaatkan utas latar belakang untuk memparalelkan pemrosesan pipa. Utas ini dapat mengganggu aktivitas sisi host GPU yang terjadi di awal setiap langkah, seperti menyalin data atau menjadwalkan operasi GPU.

Jika Anda melihat celah besar di sisi host, yang menjadwalkan operasi ini di GPU, Anda dapat mengatur variabel lingkungan TF_GPU_THREAD_MODE=gpu_private . Ini memastikan bahwa kernel GPU diluncurkan dari utas khusus mereka sendiri, dan tidak mengantre 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 kinerja yang baik seperti operasi di dalam grafik TensorFlow. Selain itu, beberapa operasi ini berjalan di CPU dan menyalin tensor bolak-balik dari GPU.

Jika setelah mengoptimalkan saluran input Anda, Anda masih melihat celah di antara langkah-langkah di penampil pelacakan, Anda harus melihat kode model di antara langkah-langkah dan memeriksa apakah menonaktifkan panggilan balik/metrik meningkatkan kinerja. Beberapa detail operasi ini juga ada di penampil jejak (baik perangkat dan sisi host). Rekomendasi dalam skenario ini adalah mengamortisasi overhead operasi ini dengan menjalankannya setelah sejumlah langkah tetap, bukan setiap langkah. Saat menggunakan metode compile di API tf.keras , menyetel tanda experimental_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, tetapi ada latensi (sekitar 20-40 s) yang terlibat sebelum kernel benar-benar dieksekusi di GPU. Dalam kasus yang ideal, host mengantrekan cukup banyak kernel pada GPU sehingga GPU menghabiskan sebagian besar waktunya untuk mengeksekusi, daripada menunggu host untuk mengantrekan lebih banyak kernel.

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

image

Penampil jejak untuk program yang sama ini menunjukkan celah kecil antara kernel di mana tuan rumah sibuk meluncurkan kernel pada GPU.

image

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

image

Jika penampil jejak Anda menunjukkan banyak celah kecil antara 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 bersemangat murni. 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.
  • Fuse kernel menggunakan XLA dengan tf.function(jit_compile=True) atau auto-clustering. Untuk detail selengkapnya, 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 kepada Anda persentase operasi yang ditempatkan di host vs. perangkat (Anda juga dapat memverifikasi penempatan operasi tertentu dengan melihat trace viewer . Seperti pada gambar di bawah, Anda ingin persentase operasi di host menjadi sangat kecil dibandingkan dengan perangkat.

image

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

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

Perhatikan bahwa dalam beberapa kasus, bahkan jika Anda menentukan op untuk ditempatkan pada perangkat tertentu, implementasinya 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 dalam tampilan jejak di bawah pada aliran GPU #167 , #168 , dan #169 .

image

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

3. Kernel yang lebih efisien pada GPU

Setelah penggunaan GPU program Anda dapat diterima, langkah selanjutnya adalah melihat peningkatan efisiensi kernel GPU dengan memanfaatkan Tensor Cores atau operasi sekering.

1. Gunakan Tensor Cores

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

Anda dapat menggunakan statistik kernel GPU TensorBoard untuk memvisualisasikan kernel GPU mana yang memenuhi syarat untuk 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) program Anda (matmul ops) menggunakan Tensor Core. Kernel GPU menggunakan Core Tensor secara efisien saat presisinya adalah fp16 dan dimensi tensor input/output dapat dibagi 8 atau 16 (untuk int8 ).

Untuk rekomendasi terperinci lainnya tentang cara membuat kernel menjadi efisien untuk GPU, lihat panduan kinerja pembelajaran mendalam NVIDIA® .

2. Operasi sekering

Gunakan tf.function(jit_compile=True) untuk menggabungkan operasi yang lebih kecil untuk membentuk kernel yang lebih besar yang 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 meningkatkan kinerja lebih lanjut. Pendekatan yang disarankan adalah mengaktifkannya satu per satu dan memverifikasi bahwa manfaat kinerja sesuai dengan yang diharapkan.

1. Aktifkan presisi campuran

Panduan presisi TensorFlow Mixed menunjukkan cara mengaktifkan presisi fp16 pada GPU. Aktifkan AMP pada GPU NVIDIA® untuk menggunakan Tensor Cores dan realisasikan percepatan keseluruhan hingga 3x lipat jika dibandingkan dengan hanya menggunakan 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 saat presisinya adalah fp16 dan dimensi input/output dapat 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, ini tidak terjadi dan Anda tidak mengalami percepatan yang diharapkan dari mengaktifkan fp16 , karena program Anda 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 kinerja pembelajaran mendalam berisi saran tambahan tentang cara memanfaatkan Tensor Cores. Selain itu, manfaat menggunakan fp16 juga akan terlihat di kernel yang sebelumnya terikat memori, karena sekarang operasi akan memakan waktu separuh.

2. Penskalaan kerugian dinamis vs. statis

Penskalaan kerugian diperlukan saat menggunakan fp16 untuk mencegah aliran bawah karena presisi rendah. Ada dua jenis penskalaan kerugian, dinamis dan statis, keduanya dijelaskan secara lebih rinci 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 memperkenalkan operasi bersyarat tambahan yang berjalan di host, dan menyebabkan kesenjangan yang akan terlihat di antara langkah-langkah dalam penampil pelacakan. Di sisi lain, penskalaan kerugian statis tidak memiliki overhead seperti itu dan dapat menjadi opsi yang lebih baik dalam hal kinerja dengan tangkapan yang Anda perlukan untuk menentukan nilai skala kerugian statis yang benar.

2. Aktifkan XLA dengan tf.function(jit_compile=True) atau auto-clustering

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 jejak 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 (nonaktif), 1 , atau 2 . Tingkat yang lebih tinggi lebih agresif dan dapat mengurangi paralelisme dan menggunakan lebih banyak memori. Tetapkan nilainya ke 1 jika Anda memiliki batasan memori. Perhatikan bahwa XLA tidak berkinerja baik untuk model dengan bentuk tensor input variabel karena kompiler XLA harus terus mengompilasi kernel setiap kali menemukan bentuk baru.

2. Optimalkan kinerja pada host tunggal multi-GPU

tf.distribute.MirroredStrategy API dapat digunakan untuk menskalakan pelatihan model dari satu GPU ke beberapa GPU pada satu host. (Untuk mempelajari lebih lanjut tentang cara melakukan pelatihan terdistribusi dengan TensorFlow, lihat panduan pelatihan Terdistribusi dengan TensorFlow , Menggunakan GPU , dan Menggunakan TPU dan pelatihan Terdistribusi dengan tutorial 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 mengalami penskalaan kinerja hanya dengan overhead tambahan komunikasi gradien dan peningkatan pemanfaatan thread host. Karena overhead ini, Anda tidak akan memiliki speedup 2x yang tepat jika Anda berpindah dari 1 ke 2 GPU, misalnya.

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

image

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

  1. Cobalah untuk memaksimalkan ukuran batch, yang akan menghasilkan pemanfaatan perangkat yang lebih tinggi dan mengurangi 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 sebanding dengan manfaat kinerjanya.
  2. Saat berpindah dari satu GPU ke beberapa GPU, host yang sama sekarang harus memproses lebih banyak data input. Jadi, setelah (1), disarankan untuk memeriksa kembali kinerja pipa input dan memastikan tidak ada kemacetan.
  3. Periksa garis waktu GPU di tampilan pelacakan program Anda untuk panggilan AllReduce yang tidak perlu, karena ini 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, dapat terjadi bahwa satu GPU (biasanya, GPU0 ) kelebihan langganan karena tuan rumah secara keliru akhirnya memberikan lebih banyak pekerjaan padanya.
  6. Terakhir, periksa langkah pelatihan di semua GPU dalam tampilan pelacakan untuk setiap operasi yang dijalankan secara berurutan. Ini biasanya terjadi ketika program Anda menyertakan dependensi kontrol dari satu GPU ke GPU lainnya. Di masa lalu, men-debug kinerja dalam situasi ini telah diselesaikan berdasarkan kasus per kasus. Jika Anda mengamati perilaku ini di program Anda, ajukan masalah GitHub dengan gambar tampilan jejak Anda.

1. Optimalkan gradien AllReduce

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

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

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

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

Waktu untuk AllReduce harus kira-kira sama dengan:

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

Perhitungan ini berguna sebagai pemeriksaan cepat untuk memahami apakah kinerja yang Anda miliki saat menjalankan tugas pelatihan terdistribusi seperti yang diharapkan, atau jika Anda perlu melakukan debug kinerja 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 mengomunikasikan gradien. Bahkan ketika fp16 Anda diaktifkan, NCCL AllReduce menggunakan parameter fp32 .

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

2. Perdebatan utas host GPU

Saat menjalankan beberapa GPU, tugas CPU adalah membuat semua perangkat tetap 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 utas host untuk membuat satu GPU sibuk, dan kemudian meluncurkan kernel pada GPU lain dalam urutan yang tidak deterministik. . Hal ini dapat menyebabkan kemiringan atau penskalaan negatif, yang dapat memengaruhi kinerja secara negatif.

Penampil pelacakan di bawah ini menunjukkan overhead ketika CPU terhuyung-huyung meluncurkan kernel GPU 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 utas CPU).

image

Jika Anda mengalami guncangan kernel GPU seperti ini dalam tampilan jejak program Anda, tindakan yang disarankan adalah:

  • Setel variabel lingkungan TF_GPU_THREAD_MODE ke gpu_private . Variabel lingkungan ini akan memberi tahu host untuk menjaga agar utas untuk GPU tetap pribadi.
  • Secara default, TF_GPU_THREAD_MODE=gpu_private menyetel jumlah utas menjadi 2, yang cukup dalam banyak kasus. Namun, angka tersebut dapat diubah dengan menyetel variabel lingkungan TF_GPU_THREAD_COUNT ke jumlah utas yang diinginkan.