Bir GPU tarafından yürütülecek iş parçacıkları nasıl düzenlenir?
Bir GPU tarafından yürütülecek iş parçacıkları nasıl düzenlenir?
Yanıtlar:
Bir GPU cihazında, örneğin 4 çok işlemcili birim varsa ve her biri 768 iş parçacığı çalıştırabilirlerse: belirli bir anda 4 * 768 iş parçacığından fazlası gerçekten paralel çalışmaz (daha fazla iş parçacığı planladıysanız, bekliyor olacaklar) onların sırası).
konular bloklar halinde düzenlenmiştir. Bir blok çok işlemcili bir birim tarafından yürütülür. Bir bloğun dişleri 1Boyut (x), 2Dboyutlar (x, y) veya 3Dim indeksleri (x, y, z) kullanılarak tanımlanabilir (indekslenebilir), ancak her durumda örneğimiz için x y z <= 768 (diğer kısıtlamalar geçerlidir) x, y, z için kılavuza ve cihazınızın kapasitesine bakın).
Açıkçası, bu 4 * 768 dişlerden daha fazlasına ihtiyacınız varsa 4 bloktan fazlasına ihtiyacınız vardır. Bloklar ayrıca 1D, 2D veya 3D olarak indekslenebilir. GPU'ya girmeyi bekleyen bir blok sırası vardır (çünkü örneğimizde GPU'da 4 çok işlemcili vardır ve aynı anda sadece 4 blok yürütülmektedir).
Bir iş parçacığının bir pikseli (i, j) işlemesini istediğimizi varsayalım.
Her biri 64 iş parçacıklı blokları kullanabiliriz. Sonra 512 * 512/64 = 4096 bloğa ihtiyacımız var (512x512 iş parçacığı = 4096 * 64 olması için)
BlockDim = 8 x 8 (blok başına 64 iş parçacığı) olan 2B bloklardaki iş parçacıklarını düzenlemek (görüntüyü endekslemeyi kolaylaştırmak için) yaygındır. Ben ThreadPerBlock demeyi tercih ederim.
dim3 threadsPerBlock(8, 8); // 64 threads
ve 2D gridDim = 64 x 64 blok (4096 blok gereklidir). Buna numBlocks demeyi tercih ederim.
dim3 numBlocks(imageWidth/threadsPerBlock.x, /* for instance 512/8 = 64*/
imageHeight/threadsPerBlock.y);
Çekirdek şu şekilde başlatılır:
myKernel <<<numBlocks,threadsPerBlock>>>( /* params for the kernel function */ );
Son olarak: "4096 blok kuyruğu" gibi bir şey olacaktır, burada bir bloğa 64 iş parçacığını çalıştırmak için GPU'nun çok işlemcilerinden birine atanması beklenmektedir.
Çekirdekte bir iş parçacığı tarafından işlenecek piksel (i, j) şu şekilde hesaplanır:
uint i = (blockIdx.x * blockDim.x) + threadIdx.x;
uint j = (blockIdx.y * blockDim.y) + threadIdx.y;
Bir 9800GT GPU varsayalım:
https://www.tutorialspoint.com/cuda/cuda_threads.htm
Bir blok 512'den daha fazla aktif iş parçacığına sahip __syncthreads
olamaz, bu nedenle yalnızca sınırlı sayıda iş parçacığını senkronize edebilir. ie Aşağıdakileri 600 iş parçacığıyla yürütürseniz:
func1();
__syncthreads();
func2();
__syncthreads();
çekirdek iki kez çalışmalı ve yürütme sırası şu şekilde olacaktır:
Not:
Ana nokta, __syncthreads
blok çapında bir işlemdir ve tüm dişleri senkronize etmez.
512'den fazla iş parçacığı olan __syncthreads
bir blok oluşturabileceğiniz ve çözgü zamanlamasını işleyebildiğiniz için, eşitlenebilecek iş parçacıklarının tam sayısından emin değilim . Anladığım kadarıyla söylemek daha doğru: func1 en azından ilk 512 iş parçacığı için yürütülür .
Bu cevabı düzenlemeden önce (2010 yılında) 14x8x32 evre kullanılarak senkronize edildiğini ölçtüm __syncthreads
.
Birisi bunu daha doğru bir bilgi parçası için tekrar test ederse çok memnun olurum.
__syncthreads
blok çapında bir işlemdir ve tüm iş parçacıklarını gerçekten senkronize etmemesi, CUDA öğrencileri için bir sıkıntıdır. Bu yüzden cevabımı bana verdiğiniz bilgilere dayanarak güncelledim. Gerçekten onu takdir ederim.