Понимание размеров сетки CUDA, размеров блоков и организации потоков (простое объяснение) [закрыто]
Вопрос
Как организованы потоки для выполнения графическим процессором?
Решение
Аппаратное обеспечение
Если устройство 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 выполняется для остальных потоков