Soru CUDA ızgara boyutlarını, blok boyutlarını ve iplik organizasyonunu anlama (basit açıklama) [kapalı]


Bir iş parçacığı bir GPU tarafından nasıl yürütülür?


130
2018-03-06 11:08


Menşei


CUDA Programlama Rehberi bunun için başlamak için iyi bir yer olmalıdır. Ayrıca CUDA tanıtımını da kontrol etmenizi tavsiye ederim. İşte. - Tom


Cevaplar:


Donanım

Eğer bir GPU cihazı, örneğin, 4 çok işlemcili üniteye sahipse ve her biri 768 iş parçacığı çalıştırabilirlerse: o anda, en fazla 4 * 768 iş parçacığı, paralel olarak çalışacaktır (daha fazla iş parçacığı planladıysanız, bunlar bekler) onların sırası).

Yazılım

iş parçacığı bloklar halinde düzenlenmiştir. Bir çok işlemli birim tarafından bir blok yürütülür. Bir bloğun iş parçacıkları 1Dimension (x), 2Dimensions (x, y) veya 3Dim indeksleri (x, y, z) kullanılarak indekslenebilir (indekslenebilir) ancak her durumda xyÖrnek için z <= 768 (diğer kısıtlamalar x, y, z için geçerlidir, kılavuza ve cihazınızın kapasitesine bakın).

Açıkçası, 4 * 768 konudan daha fazlasına ihtiyacınız varsa 4 bloktan daha fazlasına ihtiyacınız var. Bloklar ayrıca 1D, 2D veya 3D olarak indekslenebilir. Girmek için bekleyen bir sıra kuyruğu var. GPU (çünkü bizim örneğimizde, GPU'da 4 çok işlemcili var ve sadece 4 blok var eşzamanlı olarak yürütülmektedir).

Şimdi basit bir durum: 512x512 görüntü işleme

Tek bir iş parçacığının bir piksel (i, j) işlemesini istediğimizi varsayalım.

Her biri 64 iş parçacığı blokları kullanabiliriz. O zaman 512 * 512/64 = 4096 bloğa ihtiyacımız var. (512x512 thread = 4096 * 64 olması için)

BlockDim = 8 x 8 (blok başına 64 iş parçacığı) içeren 2B bloklardaki iş parçacıklarının düzenlenmesi (görüntüyü daha kolay indekslemek) yaygındır. ThreadPerBlock demeyi tercih ederim.

dim3 threadsPerBlock(8, 8);  // 64 threads

ve 2D gridDim = 64 x 64 blok (4096 blok gerekli). Ben numBlocks demeyi tercih ederim.

dim3 numBlocks(imageWidth/threadsPerBlock.x,  /* for instance 512/8 = 64*/
              imageHeight/threadsPerBlock.y); 

Çekirdek şu şekilde başlatıldı:

myKernel <<<numBlocks,threadsPerBlock>>>( /* params for the kernel function */ );       

Son olarak: "4096 bloğu kuyruğu" gibi bir şey olacak, burada bir blok, 64 parçacığı icra etmek için GPU'nun çok işlemcilerinin birine atanmayı bekliyor.

Çekirdeğinde 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;

251
2018-03-06 11:16



Her blok 768 iş parçacığı çalıştırabilir, neden yalnızca 64 kullanıyor? Maksimum 768 limitini kullanırsanız, daha az blok ve daha iyi performansa sahip olursunuz. - Aliza
@ Aliza: bloklar mantıksal, 768 iş parçacığı sınırı fiziksel işleme ünitesi. İşi iş parçacığına dağıtmak için, sorunların özelliklerine göre blokları kullanırsınız. Sahip olduğunuz her sorun için her zaman 768 iş parçacığı bloğunu kullanmanız olası değildir. Bir 64x64 görüntü (4096 piksel) işlemek zorunda olduğunuzu düşünün. 4096/768 = 5.333333 blok? - cibercitizen1
@ cibercitizen1 - Bence Aliza'nın amacı iyi bir şeydir: eğer mümkünse, blok başına mümkün olduğunca çok sayıda iplik kullanmak ister. Daha az iplik gerektiren bir kısıtlama varsa, ikinci bir örnekte bunun neden olabileceğini daha iyi açıklayın (ama yine de daha basit ve daha arzu edilen durumu açıklayın).
@thouis Evet, belki. Ancak durum, her iş parçacığı için gereken bellek miktarının uygulamaya bağlı olmasıdır. Örneğin, son programımda, her bir iş parçacığı, "çok fazla" bellek gerektiren, en küçük kareyi optimize etme işlevini çağırır. O kadar ki, bu bloklar 4x4 iplikten daha büyük olamaz. Yine de, elde edilen hız, dramatik ve sıralı versiyona benziyordu. - cibercitizen1
@MySchizoBuddy Görüntüyü pürüzsüzleştirmeli, piksel eklemeli (veya kırparak, pikselleri çıkartarak), böylece 2 boyutta bir güce uymalısınız. - cibercitizen1


bir 9800GT GPU varsayalım: Her biri işlemcinin 32 iş parçacığına kadar işlediği anlamına gelen 14 çok işlemcili, her biri 8 iş parçacığı ve çözgü 32'dir. 14 * 8 * 32 = 3584, maksimum aktif cuncurrent iş parçacığı sayısıdır.

Eğer bu çekirdeği 3584'ten fazla iş parçacığı ile çalıştırırsanız (4000 iş parçacığı ve blok ve ızgarayı nasıl tanımladığınız önemli değildir. gpu bunları aynı şekilde ele alır):

func1();
__syncthreads();
func2();
__syncthreads();

Daha sonra bu iki fonksiyonun yürütme sırası şöyledir:

1.func1 ilk 3584 iş parçacığı için çalıştırıldı

2.Func2 ilk 3584 konu için çalıştırıldı

3.func1 kalan konular için yürütülür

4.Func2 kalan threadlar için çalıştırılır


6
2018-06-14 06:25



Func2 () işlevi func1 () sonuçlarına bağlıysa ne olur? Bence bu yanlış - Chris
@Chris Ben bunu yedi yıl önce yazdım, ama eğer doğru bir şekilde hatırlarsam, bu konuda bir test yaptım ve bu sonuca ulaşmak için, gpu'dan daha çok ileti dizisi içeren çekirdekler bu şekilde davranıyor. Bu davayı test edip farklı bir sonuca ulaşırsanız, bu mesajı silmem gerekecek. - Bijan