Bagaimana thread diatur untuk dijalankan oleh GPU?
Bagaimana thread diatur untuk dijalankan oleh GPU?
Jawaban:
Jika perangkat GPU memiliki, misalnya, 4 unit multiprosesing, dan mereka dapat menjalankan masing-masing 768 utas: maka pada saat tertentu tidak lebih dari 4 * 768 utas akan benar-benar berjalan secara paralel (jika Anda merencanakan lebih banyak utas, mereka akan menunggu giliran mereka).
utas disusun dalam blok. Blok dijalankan oleh unit multiprosesing. Utas blok dapat diidentifikasi (diindeks) menggunakan 1Dimensi (x), 2Dimensi (x, y) atau indeks 3Dim (x, y, z) tetapi dalam kasus apa pun x y z <= 768 untuk contoh kita (pembatasan lain berlaku untuk x, y, z, lihat panduan dan kemampuan perangkat Anda).
Jelas, jika Anda membutuhkan lebih dari 4 * 768 utas itu, Anda membutuhkan lebih dari 4 blok. Blok juga dapat diindeks 1D, 2D atau 3D. Ada antrian blok yang menunggu untuk memasuki GPU (karena, dalam contoh kami, GPU memiliki 4 multiprosesor dan hanya 4 blok yang dieksekusi secara bersamaan).
Misalkan kita ingin satu utas untuk memproses satu piksel (i, j).
Kita dapat menggunakan blok masing-masing 64 utas. Maka kita membutuhkan 512 * 512/64 = 4096 blok (jadi untuk memiliki 512x512 utas = 4096 * 64)
Sudah umum untuk mengatur (untuk mempermudah pengindeksan gambar) utas dalam blok 2D yang memiliki blockDim = 8 x 8 (64 utas per blok). Saya lebih suka menyebutnya threadsPerBlock.
dim3 threadsPerBlock(8, 8); // 64 threads
dan 2D gridDim = 64 x 64 blok (4096 blok diperlukan). Saya lebih suka menyebutnya numBlocks.
dim3 numBlocks(imageWidth/threadsPerBlock.x, /* for instance 512/8 = 64*/
imageHeight/threadsPerBlock.y);
Kernel diluncurkan seperti ini:
myKernel <<<numBlocks,threadsPerBlock>>>( /* params for the kernel function */ );
Akhirnya: akan ada sesuatu seperti "antrian 4096 blok", di mana satu blok sedang menunggu untuk ditugaskan salah satu dari multiprosesor GPU untuk menjalankan 64 utasnya.
Dalam kernel piksel (i, j) yang akan diproses oleh utas dihitung dengan cara ini:
uint i = (blockIdx.x * blockDim.x) + threadIdx.x;
uint j = (blockIdx.y * blockDim.y) + threadIdx.y;
Misalkan GPU 9800GT:
https://www.tutorialspoint.com/cuda/cuda_threads.htm
Blok tidak dapat memiliki utas lebih aktif dari 512 sehingga __syncthreads
hanya dapat menyinkronkan utas dalam jumlah terbatas. yaitu Jika Anda menjalankan yang berikut dengan 600 utas:
func1();
__syncthreads();
func2();
__syncthreads();
maka kernel harus dijalankan dua kali dan urutan eksekusi adalah:
catatan:
Titik utama __syncthreads
adalah operasi blok-lebar dan tidak menyinkronkan semua utas.
Saya tidak yakin tentang jumlah persis utas yang __syncthreads
dapat disinkronkan, karena Anda dapat membuat blok dengan lebih dari 512 utas dan membiarkan warp menangani penjadwalan. Untuk pemahaman saya itu lebih akurat untuk mengatakan: func1 dijalankan setidaknya untuk 512 utas pertama.
Sebelum saya mengedit jawaban ini (kembali pada tahun 2010) saya mengukur 14x8x32 utas yang disinkronkan menggunakan __syncthreads
.
Saya akan sangat menghargai jika seseorang menguji ini lagi untuk informasi yang lebih akurat.
__syncthreads
adalah operasi blok-lebar dan fakta bahwa sebenarnya tidak menyinkronkan semua utas adalah gangguan bagi pelajar CUDA. Jadi saya memperbarui jawaban saya berdasarkan informasi yang Anda berikan kepada saya. Saya sangat menghargai itu.