2013-05-17 32 views
26

Ottengo ciò che blockDim è .. Ma ho un problema con gridDim. Blockdim indica la dimensione del blocco, ma che cos'è gridDim? Su Internet si dice che gridDim.x fornisce il numero di blocchi nella coordinata x.Cuda gridDim e blockDim

Come posso sapere che cosa offre blockDim.x * gridDim.x?

Come posso sapere quanti valori di gridDim.x ci sono nella linea x?

Ad esempio, si consideri il codice qui sotto:

int tid = threadIdx.x + blockIdx.x * blockDim.x; 
double temp = a[tid]; 
tid += blockDim.x * gridDim.x; 

while (tid < count) 
{ 
    if (a[tid] > temp) 
    { 
     temp = a[tid]; 
    } 
    tid += blockDim.x * gridDim.x; 
} 

so che tid inizia con 0. Il codice è quindi tid+=blockDim.x * gridDim.x. Cosa è tid ora dopo questa operazione?

risposta

59
  • blockDim.x,y,z dà il numero di thread in un blocco, in direzione particolare
  • gridDim.x,y,z fornisce il numero di blocchi in una griglia, in direzione particolare
  • blockDim.x * gridDim.x dà il numero di fili in una griglia (nella direzione x, in questo caso)

Le variabili di blocco e griglia possono essere 1, 2 o 3 dimensioni. È pratica comune quando si gestiscono dati 1-D per creare solo blocchi e griglie 1-D.

In particolare, quando i fili totali nella dimensione X (gridDim.x * blockDim.x) è inferiore la dimensione della matrice desidero elaborare, allora è pratica comune per creare un ciclo e avere la griglia di fili si sposta attraverso l'intero array. In questo caso, dopo l'elaborazione dell'iterazione di un ciclo, ciascun thread deve quindi spostarsi nella posizione successiva non elaborata, che è data da tid+=blockDim.x*gridDim.x; In effetti, l'intera griglia di thread sta saltando attraverso l'array 1-D di dati, una larghezza di griglia a un tempo. Questo argomento, a volte chiamato "loop striding grid", è ulteriormente discusso in questo blog article.

Si consiglia di prendere in considerazione la possibilità di prendere un paio di webinar introduttivi CUDA disponibili su NVIDIA webinar page. Ad esempio, questi 2:

  • GPU Computing utilizzando CUDA C - Introduzione (2010) Un'introduzione ai principi fondamentali di calcolo GPU utilizzando CUDA C. Concetti sarà illustrato con procedure dettagliate di esempi di codice. Nessuna esperienza precedente di GPU Computing richiesto
  • Computing GPU con CUDA C - Avanzato 1 (2010) Primo livello tecniche di ottimizzazione come ad esempio l'ottimizzazione della memoria globale, e utilizzo del processore. I concetti verranno illustrati utilizzando il codice reale esempi

Sarebbe 2 ore ben speso, se volete capire meglio questi concetti.

L'argomento generale dei loop strider è descritto in dettaglio here.

+0

Direi che la tua risposta è la migliore, +1 – alrikai

+0

@alrikai Stavo semplicemente aggiungendo alcuni commenti alla tua risposta quando l'hai cancellato (Poi in seguito hai ripubblicato, immagino). Prima eri tu, e la tua risposta va bene. –

+0

Sì, l'avevo postato per sbaglio a metà circa della sua scrittura (whoops) – alrikai

37

parafrasate dalla CUDA Programming Guide:

gridDim: Questa variabile contiene le dimensioni della griglia.

blockIdx: questa variabile contiene l'indice di blocco all'interno della griglia.

blockDim: questa variabile e contiene le dimensioni del blocco.

threadIdx: questa variabile contiene l'indice del thread all'interno del blocco.

Sembra che tu sia un po 'confuso riguardo alla gerarchia dei thread di CUDA; in poche parole, per un kernel ci sarà 1 griglia, (che visualizzo sempre come un cubo tridimensionale). Ciascuno dei suoi elementi è un blocco, in modo tale che una griglia dichiarata come dim3 grid(10, 10, 2); avrebbe blocchi 10 * 10 * 2 totali. A turno, ogni blocco è un cubo tridimensionale di thread.

Detto questo, è comune utilizzare solo la dimensione x dei blocchi e delle griglie, che è come appare il codice nella tua domanda. Ciò è particolarmente positivo se si lavora con array 1D. In tal caso, la tua riga tid+=blockDim.x * gridDim.x sarebbe in effetti l'indice univoco di ogni thread all'interno della griglia. Questo perché il tuo blockDim.x sarebbe la dimensione di ogni blocco e il tuo gridDim.x sarebbe il numero totale di blocchi.

Quindi, se si avvia un kernel con i parametri

dim3 block_dim(128,1,1); 
dim3 grid_dim(10,1,1); 
kernel<<<grid_dim,block_dim>>>(...); 

poi nel kernel aveva threadIdx.x + blockIdx.x*blockDim.x si avrebbe effettivamente avere:

threadIdx.x range from [0 ~ 128)

blockIdx.x range from [0 ~ 10)

blockDim.x equal to 128

gridDim.x equal to 10

