Cuda gridDim и blockDim

Я получаю, что такое blockDim.. Но у меня проблема с gridDim. Blockdim дает размер блока, но что такое gridDim? В Интернете говорится, что gridDim.x дает количество блоков в координате x.

Как я могу узнать, что blockDim.x * gridDim.x?

Как узнать, сколько значений gridDim.x есть в строке x?

Например, рассмотрите следующий код:

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

Я знаю, что tid начинается с 0. Затем код имеет tid+=blockDim.x * gridDim.x. Что теперь tid после этой операции?

Ответ 1

  • blockDim.x,y,z задает количество потоков в блоке, в определенном направлении
  • gridDim.x,y,z задает количество блоков в сетке в определенном направлении
  • blockDim.x * gridDim.x дает количество потоков в сетке (в этом случае в направлении x)

переменные блока и сетки могут быть 1, 2 или 3 мерными. Это обычная практика при обработке одномерных данных для создания только одномерных блоков и сеток.

В документации CUDA эти переменные определены здесь

В частности, когда общие потоки в x-размерности (gridDim.x * blockDim.x) меньше размера массива, который я хочу обработать, тогда обычной практикой является создание цикла и перемещение сетки потоков весь массив. В этом случае после обработки одной итерации цикла каждый поток должен перейти в следующее необработанное местоположение, которое задается tid+=blockDim.x*gridDim.x; По сути, вся сетка потоков перескакивает через 1-D массив данных, ширина сетки за раз. Эта тема, иногда называемая "сетчатым шаговым циклом", далее обсуждается в этой статье блога.

Возможно, вам захочется рассмотреть пару вводных веб-семинаров CUDA на веб- странице NVIDIA. Например, эти 2:

  • GPU Computing с использованием CUDA C - Введение (2010) Введение в основы вычислений GPU с использованием CUDA C. Концепции будут проиллюстрированы прохождением образцов кода. Не требуется предварительный опыт работы в GPU
  • GPU Computing с использованием CUDA C - Advanced 1 (2010) Методы оптимизации первого уровня, такие как оптимизация глобальной памяти и использование процессора. Концепции будут проиллюстрированы с использованием реальных примеров кода

Было бы потрачено 2 часа, если вы хотите лучше понять эти концепции.

Общая тема петель сетки-шагающей покрыта в некоторых деталях здесь.

Ответ 2

Перефразируемый из руководства по программированию CUDA:

gridDim: эта переменная содержит размеры сетки.

blockIdx: эта переменная содержит индекс блока в сетке.

blockDim: эта переменная и содержит размеры блока.

threadIdx: эта переменная содержит индекс потока внутри блока.

Кажется, вы немного смущены нитью, которая имеет CUDA; в двух словах, для ядра будет 1 сетка (которую я всегда представляю как 3-мерный куб). Каждый из его элементов является блоком, так что сетка объявлена dim3 grid(10, 10, 2); будет иметь 10 * 10 * 2 общих блоков. В свою очередь, каждый блок представляет собой трехмерный куб нитей.

С учетом сказанного, обычно используется только x-размерность блоков и сеток, что похоже на то, что делает код в вашем вопросе. Это особенно актуально, если вы работаете с 1D массивами. В этом случае ваша tid+=blockDim.x * gridDim.x фактически станет уникальным индексом каждого потока в вашей сетке. Это связано с тем, что ваш blockDim.x будет размером каждого блока, а ваш gridDim.x будет общим количеством блоков.

Поэтому, если вы запускаете ядро с параметрами

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

то в вашем ядре был threadIdx.x + blockIdx.x*blockDim.x вас было бы эффективно:

threadIdx.x range from [0 ~ 128)

blockIdx.x range from [0 ~ 10)

blockDim.x equal to 128

gridDim.x equal to 10

Следовательно, при вычислении threadIdx.x + blockIdx.x*blockDim.x вас будут значения в пределах диапазона, определяемого: [0, 128) + 128 * [1, 10), что означает, что ваши значения tid будут варьироваться от {0, 1, 2,..., 1279}. Это полезно, когда вы хотите сопоставить потоки задачам, поскольку это обеспечивает уникальный идентификатор для всех ваших потоков в вашем ядре.

Однако, если у вас есть

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

то вы по существу будете иметь: tid = [0, 128) + 128 * [1, 10) + (128 * 10), а ваши значения tid будут варьироваться от {1280, 1281,..., 2559} Я не знаю, где это будет актуально, но все зависит от вашего приложения и того, как вы сопоставляете свои потоки с вашими данными. Это сопоставление очень важно для любого запуска ядра, и именно вы определяете, как это должно быть сделано. Когда вы запускаете ядро, вы указываете размеры сетки и блока, и именно вы должны обеспечить соответствие данных вашим данным внутри вашего ядра. Пока вы не превышаете лимиты вашего оборудования (для современных карт вы можете иметь максимум 2 ^ 10 потоков на блок и 2 ^ 16 - 1 блок на поток)

Ответ 3

В этом исходном коде у нас даже есть 4 threds, функция ядра может обращаться ко всем 10 массивам. Как?

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

Почему мы не создаем 10 потоков ex) add<<<2,5>>> or add<5,2>>> Потому что нам нужно создать достаточно малое количество потоков, если N больше 10 бывших) 33 * 1024,

Этот исходный код является примером этого случая. массивы - 10, потоки cuda - 4. Как получить доступ ко всем 10 массивам только по 4 потокам.

см. страницу о значении threadIdx, blockIdx, blockDim, gridDim в деталях cuda.

В этом исходном коде,

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

Наш номер потока равен 4, потому что 2 * 2 (блокирует поток).

В добавление функции ядра мы можем получить доступ к индексу потока 0, 1, 2, 3

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

①0 + 0 * 2 = 0

②1 + 0 * 2 = 1

③0 + 1 * 2 = 2

④1 + 1 * 2 = 3

Как получить доступ к остальным индексам 4, 5, 6, 7, 8, 9. Есть расчет в цикле while

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

** первый вызов ядра **

-1: 0 + 2 * 2 = 4

-2: 4 + 2 * 2 = 8

-3: 8 + 2 * 2 = 12 (но это значение ложно, а вне!)

** второй вызов ядра **

-1:1 + 2 * 2 = 5

-2: 5 + 2 * 2 = 9

-3: 9 + 2 * 2 = 13 (но это значение ложно, а вне!)

** третий вызов ядра **

-1: 2 + 2 * 2 = 6

-2: 6 + 2 * 2 = 10 (но это значение ложно, а вне!)

** четвертый вызов ядра **

-1: 3 + 2 * 2 = 7

-2: 7 + 2 * 2 = 11 (но это значение ложно, а вне!)

Таким образом, все индексы 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 могут получить доступ по значению tid.

обратитесь к этой странице. http://study.marearts.com/2015/03/to-process-all-arrays-by-reasonably.html Я не могу загрузить изображение, потому что низкая репутация.