x是全局存储器中的长度为N的数组,其由每个w个线程的k个块的cuda / opencl内核操作(因此k = ceil(N / w))。内核中的每个块都有一个长度为w的本地共享数组xlocal。任务是为每个块加载他们的x块到xlocal。
如果w确切地划分N,那么我们可以这样做:
int lid = threadIdx.x;
int gid = threadIdx.x + (blockIdx.x * blockDim.x);
xlocal[lid] = x[gid];
如果没有,那么我们在最后一个块中有(N%w)冗余线程。我们该如何处理它们?我可以想到以下几个选项:
Malloc x的长度更大。即,分配k * w元素而不是N.这很有用,因为上面的代码才有效。不幸的是,我认为在cuda或opencl中没有realloc等价物。
在加载前进行范围检查。这很好,因为我们不需要乱用x的分配。但仅仅因为边缘条件而将工作添加到大多数线程是很烦人的。
if (gid < N) xlocal[lid] = x[gid];
从x modulo N加载,以便冗余线程环绕:
xlocal[lid] = x[gid%N];
有关解决此问题的其他想法吗?
以下是比较选项(2)rangecheck(蓝色)与选项(3)加载模N(红色)的一些结果。
我们修复了32个线程的块大小并将N从45.6k变为45.6k + 32,分别在最后一个块中给出0到32个冗余线程。该测试运行一个简单的内核,从全局内存中预加载共享数组。左侧(/右侧)的图形为每个线程加载一个(/三个)元素。我用cuda 3.2.16标志-O2编译并在特斯拉M2070卡上运行。
答案 0 :(得分:2)
您可以从主机分配更大的x。然后你应该考虑可能引入的额外复制时间,以及内存空间。这也会使你的代码失去意义和结构。
使用此选项,您正在为每个线程添加计算gid%N的额外工作,这正是您要避免的,再加上来自全局内存的额外副本(可能不会因为复制而受到太大影响)合并,但仍然)。
在我看来,2(或3)是你最好的选择 你只需要为每个线程添加几条指令。考虑到您的代码将保持清晰且不言自明,不用担心这一点。
你应该避免选择1。