Як організовано потоки для виконання GPU?
Як організовано потоки для виконання GPU?
Відповіді:
Якщо у пристрої GPU є, наприклад, 4 мультипроцесорні блоки, і вони можуть запускати по 768 потоків кожен: тоді в даний момент не більше 4 * 768 потоків буде дійсно паралельно (якщо ви запланували більше потоків, вони будуть чекати їх черга).
нитки організовані в блоки. Блок виконується багатопроцесорним блоком. Нитки блоку можна ідентифікувати (індексувати) за допомогою індексів 1Dimension (x), 2Dimensions (x, y) або 3Dim (x, y, z), але в будь-якому випадку x y z <= 768 для нашого прикладу (застосовуються інші обмеження до x, y, z, див. посібник та можливості вашого пристрою).
Очевидно, що якщо вам потрібно більше, ніж ті 4 * 768 потоків, вам потрібно більше 4-х блоків. Блоки можуть також індексуватися 1D, 2D або 3D. Існує черга блоків, які чекають входу в GPU (адже, у нашому прикладі, в GPU є 4 мультипроцесори і одночасно виконуються лише 4 блоки).
Припустимо, ми хочемо, щоб один потік обробив один піксель (i, j).
Ми можемо використовувати блоки по 64 потоки кожен. Тоді нам потрібно 512 * 512/64 = 4096 блоків (щоб мати 512x512 ниток = 4096 * 64)
Загальноприйнято організовувати (щоб полегшити індексацію зображення) потоки в 2D блоки, що мають blockDim = 8 x 8 (64 потоки на блок). Я вважаю за краще називати це темиPerBlock.
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;
Припустимо, графічний процесор 9800 ГТ:
https://www.tutorialspoint.com/cuda/cuda_threads.htm
Блок не може мати більш активних потоків, ніж 512, тому __syncthreads
може синхронізувати лише обмежену кількість потоків. тобто якщо ви виконаєте наступне з 600 потоками:
func1();
__syncthreads();
func2();
__syncthreads();
тоді ядро повинно працювати два рази, і порядок виконання буде таким:
Примітка:
Основним моментом є __syncthreads
операція, що працює в масштабі блоку, і вона не синхронізує всі потоки.
Я не впевнений у точній кількості потоків, які __syncthreads
можуть синхронізуватися, оскільки ви можете створити блок з більш ніж 512 потоками і дозволити основі обробляти планування. На мій погляд, точніше сказати: func1 виконується принаймні для перших 512 потоків.
Перш ніж редагувати цю відповідь (ще в 2010 році), я виміряв 14x8x32 нитки, які синхронізувались за допомогою __syncthreads
.
Я дуже вдячний, якщо хтось перевірить це ще раз для отримання більш точної інформації.
__syncthreads
це загальноблокова операція, і той факт, що він фактично не синхронізує всі потоки, є неприємністю для студентів CUDA. Тож я оновив свою відповідь на основі інформації, яку ви мені дали. Я дійсно ціную це.