RSVP untuk acara TensorFlow Everywhere lokal Anda hari ini!
Halaman ini diterjemahkan oleh Cloud Translation API.
Switch to English

Optimalkan Performa GPU TensorFlow dengan TensorFlow Profiler

Gambaran

Panduan ini ditujukan bagi pengguna TensorFlow yang menggunakan GPU untuk meningkatkan performa model. Dengan menggunakan TensorFlow Profiler sebagai alat utama untuk mendapatkan insight tentang performa, panduan ini akan membantu Anda melakukan debug saat satu atau beberapa GPU Anda kurang dimanfaatkan. Panduan memulai cepat untuk TensorFlow Profiler dapat ditemukan di tutorial TensorFlow Profiler , dan cara tambahan untuk mendapatkan profil didokumentasikan di performa Optimize TensorFlow menggunakan panduan Profiler .

Perlu diingat bahwa memindahkan komputasi ke GPU mungkin tidak selalu bermanfaat, terutama untuk model kecil. Ada overhead karena transfer data antara host (CPU) dan perangkat (GPU), serta overhead karena latensi yang terlibat saat host meluncurkan kernel GPU. Performa yang baik dicapai jika host berhasil mempertahankan GPU dengan cukup membongkar pekerjaan.

Alur Kerja Pengoptimalan Kinerja

Panduan ini menguraikan cara men-debug masalah kinerja yang dimulai dengan satu GPU, kemudian beralih ke satu host dengan beberapa GPU. Disarankan untuk men-debug masalah kinerja dalam urutan ini. Misalnya, jika Anda menggunakan strategi distribusi TensorFlow untuk melatih model pada satu host dengan beberapa GPU dan melihat pemanfaatan GPU yang kurang optimal, Anda harus mengoptimalkan dan men-debug kinerja untuk 1 GPU terlebih dahulu, sebelum men-debug sistem multi-GPU. Urutan yang direkomendasikan adalah sebagai berikut:

  1. Mengoptimalkan dan men-debug kinerja pada 1 GPU
    1. Periksa apakah pipa masukan mengalami hambatan
    2. Performa debug dari 1 GPU
    3. Aktifkan fp16 dan aktifkan XLA secara opsional
  2. Mengoptimalkan dan men-debug kinerja pada host tunggal multi-GPU

Sebagai dasar untuk mendapatkan kode performa pada GPU, panduan ini mengasumsikan Anda sudah menggunakan tf.function . Kompilasi Keras / fit API akan menggunakan tf.function secara otomatis di bawah tenda. Saat menulis loop pelatihan kustom, lihat panduan ini tentang cara mengaktifkan tf.function .

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

Optimalkan Performa pada 1 GPU

Dalam kasus yang ideal, program Anda harus memiliki pemanfaatan GPU yang tinggi, minimal komunikasi CPU (host) ke GPU (perangkat), dan tidak ada overhead dari pipeline input. Langkah pertama untuk menganalisis kinerja adalah mendapatkan profil untuk model yang berjalan dengan satu GPU.

Halaman Ringkasan TensorFlow Profiler memberikan gambaran tentang seberapa jauh program Anda dari skenario yang ideal.

TensorFlow Profiler Overview Page

Angka-angka kunci yang harus dicari di halaman ikhtisar adalah:

  1. Berapa banyak waktu langkah dari eksekusi perangkat yang sebenarnya
  2. Persentase operasi yang ditempatkan di 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 yang lebih mendalam tentang program Anda, Anda harus memahami trace viewer TensorFlow Profiler. Bagian di bawah ini menunjukkan beberapa pola penampil jejak umum yang harus Anda cari saat mendiagnosis hambatan kinerja.

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

image

