web-dev-qa-db-fra.com

Cuda gridDim et blockDim

J'ai ce que blockDim est, mais j'ai un problème avec gridDim. Blockdim donne la taille du bloc, mais qu'est-ce que gridDim? Sur Internet, il est écrit gridDim.x donne le nombre de blocs dans la coordonnée x.

Comment puis-je savoir ce que blockDim.x * gridDim.x donne?

Comment puis-je savoir que combien gridDim.x les valeurs sont-elles dans la ligne x?

Par exemple, considérez le code ci-dessous:

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;
}

Je sais que tid commence par 0. Le code a alors tid+=blockDim.x * gridDim.x. Qu'est-ce que tid maintenant après cette opération?

40
ehah
  • blockDim.x,y,z donne le nombre de threads dans un bloc, dans la direction particulière
  • gridDim.x,y,z donne le nombre de blocs dans une grille, dans la direction particulière
  • blockDim.x * gridDim.x donne le nombre de threads dans une grille (dans le sens x, dans ce cas)

les variables de bloc et de grille peuvent être à 1, 2 ou 3 dimensions. Il est courant lors de la manipulation de données 1D de ne créer que des blocs et des grilles 1D.

Dans la documentation CUDA, ces variables sont définies ici

En particulier, lorsque le nombre total de threads dans la dimension x (gridDim.x * blockDim.x) est inférieur à la taille du tableau que je souhaite processus, il est alors courant de créer une boucle et de déplacer la grille de threads dans l'ensemble du tableau. Dans ce cas, après avoir traité une itération de boucle, chaque thread doit ensuite se déplacer vers l'emplacement non traité suivant, qui est donné par tid+=blockDim.x*gridDim.x; En effet, toute la grille de threads saute dans le tableau de données 1D, une largeur de grille à la fois. Ce sujet, parfois appelé "boucle de quadrillage", est traité plus en détail dans ce article de blog .

Vous voudrez peut-être envisager de suivre quelques-uns des webinaires d'introduction CUDA disponibles sur la page du webinaire NVIDIA . Par exemple, ces 2:

  • GPU Computing à l'aide de CUDA C - Une introduction (2010) Une introduction aux bases de l'informatique GPU à l'aide de CUDA C. Les concepts seront illustrés par des procédures pas à pas d'exemples de code. Aucune expérience préalable en informatique GPU requise
  • GPU Computing utilisant CUDA C - Advanced 1 (2010) Techniques d'optimisation de premier niveau telles que l'optimisation globale de la mémoire et l'utilisation du processeur. Les concepts seront illustrés à l'aide d'exemples de code réels

Ce serait 2 heures bien dépensées, si vous voulez mieux comprendre ces concepts.

Le sujet général des boucles à quadrillage est traité en détail ici .

83
Robert Crovella

Paraphrasé du Guide de programmation CUDA :

gridDim: Cette variable contient les dimensions de la grille.

blockIdx: cette variable contient l'index de bloc dans la grille.

blockDim: cette variable et contient les dimensions du bloc.

threadIdx: cette variable contient l'index des threads dans le bloc.

Vous semblez un peu confus au sujet de la hiérarchie des threads que CUDA a; en un mot, pour un noyau, il y aura 1 grille (que je visualise toujours comme un cube en 3 dimensions). Chacun de ses éléments est un bloc, de telle sorte qu'une grille déclarée comme dim3 grid(10, 10, 2); aurait 10 * 10 * 2 blocs au total. À son tour, chaque bloc est un cube tridimensionnel de fils.

Cela dit, il est courant de n'utiliser que la dimension x des blocs et des grilles, ce à quoi ressemble le code dans votre question. Cela est particulièrement intéressant si vous travaillez avec des matrices 1D. Dans ce cas, votre ligne tid+=blockDim.x * gridDim.x Serait en fait l'index unique de chaque thread de votre grille. En effet, votre blockDim.x Aurait la taille de chaque bloc et votre gridDim.x Serait le nombre total de blocs.

