CUDA ızgara boyutlarını, blok boyutlarını ve iş parçacığı organizasyonunu anlama (basit açıklama) [kapalı]


161

Bir GPU tarafından yürütülecek iş parçacıkları nasıl düzenlenir?


Bunun için CUDA Programlama Kılavuzu iyi bir başlangıç ​​olmalıdır. Ayrıca CUDA tanıtımını buradan kontrol etmenizi de tavsiye ederim .
Tom

Yanıtlar:


287

Donanım

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ı).

Yazılım

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).

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

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;

11
Her blok 768 iş parçacığı çalıştırabiliyorsa, neden yalnızca 64 kullanıyorsunuz? 768 maksimum sınırını kullanırsanız, daha az blok ve daha iyi performans elde edersiniz.
Aliza

10
@Aliza: bloklar mantıklı , her fiziksel işlem birimi için 768 iş parçacığı sınırı . İşi iş parçacıklarına dağıtmak için probleminizin özelliklerine göre bloklar kullanırsınız. Sahip olduğunuz her sorun için her zaman 768 iş parçacığının bloklarını kullanmanız olası değildir. 64x64 boyutunda bir görüntüyü (4096 piksel) işlemeniz gerektiğini düşünün. 4096/768 = 5.333333 blok?
cibercitizen1

1
blok mantıklıdır, ancak her blok bir çekirdeğe atanır. çekirdekten daha fazla blok varsa, çekirdekler serbest kalana kadar bloklar sıraya alınır. Örneğinizde 6 blok kullanabilir ve ekstra dişlerin hiçbir şey yapmamasını sağlayabilirsiniz (6. bloktaki dişlerin 2 / 3'ü).
Aliza

3
Bence Aliza'nın amacı iyi bir nokta: mümkünse, blok başına mümkün olduğunca çok iş parçacığı kullanmak istiyor. Daha az iş parçacığı gerektiren bir kısıtlama varsa, bunun ikinci bir örnekte neden böyle olabileceğini açıklamak daha iyidir (ancak yine de daha basit ve daha arzu edilen durumu ilk önce açıklayın).

6
@thouis Evet, belki. Ancak durum, her bir iş parçacığı için gereken bellek miktarının uygulamaya bağlı olmasıdır. Örneğin, son programımda, her iş parçacığı "çok" bellek gerektiren en küçük kareler optimizasyon işlevini çağırır. Öyle ki, bu bloklar 4x4 iplikten daha büyük olamaz. Buna rağmen, elde edilen hız, sıralı versiyona kıyasla dramatikti.
22.11.2012

9

Bir 9800GT GPU varsayalım:

  • 14 çok işlemcili (SM)
  • her SM'de 8 iş parçacığı işlemcisi vardır (AKA akış işlemcileri, SP veya çekirdekler)
  • blok başına 512 adede kadar iş parçacığına izin verir
  • warpsize 32'dir (bu, 14x8 = 112 iş parçacığının her birinin 32 iş parçacığına kadar zamanlayabileceği anlamına gelir)

https://www.tutorialspoint.com/cuda/cuda_threads.htm

Bir blok 512'den daha fazla aktif iş parçacığına sahip __syncthreadsolamaz, 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:

  1. func1 ilk 512 iş parçacığı için yürütülür
  2. func2 ilk 512 iş parçacığı için yürütülür
  3. func1 kalan iş parçacıkları için yürütülür
  4. func2 kalan iş parçacıkları için yürütülür

Not:

Ana nokta, __syncthreadsblok çapında bir işlemdir ve tüm dişleri senkronize etmez.


512'den fazla iş parçacığı olan __syncthreadsbir 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.


Func2 (), func1 () sonuçlarına bağlıysa ne olur? Sanırım bu yanlış
Chris

@Chris Bunu yedi yıl önce yazdım, ancak doğru hatırlarsam, bu konuda bir test yaptım ve gpu'dan daha fazla iş parçacığına sahip çekirdeklerin bu şekilde davrandığı sonucuna vardım. Bu vakayı test edip farklı bir sonuca ulaştıysanız, bu yayını silmem gerekecek.
Bizhan

Üzgünüz, bunun yanlış olduğunu düşünüyorum, ayrıca GPU sadece aynı anda 112 iş parçacığı çalıştırabilir.
Steven Lu

@StevenLu denediniz mi? Ayrıca 112 eşzamanlı iş parçacığının bir GPU için anlamlı olduğunu düşünmüyorum. 112, akış işlemcisi sayısıdır. Şimdi
CUDA'yı

1
@StevenLu burada maksimum iş parçacığı sayısı değil, __syncthreadsblok ç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.
Bizhan
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.