Jejak ini menunjukkan karakteristik umum dari model performant. Misalnya, garis waktu komputasi GPU ( Stream # 118 ) terlihat sibuk dengan sedikit celah. Ada salinan minimal dari host ke perangkat ( Aliran # 119 ) dan dari perangkat ke host ( Aliran # 120 ), serta celah minimal antar langkah. Saat menjalankan TensorFlow Profiler untuk program Anda, Anda mungkin tidak melihat karakteristik ideal ini dalam tampilan pelacakan. Bagian lain dari panduan ini mencakup skenario umum dan cara memperbaikinya.

Debug Input Pipeline

Langkah pertama dalam debugging kinerja GPU adalah menentukan apakah program Anda terikat dengan input. Cara termudah untuk mengetahuinya adalah dengan menggunakan Input-Pipeline Analyzer dari TensorFlow Profiler, yang memberikan ringkasan waktu yang dihabiskan dalam pipeline input.

image

Berikut ini adalah tindakan potensial yang dapat Anda lakukan jika pipeline input Anda berkontribusi secara signifikan terhadap waktu langkah:

  • Lihat panduan khusus tf.data untuk mempelajari cara men-debug pipeline input Anda.
  • Cara cepat lain untuk memeriksa apakah pipeline masukan adalah penghambat adalah dengan menggunakan data masukan yang dibuat secara acak yang tidak memerlukan pemrosesan awal. Berikut adalah contoh penggunaan teknik ini untuk model ResNet. Jika pipeline masukan optimal, Anda akan melihat performa serupa dengan data nyata dan dengan data acak / sintetis yang dihasilkan. Satu-satunya overhead dalam kasus data sintetis akan disebabkan oleh salinan data masukan yang lagi-lagi dapat diambil terlebih dahulu dan dioptimalkan.

Kinerja Debug dari 1 GPU

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

Menganalisis Kesenjangan Antar Langkah

Pengamatan umum ketika program Anda tidak berjalan secara optimal adalah kesenjangan antara langkah-langkah pelatihan. Pada gambar di bawah ini, ada celah besar antara langkah 8 dan 9, yang berarti GPU dalam keadaan diam selama waktu itu.

image

Jika penampil jejak Anda menunjukkan celah besar di antara langkah-langkah, ini bisa menjadi indikasi bahwa program Anda terikat dengan input. Dalam hal ini, Anda harus merujuk ke bagian sebelumnya tentang men-debug pipeline input Anda jika Anda belum melakukannya. Namun, bahkan dengan pipeline input yang dioptimalkan, Anda masih dapat melihat celah antara akhir satu langkah dan awal langkah lainnya karena pertentangan CPU thread. tf.data memanfaatkan thread latar belakang untuk memparalelkan pemrosesan pipeline. 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 menyetel variabel lingkungan TF_GPU_THREAD_MODE=gpu_private . Ini memastikan bahwa kernel GPU diluncurkan dari utas khusus mereka sendiri, dan tidak mengantri di belakang pekerjaan tf.data .

Kesenjangan antar langkah juga dapat disebabkan oleh kalkulasi metrik, panggilan balik 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 pipeline input, Anda masih melihat celah di antara langkah-langkah di trace viewer, Anda harus melihat kode model di antara langkah-langkah tersebut, dan melihat apakah menonaktifkan callback / metrik meningkatkan performa. Beberapa detail operasi ini juga ada di penampil jejak (baik perangkat maupun sisi host). Rekomendasi dalam skenario ini adalah untuk mengamortisasi overhead operasi ini dengan menjalankannya setelah sejumlah langkah tetap, bukan setiap langkah. Saat menggunakan metode compile di tf.keras API, menyetel tanda experimental_steps_per_execution melakukan ini secara otomatis. Untuk loop pelatihan kustom, gunakan tf.while_loop . tf.while_loop .

Raih Pemanfaatan Perangkat yang Lebih Tinggi

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, tuan rumah mengantrekan cukup banyak kernel pada GPU sehingga GPU menghabiskan sebagian besar waktunya untuk mengeksekusi, daripada menunggu tuan rumah untuk memasukkan lebih banyak kernel.

The TensorFlow Profiler ini Gambaran Halaman menunjukkan berapa banyak waktu GPU itu karena menganggur untuk menunggu di host untuk kernel peluncuran. Pada gambar di bawah, GPU menganggur sekitar 10% dari waktu tunggu menunggu kernel diluncurkan.

image

Penampil jejak untuk program yang sama ini menunjukkan celah kecil antara kernel tempat host sibuk meluncurkan kernel di GPU.

image

Dengan meluncurkan banyak operasi kecil di GPU (seperti penambahan skalar misalnya), host mungkin tidak dapat mengikuti GPU. Halaman Statistik Tensorflow untuk Profil TensorFlow yang sama menampilkan 126.224 operasi Mul yang membutuhkan waktu 2,77 detik. Dengan demikian, 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 di GPU seperti gambar di atas, Anda dapat:

  • Gabungkan tensor kecil dan gunakan operasi vektor atau gunakan ukuran batch yang lebih besar agar setiap kernel yang diluncurkan melakukan lebih banyak pekerjaan, yang akan membuat GPU sibuk lebih lama.
  • Pastikan Anda menggunakan tf.function untuk membuat grafik TF dan tidak menjalankan operasi dalam mode bersemangat murni (Menggunakan tf.keras.Model.compile secara otomatis melakukan ini).
  • Kernel sekering menggunakan XLA. Untuk lebih jelasnya, lihat bagian di bawah ini tentang cara mengaktifkan XLA untuk mendapatkan kinerja yang lebih tinggi. Ini adalah fitur eksperimental, tetapi mengarah pada pemanfaatan perangkat yang tinggi.
Penempatan Operasi Tensorflow

Halaman Ringkasan Profiler TensorFlow memberi tahu Anda persentase operasi yang ditempatkan pada perangkat vs host (Anda juga dapat memverifikasi penempatan operasi tertentu dengan melihat penampil jejak). Seperti pada gambar di bawah, Anda ingin persentase operasi pada host menjadi sangat kecil dibandingkan dengan perangkat.

image

Idealnya, sebagian besar operasi intensif komputasi harus ditempatkan di GPU. Untuk mengetahui perangkat mana yang menetapkan operasi dan tensor dalam model Anda, setel tf.debugging.set_log_device_placement(True) sebagai pernyataan pertama program Anda. Perhatikan bahwa dalam beberapa kasus, meskipun Anda menentukan op untuk ditempatkan pada perangkat tertentu, implementasinya mungkin menimpa kondisi ini (contoh: tf.unique ). Bahkan untuk pelatihan GPU tunggal, menentukan strategi distribusi, seperti tf.distribute.OneDeviceStrategy , dapat menghasilkan penempatan operasi yang lebih deterministik di perangkat Anda.

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

image

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

Kernel yang Lebih Efisien pada GPU

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

Memanfaatkan Tensor Cores

GPU modern memiliki inti tensor khusus yang dapat secara signifikan meningkatkan kinerja kernel yang memenuhi syarat. Halaman statistik kernel GPU menunjukkan kernel GPU mana yang memenuhi syarat Tensor Core, dan kernel mana yang menggunakan Tensor Core. Mengaktifkan fp16 (lihat bagian Mengaktifkan Presisi Campuran di bawah) adalah salah satu cara untuk membuat kernel General Matrix Multiply (GEMM) (matmul ops) program Anda menggunakan Tensor Core.

Untuk rekomendasi mendetail lainnya tentang cara membuat kernel efisien untuk GPU, lihat panduan Performa Pembelajaran Mendalam NVIDIA , yang mencakup berbagai teknik yang dapat Anda coba seperti menggunakan format NCHW vs NHWC untuk merepresentasikan input, atau membuat dimensi input menjadi kelipatan 8.

Fusing Ops

Dengan fitur tf.xla.xperimental_compile , TensorFlow dapat menggabungkan operasi yang lebih kecil untuk membentuk kernel yang lebih besar yang menghasilkan peningkatan performa yang signifikan. Detail lebih lanjut dibahas di bagian Aktifkan XLA di bawah ini.

Aktifkan fp16 dan XLA

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

Mengaktifkan Presisi Campuran

Panduan Presisi Campuran TensorFlow menunjukkan cara mengaktifkan presisi fp16 pada GPU. Dalam hal menyadari manfaat kinerja fp16 ada beberapa petunjuk yang perlu diingat.

Menggunakan Kernel fp16 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 melihat speedup yang diharapkan dari pengaktifan 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 performa deep learning berisi saran tambahan tentang cara memanfaatkan Tensor Cores. Selain itu, manfaat fp16 juga akan ditampilkan di kernel yang sebelumnya terikat memori, karena sekarang pengoperasiannya akan memakan waktu separuh.

Penskalaan Kerugian Dinamis vs Statis

Loss scaling diperlukan saat menggunakan fp16 untuk mencegah aliran bawah karena presisi yang rendah. Ada dua jenis penskalaan kerugian, dinamis dan statis, keduanya dijelaskan secara lebih rinci dalam panduan Presisi Campuran . Saat mencoba mengoptimalkan kinerja, penting untuk diingat bahwa penskalaan kerugian dinamis dapat memperkenalkan operasi bersyarat tambahan yang dijalankan pada host, dan menyebabkan celah yang akan terlihat di antara langkah-langkah dalam penampil jejak. Di sisi lain, penskalaan kerugian statis tidak memiliki overhead seperti itu dan dapat menjadi opsi yang lebih baik dalam hal kinerja dengan tangkapan bahwa Anda perlu menentukan nilai skala kerugian statis yang benar.

Mengaktifkan XLA

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, lihat panduan XLA: Mengoptimalkan Kompiler untuk Pembelajaran Mesin .

Mengoptimalkan Performa pada Multi-GPU Single Host

API tf.distribute.MirroredStrategy dapat digunakan untuk menskalakan pelatihan model dari 1 GPU ke beberapa GPU pada satu host. Untuk mempelajari lebih lanjut tentang cara melakukan pelatihan terdistribusi dengan Tensorflow, lihat panduan Pelatihan terdistribusi dengan Keras . Meskipun transisi dari satu GPU ke beberapa GPU idealnya harus dapat diskalakan di luar kotak, terkadang Anda dapat mengalami masalah kinerja.

Saat beralih dari pelatihan dengan satu GPU ke beberapa GPU pada host yang sama, idealnya Anda akan melihat penskalaan kinerja hanya dengan overhead tambahan komunikasi gradien dan peningkatan pemanfaatan utas host. Karena overhead ini, Anda tidak akan melihat percepatan 2x yang tepat jika Anda berpindah dari 1 ke 2 GPU misalnya. Tampilan pelacakan di bawah ini menunjukkan contoh overhead komunikasi tambahan saat melatih beberapa GPU. Ada beberapa overhead untuk menggabungkan gradien, mengkomunikasikannya di seluruh replika, dan memisahkannya 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 penggunaan perangkat yang lebih tinggi dan mengurangi biaya komunikasi di beberapa GPU. Menggunakan profiler memori membantu memahami seberapa dekat program Anda dengan pemanfaatan memori puncak. Perhatikan bahwa meskipun ukuran kumpulan yang lebih tinggi dapat memengaruhi konvergensi, ini biasanya sebanding dengan manfaat kinerja.
  2. Saat berpindah dari satu GPU ke beberapa GPU, host yang sama sekarang harus memproses lebih banyak data masukan. Jadi setelah (1) direkomendasikan untuk memeriksa kembali kinerja pipeline input dan memastikan itu bukan bottleneck.
  3. Periksa timeline GPU di tampilan jejak program Anda untuk melihat apakah ada 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 dan lihat apakah dapat diminimalkan.
  5. Periksa waktu langkah untuk memastikan setiap replika melakukan pekerjaan yang sama. Bisa terjadi bahwa satu GPU (biasanya GPU0) kelebihan permintaan karena host secara keliru akhirnya menempatkan lebih banyak pekerjaan di atasnya.
  6. Terakhir, periksa langkah pelatihan di semua GPU dalam tampilan pelacakan Anda untuk setiap operasi yang dijalankan secara berurutan. Ini biasanya terjadi ketika program Anda menyertakan dependensi kontrol dari satu GPU ke yang lain. Kinerja debugging dalam situasi ini telah diselesaikan berdasarkan kasus per kasus di masa lalu. Jika Anda mengamati perilaku ini dalam program Anda, ajukan masalah Github dengan gambar tampilan jejak Anda.

Optimalkan Gradient 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 digabungkan dan dikurangi. Gradien AllReduce ini terjadi setelah kalkulasi gradien pada setiap perangkat, dan sebelum pengoptimal memperbarui bobot model. Setiap GPU pertama-tama menggabungkan gradien di seluruh lapisan model, mengkomunikasikannya di 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 pada waktu yang sama di semua GPU untuk mencegah overhead apa pun. Waktu untuk AllReduce kira-kira sama dengan:

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

Penghitungan ini berguna sebagai pemeriksaan cepat untuk memahami apakah performa yang Anda lihat saat menjalankan tugas pelatihan terdistribusi sesuai yang diharapkan, atau jika Anda perlu melakukan debug performa lebih lanjut. Anda bisa mendapatkan jumlah parameter dalam model Anda dari tf.keras.Model.summary .

Perhatikan bahwa setiap parameter model adalah 4 byte karena Tensorflow menggunakan fp32 untuk mengomunikasikan gradien. Meskipun Anda telah mengaktifkan fp16, NCCL AllReduce menggunakan parameter fp32. Di masa mendatang, Tensorflow akan mendukung operasi AllReduce menggunakan fp16, serta melakukan pipeline pada gradien AllReduce sehingga tumpang tindih dengan komputasi gradien.

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

Isi Thread Host GPU

Saat menjalankan beberapa GPU, tugas CPU adalah membuat semua perangkat sibuk dengan meluncurkan kernel GPU secara efisien di seluruh perangkat. Namun, jika ada banyak operasi independen yang dapat dijadwalkan oleh CPU pada satu GPU, CPU dapat memutuskan untuk menggunakan banyak utas hostnya untuk membuat satu GPU sibuk, dan kemudian meluncurkan kernel pada GPU lain dalam urutan non-deterministik . Hal ini dapat menyebabkan penskalaan miring atau negatif, yang dapat mengganggu performa.

Penampil pelacakan di bawah ini menunjukkan overhead saat CPU menghentikan peluncuran kernel GPU secara tidak efisien, karena GPU1 tidak aktif 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 melihat kernel GPU yang mengejutkan ini dalam tampilan pelacakan program Anda, tindakan yang disarankan adalah:

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