Вот код, который я использую, чтобы разделить задание, требующее num_threads
, на блок и сетку. Да, вы можете запустить много блоков (но только очень мало), и у вас, вероятно, будет больше реальных потоков, чем требуется, но это легко и эффективно. См. Второй пример кода ниже для моей простой проверки границ в ядре.
PS: у меня всегда есть block_size == 128
, потому что это был хороший компромисс между занятостью многоядерности, использованием регистров, требованиями к общей памяти и коалесцентным доступом для всех моих ядер.
Код для расчета хорошего размера сетки (хост):
#define GRID_SIZE 65535
//calculate grid size (store result in grid/block)
void kernelUtilCalcGridSize(unsigned int num_threads, unsigned int block_size, dim3* grid, dim3* block) {
//block
block->x = block_size;
block->y = 1;
block->z = 1;
//number of blocks
unsigned int num_blocks = kernelUtilCeilDiv(num_threads, block_size);
unsigned int total_threads = num_blocks * block_size;
assert(total_threads >= num_threads);
//calculate grid size
unsigned int gy = kernelUtilCeilDiv(num_blocks, GRID_SIZE);
unsigned int gx = kernelUtilCeilDiv(num_blocks, gy);
unsigned int total_blocks = gx * gy;
assert(total_blocks >= num_blocks);
//grid
grid->x = gx;
grid->y = gy;
grid->z = 1;
}
//ceil division (rounding up)
unsigned int kernelUtilCeilDiv(unsigned int numerator, unsigned int denominator) {
return (numerator + denominator - 1) / denominator;
}
Код для расчета уникального идентификатора потока и проверки границ (устройства):
//some kernel
__global__ void kernelFoo(unsigned int num_threads, ...) {
//calculate unique id
const unsigned int thread_id = threadIdx.x;
const unsigned int block_id = blockIdx.x + blockIdx.y * gridDim.x;
const unsigned int unique_id = thread_id + block_id * blockDim.x;
//check range
if (unique_id >= num_threads) return;
//do the actual work
...
}
Я не думаю, что нужно много усилий / регистров / строк кода, чтобы проверить правильность.