Donc, si vous lancez un noyau avec des paramètres

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

alors dans votre noyau avait threadIdx.x + blockIdx.x*blockDim.x vous auriez effectivement:

threadIdx.x range from [0 ~ 128)

blockIdx.x range from [0 ~ 10)

blockDim.x equal to 128

gridDim.x equal to 10

Par conséquent, dans le calcul de threadIdx.x + blockIdx.x*blockDim.x, Vous auriez des valeurs dans la plage définie par: [0, 128) + 128 * [1, 10), Ce qui signifierait que vos valeurs de tid seraient comprises entre {0, 1, 2, ..., 1279}. Ceci est utile lorsque vous souhaitez mapper des threads à des tâches, car cela fournit un identifiant unique pour tous vos threads dans votre noyau.

Cependant, si vous avez

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

alors vous aurez essentiellement: tid = [0, 128) + 128 * [1, 10) + (128 * 10), et vos valeurs de tid iraient de {1280, 1281, ..., 2559} Je ne sais pas où cela serait pertinent, mais tout dépend de votre application et comment vous mappez vos threads à vos données. Ce mappage est assez central pour tout lancement de noyau, et c'est vous qui déterminez comment cela doit être fait. Lorsque vous lancez votre noyau, vous spécifiez les dimensions de la grille et du bloc, et c'est vous qui devez appliquer le mappage à vos données à l'intérieur de votre noyau. Tant que vous ne dépassez pas vos limites matérielles (pour les cartes modernes, vous pouvez avoir un maximum de 2 ^ 10 threads par bloc et 2 ^ 16 - 1 blocs par grille)

46
alrikai

Dans ce code source, nous avons même 4 threds, la fonction noyau peut accéder à l'ensemble des 10 tableaux. Comment?

#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 );
}

Pourquoi nous ne créons pas 10 threads ex) add<<<2,5>>> or add<5,2>>> Parce que nous devons créer un nombre raisonnablement petit de threads, si N est supérieur à 10 ex) 33 * 1024.

Ce code source est un exemple de ce cas. les tableaux sont 10, les threads cuda sont 4. Comment accéder aux 10 tableaux uniquement par 4 threads.

voir la page sur la signification de threadIdx, blockIdx, blockDim, gridDim dans le détail cuda.

Dans ce code source,

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

Notre nombre de threads est de 4, car 2 * 2 (blocs * thread).

Dans la fonction d'ajout de noyau, nous pouvons accéder à 0, 1, 2, 3 index du thread

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

①0 + 0 * 2 = 0

②1 + 0 * 2 = 1

③0 + 1 * 2 = 2

④1 + 1 * 2 = 3

Comment accéder au reste de l'index 4, 5, 6, 7, 8, 9. Il y a un calcul dans la boucle while

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

** premier appel du noyau **

-1 boucle: 0 + 2 * 2 = 4

-2 boucle: 4 + 2 * 2 = 8

-3 boucle: 8 + 2 * 2 = 12 (mais cette valeur est fausse, quand elle est sortie!)

** deuxième appel du noyau **

-1 boucle: 1 + 2 * 2 = 5

-2 boucle: 5 + 2 * 2 = 9

-3 boucle: 9 + 2 * 2 = 13 (mais cette valeur est fausse, quand elle est sortie!)

** troisième appel du noyau **

-1 boucle: 2 + 2 * 2 = 6

Boucle -2: 6 + 2 * 2 = 10 (mais cette valeur est fausse, tout en étant absent!)

** quatrième appel du noyau **

-1 boucle: 3 + 2 * 2 = 7

Boucle -2: 7 + 2 * 2 = 11 (mais cette valeur est fausse, tout en étant absent!)

Ainsi, tous les index de 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 peuvent accéder par valeur tid.

se référer à cette page. http://study.marearts.com/2015/03/to-process-all-arrays-by-reasonably.html Je ne peux pas télécharger d'image, car mauvaise réputation.

1
Jeonghyun Kim