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

Как организовать streamи с помощью графического процессора?

аппаратные средства

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

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

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

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

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

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

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

Обычно проще организовать (чтобы упростить индексирование изображения) streamи в 2D блоках с blockDim = 8 x 8 (64 streamа на блок). Я предпочитаю называть его 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 <<>>( /* params for the kernel function */ ); 

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

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

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

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

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

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

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

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

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

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

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

  • Моя карта NVIDIA постоянно ломалась сама по себе? Или я могу это исправить?
  • Проблема с ноутбуком + 2
  • Самый простой способ получить 3 или 4 монитора, работающие на Linux?
  • Могу ли я заставить свой ноутбук использовать NVIDIA вместо Intel?
  • Как управляется память CUDA?
  • 128-битное целое число на cuda?
  • CUDA определяет streamи на блок, блоки на каждую сетку
  • Ужасная производительность перерисовки DataGridView на одном из моих двух экранов
  • Ядро возврата Cuda
  • Внезапный «Нет сигнала» в соединении HDMI-DVI
  • Как установить 64-битный openGL в Linux
  • Давайте будем гением компьютера.