CUDA come ottenere griglia, blocco, dimensione della filettatura e parallelizzare il calcolo della matrice non quadrata

Sono nuovo di CUDA e ho bisogno di aiuto per capire alcune cose. Ho bisogno di aiuto per parallelizzare questi due cicli. In particolare come configurare il dimBlock e il dimGrid per velocizzare l’esecuzione. So che questo sembra l’esempio di aggiunta del vettore nel sdk ma quell’esempio è solo per le matrici quadrate e quando provo a modificare quel codice per la mia matrice 128 x 1024 non funziona correttamente.

__global__ void mAdd(float* A, float* B, float* C) { for(int i = 0; i < 128; i++) { for(int i = 0; i < 1024; i++) { C[i * 1024 + j] = A[i * 1024 + j] + B[i * 1024 + j]; } } } 

Questo codice fa parte di un loop più grande ed è la parte più semplice del codice, quindi ho deciso di provare a paragonare il thia e ad imparare CUDA allo stesso tempo. Ho letto le guide ma ancora non capisco come ottenere il giusto no. di griglie / blocco / thread in corso e usarli in modo efficace.

Come hai scritto, quel kernel è completamente seriale. Ogni thread lanciato per eseguirlo eseguirà lo stesso lavoro.

L’idea principale alla base di CUDA (e OpenCL e altri simili modelli di programmazione di tipo “singolo programma, più dati”) è che si esegua un’operazione “dati paralleli”, quindi si deve eseguire molte volte la stessa operazione, in gran parte indipendente, e scrivi un kernel che esegue quell’operazione. Viene quindi avviato un gran numero di thread (semi) autonomi per eseguire tale operazione attraverso il set di dati di input.

Nell’esempio di addizione dell’array, l’operazione parallela dei dati è

 C[k] = A[k] + B[k]; 

per tutti i k tra 0 e 128 * 1024. Ogni operazione di aggiunta è completamente indipendente e non ha requisiti di ordinamento e pertanto può essere eseguita da un thread diverso. Per esprimere questo in CUDA, si potrebbe scrivere il kernel in questo modo:

 __global__ void mAdd(float* A, float* B, float* C, int n) { int k = threadIdx.x + blockIdx.x * blockDim.x; if (k < n) C[k] = A[k] + B[k]; } 

[disclaimer: codice scritto nel browser, non testato, usato a proprio rischio]

Qui, il ciclo interno ed esterno dal codice seriale sono sostituiti da un thread CUDA per operazione e ho aggiunto un controllo limite nel codice in modo che nei casi in cui vengono avviati più thread rispetto alle operazioni richieste, non può verificarsi un overflow del buffer. Se il kernel viene quindi lanciato in questo modo:

 const int n = 128 * 1024; int blocksize = 512; // value usually chosen by tuning and hardware constraints int nblocks = n / nthreads; // value determine by block size and total work madd<<>>mAdd(A,B,C,n); 

Quindi 256 blocchi, ciascuno contenente 512 thread, verranno lanciati sull'hardware della GPU per eseguire l'operazione di aggiunta dell'array in parallelo. Si noti che se la dimensione dei dati di input non fosse espressiva come un bel multiplo rotondo della dimensione del blocco, il numero di blocchi dovrebbe essere arrotondato per coprire l'intero set di dati di input.

Tutto quanto sopra è una panoramica estremamente semplificata del paradigma CUDA per un'operazione molto banale, ma forse fornisce abbastanza informazioni per poter continuare da soli. CUDA è piuttosto maturo al giorno d'oggi e c'è un sacco di materiale educativo buono e gratuito che gira intorno al web che probabilmente si può usare per illuminare ulteriormente molti aspetti del modello di programmazione che ho illustrato in questa risposta.