Понимание размеров сетки CUDA, размеров блоков и организации потоков (простое объяснение) [закрыто]
Как организованы потоки для выполнения графическим процессором?
2 ответа:
оборудование
если устройство GPU имеет, например, 4 многопроцессорных блока, и они могут запускать 768 потоков каждый: то в данный момент не более 4*768 потоков будут действительно работать параллельно (если вы запланировали больше потоков, они будут ждать своей очереди).
программа
потоки организованы в блоки. Блок выполняется многопроцессорным блоком. Потоки блока могут быть идентифицированы (индексированы) с помощью 1Dimension (x), 2Dimensions (x, y) или 3dim индексы (x, y, z) но в любом случае xyz
очевидно, что если вам нужно больше, чем эти 4*768 потоков вам нужно больше, чем 4 блока. Блоки могут быть также проиндексированы в 1D, 2D или 3D. Там очереди блоков, ожидающих входа GPU (потому что, в нашем примере, GPU имеет 4 мультипроцессора и только 4 блока являются выполняется одновременно).
теперь простой случай: обработка изображения с разрешением 512 х 512
предположим,что мы хотим, чтобы один поток обрабатывал один пиксель (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 блоков необходимый.) Я предпочитаю называть это тупиками.
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-максимальное количество фактических потоков cuncurrent.
Если вы выполняете это ядро с более чем 3584 потоками (скажем, 4000 потоков, и не важно, как вы определяете блок и сетку. gpu будет относиться к ним так же):
func1(); __syncthreads(); func2(); __syncthreads();
затем порядок выполнения этих двух функций, как следует:
1.func1 выполняется для первых 3584 потоков
2.func2 выполняется для первых 3584 потоков
3.func1 выполняется для остальных потоков
4.func2 выполняется для остальных потоков