Quindi nel calcolo threadIdx.x + blockIdx.x*blockDim.x, si avrebbe valori all'interno del range definito da: [0, 128) + 128 * [1, 10), il che significherebbe tuoi valori TID sarebbe compreso tra {0, 1, 2, ..., 1279}. Questo è utile quando si desidera mappare i thread alle attività, in quanto fornisce un identificatore univoco per tutti i thread nel kernel.

Tuttavia, se si dispone di

int tid = threadIdx.x + blockIdx.x * blockDim.x; 
tid += blockDim.x * gridDim.x; 

Allora ti essenzialmente avere: tid = [0, 128) + 128 * [1, 10) + (128 * 10), ei tuoi valori TID sarebbe compreso tra {1280, 1281, ..., 2559} non sono sicuro dove sarebbe rilevante, ma dipende tutto dalla tua applicazione e da come mappi i tuoi thread ai tuoi dati. Questa mappatura è piuttosto centrale per qualsiasi lancio del kernel, e tu sei quello che determina come dovrebbe essere fatto. Quando si avvia il kernel, si specificano le dimensioni della griglia e del blocco e si è quello che deve applicare la mappatura ai dati all'interno del kernel. Finché non superare i propri limiti hardware (per le carte moderne, si può avere un massimo di 2^10 thread per blocco e 2^16 - 1 isolati per thread)

+2

L'esempio concreto è stato molto utile, grazie. Molte persone ripetono semplicemente le definizioni di 'gridDim',' blockIdx', ecc., Ma l'esempio è vitale. – cmo

+0

Scusami signore, ma nella frase finale hai detto * puoi avere un massimo di 2^10 thread per blocco e 2^16 - 1 blocchi per thread * ma non dovrebbe essere: puoi avere un massimo di 2^10 thread per blocco e 2^16 - 1 blocchi per ** grid ** – meJustAndrew

1

In questo codice sorgente, abbiamo anche 4 thred, la funzione kernel può accedere a tutti i 10 array. Come?

#define N 10 //(33*1024) 

__global__ void add(int *c){ 
    int tid = threadIdx.x + blockIdx.x * gridDim.x; 

    if(tid < N) 
     c[tid] = 1; 

    while(tid < N) 
    { 
     c[tid] = 1; 
     tid += blockDim.x * gridDim.x; 
    } 
} 

int main(void) 
{ 
    int c[N]; 
    int *dev_c; 
    cudaMalloc((void**)&dev_c, N*sizeof(int)); 

    for(int i=0; i<N; ++i) 
    { 
     c[i] = -1; 
    } 

    cudaMemcpy(dev_c, c, N*sizeof(int), cudaMemcpyHostToDevice); 

    add<<< 2, 2>>>(dev_c); 
    cudaMemcpy(c, dev_c, N*sizeof(int), cudaMemcpyDeviceToHost); 

    for(int i=0; i< N; ++i) 
    { 
     printf("c[%d] = %d \n" ,i, c[i]); 
    } 

    cudaFree(dev_c); 
} 

Perché non creare 10 thread ex) add<<<2,5>>> or add<5,2>>> Perché dobbiamo creare ragionevolmente piccolo numero di thread, se N è maggiore di 10 ex) 33 * 1024.

Questo codice sorgente è un esempio di questo caso. Gli array sono 10, i thread cuda sono 4. Come accedere a tutti e 10 gli array solo per 4 thread.

vedere la pagina sul significato di threadIdx, blockIdx, blockDim, gridDim nel dettaglio cuda.

In questo codice sorgente,

gridDim.x : 2 this means number of block of x 

gridDim.y : 1 this means number of block of y 

blockDim.x : 2 this means number of thread of x in a block 

blockDim.y : 1 this means number of thread of y in a block 

nostro numero di filo sono 4, perché 2 * 2 (blocchi * filo).

In funzione del kernel aggiuntivo, si può accedere a 0, 1, 2, 3 indice di filo

->tid = threadIdx.x + blockIdx.x * blockDim.x

①0 + 0 * 2 = 0

②1 + 0 * 2 = 1

③0 + 1 * 2 = 2

④1 + 1 * 2 = 3

Come t o accesso resto punti 4, 5, 6, 7, 8, 9. C'è un calcolo nel ciclo while

tid += blockDim.x + gridDim.x in while 

** prima chiamata di kernel **

-1 ciclo: 0+ 2 * 2 = 4

-2 ciclo: 4 + 2 * 2 = 8

-3 ciclo: 8 + 2 * 2 = 12 (ma questo valore è falso, mentre fuori!)

** seconda chiamata del kernel **

.515.053.691,36321 milioni

-1 ciclo: 1 + 2 * 2 = 5

-2 ciclo: 5 + 2 * 2 = 9

-3 ciclo: 9 + 2 * 2 = 13 (ma questo valore è falso , mentre esce)

** terzo richiamo del kernel **

-1 ciclo: 2 + 2 * 2 = 6

-2 ciclo: 6 + 2 * 2 = 10 (ma questo il valore è falso, mentre fuori!)

** quarto invito del kernel **

-1 ciclo: 3 + 2 * 2 = 7

-2 ciclo: 7 + 2 * 2 = 11 (! Ma questo valore è falso, mentre fuori)

Quindi, tutti gli indici di 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 possono accedere in base al valore tid.

fare riferimento a questa pagina. http://study.marearts.com/2015/03/to-process-all-arrays-by-reasonably.html Non riesco a caricare l'immagine, perché la reputazione bassa.