【发布时间】:2016-08-21 02:57:42
【问题描述】:
我正在尝试实现一个 OpenCL 版本来减少浮点数组。
为了实现它,我拿了以下在网上找到的sn-p代码:
__kernel void sumGPU ( __global const double *input,
__global double *partialSums,
__local double *localSums)
{
uint local_id = get_local_id(0);
uint group_size = get_local_size(0);
// Copy from global memory to local memory
localSums[local_id] = input[get_global_id(0)];
// Loop for computing localSums
for (uint stride = group_size/2; stride>0; stride /=2)
{
// Waiting for each 2x2 addition into given workgroup
barrier(CLK_LOCAL_MEM_FENCE);
// Divide WorkGroup into 2 parts and add elements 2 by 2
// between local_id and local_id + stride
if (local_id < stride)
localSums[local_id] += localSums[local_id + stride];
}
// Write result into partialSums[nWorkGroups]
if (local_id == 0)
partialSums[get_group_id(0)] = localSums[0];
}
此内核代码运行良好,但我想通过添加每个工作组的所有部分和来计算最终总和。
目前,我通过一个简单的循环和迭代nWorkGroups 完成 CPU 的最终总和这一步。
我还看到了另一种具有原子函数的解决方案,但它似乎是为 int 实现的,而不是为浮点数实现的。我认为只有 CUDA 提供了 float 的原子函数。
我还看到我可以使用另一个内核代码来执行 sum 的此操作,但我想避免使用此解决方案以保持简单可读的源代码。也许我不能没有这个解决方案......
我必须告诉你,我在 Radeon HD 7970 Tahiti 3GB 上使用 OpenCL 1.2(由 clinfo 返回)(我认为我的卡不支持 OpenCL 2.0)。
更一般地说,我想获得有关使用我的显卡模型和 OpenCL 1.2 执行最后最终求和的最简单方法的建议。
【问题讨论】:
-
通常不需要在GPU中做最后一步。例如,如果您有 8M=2^23 个元素,并且每个工作组是 128=2^7。如果你运行你的内核两次,你最终会得到 512 个值,数量非常少。再一次迭代,结果只有 4 个值。 CPU可以轻松处理它。最好的方法是,将 GPU 用于好的、小尺寸和线性和不太适合的东西,所以最好用 CPU 来做(即使在 512 的情况下也可能)。
-
@DarkZeros 谢谢你的评论,问题是我想把这个减少到一个主循环中,在每次迭代中,我必须计算总和减少。为此,我需要在每次迭代时交换输入和输出缓冲区(将参数交换到 setKernelArg):这就是为什么我不想将 CPU 用于部分和的最终总和,因为我认为这种方法会破坏执行管道,即GPU处理的好处。
-
您必须记住,GPU 没有无限量的工作项。很有可能,您可以并行处理数千个工作项。因此,对于 1M 元素,减少需要在 GPU 内部进行几次“迭代”。用不同的参数重新启动另一个迭代应该很便宜,而且开销最小。只要您不执行 clFinish() 来阻止管道。您可以阅读第 3 节,两级还原:developer.amd.com/resources/documentation-articles/…