Come sono organizzati i thread per essere eseguiti da una GPU?
Come sono organizzati i thread per essere eseguiti da una GPU?
Risposte:
Se un dispositivo GPU ha, ad esempio, 4 unità multiprocessore e può eseguire 768 thread ciascuno: in un determinato momento non più di 4 * 768 thread funzioneranno in parallelo (se hai pianificato più thread, saranno in attesa il loro turno).
le discussioni sono organizzate in blocchi. Un blocco viene eseguito da un'unità multiprocessore. I thread di un blocco possono essere identificati (indicizzati) usando 1Dimension (x), 2Dimensions (x, y) o 3Dim indexs (x, y, z) ma in ogni caso x y z <= 768 per il nostro esempio (si applicano altre restrizioni a x, y, z, consultare la guida e le funzionalità del dispositivo).
Ovviamente, se hai bisogno di più di quei thread 4 * 768 hai bisogno di più di 4 blocchi. I blocchi possono anche essere indicizzati 1D, 2D o 3D. C'è una coda di blocchi in attesa di entrare nella GPU (perché, nel nostro esempio, la GPU ha 4 multiprocessori e solo 4 blocchi vengono eseguiti contemporaneamente).
Supponiamo di volere che un thread elabori un pixel (i, j).
Possiamo usare blocchi di 64 thread ciascuno. Quindi abbiamo bisogno di 512 * 512/64 = 4096 blocchi (quindi per avere 512x512 thread = 4096 * 64)
È comune organizzare (per facilitare l'indicizzazione dell'immagine) i thread nei blocchi 2D con blockDim = 8 x 8 (i 64 thread per blocco). Preferisco chiamarlo threadPerBlock.
dim3 threadsPerBlock(8, 8); // 64 threads
e 2D gridDim = 64 x 64 blocchi (i 4096 blocchi necessari). Preferisco chiamarlo numBlocks.
dim3 numBlocks(imageWidth/threadsPerBlock.x, /* for instance 512/8 = 64*/
imageHeight/threadsPerBlock.y);
Il kernel viene lanciato in questo modo:
myKernel <<<numBlocks,threadsPerBlock>>>( /* params for the kernel function */ );
Infine: ci sarà qualcosa come "una coda di 4096 blocchi", in cui un blocco è in attesa di essere assegnato a uno dei multiprocessori della GPU per ottenere i suoi 64 thread eseguiti.
Nel kernel il pixel (i, j) da elaborare con un thread viene calcolato in questo modo:
uint i = (blockIdx.x * blockDim.x) + threadIdx.x;
uint j = (blockIdx.y * blockDim.y) + threadIdx.y;
Supponiamo che una GPU 9800GT:
https://www.tutorialspoint.com/cuda/cuda_threads.htm
Un blocco non può avere più thread attivi di 512 quindi __syncthreads
può solo sincronizzare un numero limitato di thread. vale a dire se si esegue quanto segue con 600 thread:
func1();
__syncthreads();
func2();
__syncthreads();
quindi il kernel deve essere eseguito due volte e l'ordine di esecuzione sarà:
Nota:
Il punto principale __syncthreads
è un'operazione a livello di blocco e non sincronizza tutti i thread.
Non sono sicuro del numero esatto di thread che è __syncthreads
possibile sincronizzare, poiché è possibile creare un blocco con più di 512 thread e lasciare che l'ordito gestisca la pianificazione. Secondo la mia comprensione è più preciso dire: func1 viene eseguito almeno per i primi 512 thread.
Prima di modificare questa risposta (nel 2010), ho misurato che i thread 14x8x32 sono stati sincronizzati usando __syncthreads
.
Gradirei molto se qualcuno lo testasse nuovamente per un'informazione più accurata.
__syncthreads
è un'operazione a livello di blocco e il fatto che non sincronizzi effettivamente tutti i thread è un fastidio per gli studenti CUDA. Quindi ho aggiornato la mia risposta in base alle informazioni che mi hai fornito. Lo apprezzo molto.