我在OpenCL中实现了一个reduce
内核,以总结大小为input
的{{1}}向量中的所有条目。为了便于测试,我使用N
初始化input
向量。所以结果应该是1.0f
。但事实并非如此!
这是我的N
- 内核:
reduce
以下是OpenCL的设置:
kernel void reduce(global float* input, global float* output, const unsigned int N, local float* cache)
{
const uint local_id = get_local_id(0);
const uint global_id = get_global_id(0);
const uint local_size = get_local_size(0);
cache[local_id] = (global_id < N) ? input[global_id] : 0.0f;
barrier(CLK_LOCAL_MEM_FENCE);
for (unsigned int s = local_size >> 1; s > 0; s >>= 1) {
if (local_id < s) {
cache[local_id] += cache[local_id + s];
}
barrier(CLK_LOCAL_MEM_FENCE);
}
if (local_id == 0) output[local_size] = cache[0];
}
结果取决于工作组大小。我究竟做错了什么?它是内核本身还是OpenCL的设置?
答案 0 :(得分:2)
在将总和写回全局内存时,您应该使用该组的ID。
if (local_id == 0) output[local_size] = cache[0];
该行将重复写入输出[512]。您需要每个工作组写入输出中的专用位置。
kernel void reduce(global float* input, global float* output, const unsigned int N, local float* cache)
{
const uint local_id = get_local_id(0);
const uint global_id = get_global_id(0);
const uint group_id = get_group_id(0);
const uint local_size = get_local_size(0);
cache[local_id] = (global_id < N) ? input[global_id] : 0.0f;
barrier(CLK_LOCAL_MEM_FENCE);
for (unsigned int s = local_size >> 1; s > 0; s >>= 1) {
if (local_id < s) {
cache[local_id] += cache[local_id + s];
}
barrier(CLK_LOCAL_MEM_FENCE);
}
if (local_id == 0) output[group_id] = cache[0];
}
然后,您需要对主机上的输出中的值求和。请注意&#39; b&#39;在主机代码中不需要保存N个元素。每个工作组只使用一个元素。
//replace (globalSize/localSize) with the pre-calculated/known number of work groups
for (i=1; i<(globalSize/localSize); i++) {
b[0] += b[i];
}
现在b [0]是你的总计。
答案 1 :(得分:1)
在简化循环中,你需要这个:
for(unsigned int s = localSize >> 1; s > 0; s >>= 1)
在初始化s时,你的位数比你应该多一点。
在解决之后,让我们看看你的内核在做什么。主机代码使用globalSize为8192和localSize为512执行它,从而生成16个工作组。在内核中,首先对索引2 * global_id处的两个连续内存位置的数据求和。对于id为15的工作组,工作项0,它将位于索引15 * 512 * 2 = 15,360和15,361,它位于输入数组的边界之外。我很惊讶你不会崩溃。同时,这也解释了为什么你有两倍的期望值。
要解决此问题,您可以执行以下操作:
cache[localID] = input[globalID];
或者指定一个全局大小,它是当前大小的一半。