如何计算块编号

时间:2012-05-02 11:30:38

标签: c cuda nvidia

我正在编写CUDA代码而我正在使用GForce 9500 GT显卡。

我正在尝试处理20000000个整数元素的数组,而我使用的线程数是256

warp大小为32.计算能力为1.1

这是硬件http://www.geforce.com/hardware/desktop-gpus/geforce-9500-gt/specifications

现在,块数= 20000000/256 = 78125?

这个声音不正确。如何计算块编号? 任何帮助将不胜感激。

我的CUDA内核功能如下。这个想法是每个块将计算其总和,然后通过添加每个块的总和来计算最终总和。

__global__ static void calculateSum(int * num, int * result, int DATA_SIZE)
{
    extern __shared__ int shared[];
    const int tid = threadIdx.x;
    const int bid = blockIdx.x;

    shared[tid] = 0;
    for (int i = bid * THREAD_NUM + tid; i < DATA_SIZE; i += BLOCK_NUM * THREAD_NUM) {
        shared[tid] += num[i];
    }

    __syncthreads();
    int offset = THREAD_NUM / 2;
    while (offset > 0) {
        if (tid < offset) {
            shared[tid] += shared[tid + offset];
        }
        offset >>= 1;
        __syncthreads();
    }

    if (tid == 0) {
        result[bid] = shared[0];

    }
}

我将此功能称为

calculateSum <<<BLOCK_NUM, THREAD_NUM, THREAD_NUM * sizeof(int)>>> (gpuarray, result, size);

THREAD_NUM = 256 和gpu数组大小为20000000.

这里我只使用块号为16但不确定它是否正确? 如何确保实现最大并行度?

这是我的CUDA占用计算器的输出。它说当块号为8时我将占用100%。这意味着当块号= 8且线程号= 256时,我将获得最大效率。这是对的吗?

CUDA Occupancy calculation 谢谢

4 个答案:

答案 0 :(得分:2)

如果每个thred进程一个元素,并且每个块有256个线程,则应运行20000000个线程,从而产生78125个块。这是完全有效的数字。

然而,有一点问题。我手边没有CC1.1设备,但在CC1.3:

Maximum sizes of each dimension of a grid:     65535 x 65535 x 1

所以你应该为不同的数据部分运行内核几次,或者制作2D网格,只需将线程的2D地址转换为数组元素的1D地址。

答案 1 :(得分:2)

您发布的内核代码可以处理任何输入数据大小,与您选择启动的块数无关。选择应该只是降低性能。

根据经验,对于这种内核,您需要在单个多处理器上并发运行的块数乘以卡上多处理器的数量。第一个数字可以使用CUDA工具包中附带的CUDA占用电子表格获得,但每个多处理器的上限为8个块,而第二个数字为您拥有的设备的4个。这意味着实现最大可能并行性所需的块数不会超过32个,但要回答的确需要访问我目前没有的编译器。

您还可以使用基准测试来实验确定最佳块数,使用4,8,12,16,20,24,28或32个块中的一个(4的倍数,因为这是卡上多处理器的数量) )。

答案 2 :(得分:1)

在您的情况下,线程总数(20000000)按每个块的线程数(256)均匀划分,因此您可以使用该数字(78125)。如果数字不均匀分配,则常规的整数除法会将其向下舍入,最终会得到比您需要的更少的线程。因此,在这种情况下,您需要使用如下函数对除法结果进行舍入:

int DivUp(int a, int b) {
  return ((a % b) != 0) ? (a / b + 1) : (a / b);
}

由于此函数可能会为您提供比元素更多的线程,因此您还需要在内核中添加一个测试以中止最后几个线程的计算:

int i(blockIdx.x * blockDim.x + threadIdx.x);
if (i >= n_items) {
  return;
}

然而,还有一个障碍。您的硬件在网格中的每个维度中限制为最多65535个块,并且限制为二维(x和y)。因此,如果在使用DivUp()之后,最终得到的计数高于此值,则有两种选择。您可以将工作负载分开并多次运行内核,也可以使用两个维度。

要使用两个维度,您可以选择两个数字,每个数字都低于硬件限制,并且在乘以时,将成为您需要的实际块数。然后,在内核顶部添加代码,将两个维度(x和y)合并为一个索引。

答案 3 :(得分:1)

您只在内核中使用网格的x-Dimension。因此,使用cc 1.1限制为65535个块。

20000000/256 = 78125是正确的!

所以你肯定需要超过1个区块。

内核:

//get unique block index
const unsigned int blockId = blockIdx.x //1D
    + blockIdx.y * gridDim.x //2D

//terminate unnecessary blocks
if(blockId >= 78124)
    return;

//... rest of kernel

最简单的方法是在内核中使用两个y块并检查块id。

dim3 gridDim = dim3(65535, 2); 

这会使超过52945个块无用,我不知道什么是开销,但填充第一个x然后y和z维度可以创建很多未使用的块,特别是如果达到z维度!

(Nvidia应该提供一个实用程序函数,以获得内核中唯一块使用的最佳网格用法,就像这里的情况一样)

对于这个简单的例子,如何使用x和y以及计算根。

grid(280, 280) = 78400 blocks //only 275 blocks overhead, less is not possible

这是计算能力3.0的一大优势。每个块上的32位范围使生活变得更加容易。 为什么它被限制在65535我从来不理解。

但我仍然倾向于向下兼容。

我也会测试@talonmies变种。