Понимание размеров сетки CUDA, размеров блоков и организации потоков (простое объяснение) [закрыто]

StackOverflow https://stackoverflow.com/questions/2392250

  •  25-09-2019
  •  | 
  •  

Вопрос

Как организованы потоки для выполнения графическим процессором?

Это было полезно?

Решение

Аппаратное обеспечение

Если устройство GPU имеет, например, 4 многопроцессорных блока, и они могут запускать 768 потоков каждый: затем в данный момент не более 4 * 768 потоков будут действительно работать параллельно (если вы планируете больше потоков, они будут ждать их поворот).

Программное обеспечение

Темы организованы в блоках. Блок выполняется многопроцессорной единицей. Нити блока могут быть определены (индексированы) с использованием 1-меганизма (x), 2-менструкции (x, y) или указателей 3dim (x, y, z), но в любом случае xуz <= 768 Для нашего примера (другие ограничения применяются к x, y, z, см. Руководство и возможности вашего устройства).

Очевидно, что если вам нужно больше, чем эти 4 * 768 потоков, вам нужно более 4 блоков. Блоки могут быть также проиндексированы 1D, 2D или 3D. Есть очередь блоков, ожидающих ввода графического процессора (потому что в нашем примере GPU имеет 4 многопроцессора, и только 4 блока выполняются одновременно).

Теперь простой случай: обработка изображения 512x512

Предположим, мы хотим, чтобы одна нить обрабатывала один пиксель (I, j).

Мы можем использовать блоки из 64 потоков каждый. Тогда нам нужно 512 * 512/64 = 4096 блоков (так, чтобы иметь 512x512 потоки = 4096 * 64)

Общим организовать (чтобы упростить индексацию изображения) потоки в 2D блоках, имеющих BLOCKDIM = 8 x 8 (64 потоки на блок). Я предпочитаю называть это ThreadsPerblock.

dim3 threadsPerBlock(8, 8);  // 64 threads

и 2D GRIDDIM = 64 x 64 блока (необходимые блоки 4096). Я предпочитаю называть это Numblocks.

dim3 numBlocks(imageWidth/threadsPerBlock.x,  /* for instance 512/8 = 64*/
              imageHeight/threadsPerBlock.y); 

Ядро запускается так:

myKernel <<<numBlocks,threadsPerBlock>>>( /* params for the kernel function */ );       

Наконец: будет что-то вроде «очередь 4096 блоков», где блок ждет, чтобы быть присвоен одному из многопроцессоров GPU для выполнения его 64 потоков.

В ядре вычисляется пиксель (i, j), который будет обработан нитью, рассчитывается таким образом:

uint i = (blockIdx.x * blockDim.x) + threadIdx.x;
uint j = (blockIdx.y * blockDim.y) + threadIdx.y;

Другие советы

предположим, что графический процессор 9800GT:14 мультипроцессоров, каждый из которых имеет по 8 потоковых процессоров, а размер warpsize равен 32, что означает, что каждый потоковый процессор обрабатывает до 32 потоков.14*8*32=3584 — максимальное количество фактически одновременных потоков.

если вы выполняете это ядро ​​с более чем 3584 потоками (скажем, 4000 потоков, и не важно, как вы определяете блок и сетку.GPU будет относиться к ним одинаково):

func1();
__syncthreads();
func2();
__syncthreads();

тогда порядок выполнения этих двух функций следующий:

1.func1 выполняется для первых 3584 потоков.

2.func2 выполняется для первых 3584 потоков.

3.func1 выполняется для остальных потоков

4.func2 выполняется для остальных потоков

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top