Как организованы потоки с помощью графического процессора?
Понимание размеров сетки CUDA, размеров блоков и организации потоков (простое объяснение)
Ответ 1
Оборудование
Если у устройства GPU есть, например, 4 блока многопроцессорности, и они могут запускать 768 потоков каждый: тогда в данный момент не более 4 * 768 потоков будут выполняться параллельно (если вы планируете больше потоков, они будут ждать своей очереди).
Программное обеспечение
потоки организованы в виде блоков. Блок выполняется многопроцессорным блоком. Нити блока могут быть проиндексированы (индексированы) с использованием 1Dimension (x), 2Dimensions (x, y) или 3Dim-индексов (x, y, z), но в любом случае xyz <= 768 для нашего примера (применяются другие ограничения x, y, z, см. руководство и возможности вашего устройства).
Очевидно, что если вам нужно больше, чем те 4 * 768 потоков, вам нужно больше 4 блоков. Блоки также могут быть проиндексированы 1D, 2D или 3D. Существует очередь блоков, ожидающих входа GPU (потому что в нашем примере GPU имеет 4 многопроцессора и всего 4 блока выполняются одновременно).
Теперь простой случай: обработка изображения 512x512
Предположим, мы хотим, чтобы один поток обрабатывал один пиксель (i, j).
Мы можем использовать блоки по 64 потока каждый. Тогда нам нужно 512 * 512/64 = 4096 блоков (поэтому иметь 512x512 потоков = 4096 * 64)
Как правило, организовать (чтобы упростить индексирование изображения) потоки в 2D блоках с blockDim = 8 x 8 (64 потока на блок). Я предпочитаю называть его threadPerBlock.
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;
Ответ 2
предположим, что GPU 9800GT: 14 многопроцессоров, каждая из которых имеет 8 потоковых процессоров, а warpsize - 32, что означает, что каждый потокпроцессор обрабатывает до 32 потоков. 14 * 8 * 32 = 3584 - это максимальное количество потоков culturrent для исполнительного механизма.
если вы выполняете это ядро с более чем 3584 потоками (скажем, 4000 потоков, и это не важно, как вы определяете блок и сетку. gpu будет относиться к ним как к одному):
func1();
__syncthreads();
func2();
__syncthreads();
то порядок выполнения этих двух функций выглядит следующим образом:
1.func1 выполняется для первых 3584 потоков
2.func2 выполняется для первых 3584 потоков
3.func1 выполняется для остальных потоков
4.func2 выполняется для остальных потоков
Ответ 3
Руководство по программированию CUDA должно стать хорошим местом для этого. Я также рекомендовал бы проверить введение CUDA из здесь.