CUDA - 没有块,只是未定义尺寸的线程

时间:2011-03-29 21:45:29

标签: c++ matrix cuda

我有一些未知大小的矩阵,从两个方向的10-20.000不等。

我设计了一个带有(x; y)块和(x; y)线程的CUDA内核。

由于矩阵的宽度/高度不是我的维度的倍数,因此让事情起作用是一件非常痛苦的事情,并且代码变得越来越复杂以获得合并内存读取。

除此之外,内核的大小越来越大,使用越来越多的寄存器来检查是否正确......所以我认为这不是我应该采用的方式。

我的问题是:如果我完全消除块并只创建x; y线程的网格怎么办? SM单元是否会在没有很多块的情况下出现问题?

我可以消除块并使用大量线程还是必须进行块细分?

2 个答案:

答案 0 :(得分:4)

你实际上不能只创建一个“线程网格”,因为你必须将线程组织成块,每个块最多可以有512个线程。但是,您可以通过每个块使用1个线程来有效地执行此操作,这将导致1 x 1块的X x Y网格。但是,由于以下几个因素,这将导致非常糟糕的性能:

  1. 根据CUDA编程指南,SM可以随时处理最多8个块。这将限制您每个SM 8个线程,这甚至不足以填充单个warp。如果你有48个CUDA核心,你将只能在任何给定的时间处理384个线程。

  2. 如果SM上只有8个线程可用,则会有太少的warp来隐藏内存延迟。 GPU将花费大部分时间等待内存访问完成,而不是进行任何计算。

  3. 您将无法合并内存读取和写入,从而导致内存带宽使用不足。

  4. 您实际上无法利用共享内存,因为这是块中线程之间的共享资源。

  5. 虽然必须确保块中线程的正确性是令人讨厌的,但是您的性能将远远超过您的“线程网格”理念。

答案 1 :(得分:2)

这是我用来将需要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;
}

用于计算唯一线程ID和检查边界(设备)的代码:

//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
    ...
}

我不认为这需要很多努力/注册/代码行来检查是否正确。