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 Я не могу загрузить изображение, потому что низкая репутация.