我目前正致力于在NVIDIA Tesla C1060(驱动程序版本195.17)上起诉OpenCL的项目。但是我得到了一些我无法解释的奇怪行为。这是令我困惑的代码(为了清晰度和测试目的而减少):
kernel void TestKernel(global const int* groupOffsets, global float* result,
local int* tmpData, const int itemcount)
{
unsigned int groupid = get_group_id(0);
unsigned int globalsize = get_global_size(0);
unsigned int groupcount = get_num_groups(0);
for(unsigned int id = get_global_id(0); id < itemcount; id += globalsize, groupid += groupcount)
{
barrier(CLK_LOCAL_MEM_FENCE);
if(get_local_id(0) == 0)
tmpData[0] = groupOffsets[groupid];
barrier(CLK_LOCAL_MEM_FENCE);
int offset = tmpData[0];
result[id] = (float) offset;
}
}
此代码应将每个工作组的偏移量加载到本地内存中,然后将其读回并将其写入相应的outputvector条目。对于大多数工作项,这是有效的,但对于每个工作组,具有本地ID 1到31的工作项读取的值不正确。 我的输出向量(对于workgroupsize = 128)如下:
index 0: 0
index 1- 31: 470400
index 32-127: 0
index 128: 640
index 129-159: 471040
index 160-255: 640
index 256: 1280
index 257-287: 471680
index 288-511: 1280
...
我预期的输出是
index 0-127: 0
index 128-255: 640
index 256-511: 1280
...
奇怪的是:问题只发生在我使用少于itemcount工作项时(因此当globalsize&gt; = itemcount时它会按预期工作,这意味着每个工作项只处理一个条目)。所以我猜它与循环有关。 有谁知道我做错了什么以及如何解决它?
更新: 我发现如果我改变
它似乎有效if(get_local_id(0) == 0)
tmpData[0] = groupOffsets[groupid];
到
if(get_local_id(0) < 32)
tmpData[0] = groupOffsets[groupid];
这让我更加惊讶,所以虽然它可以解决这个问题,但我觉得用这种方式修复它感觉很舒服(因为它可能会破坏其他时间)。 此外,我宁愿避免在运行Geforce 8xxx类硬件时失去性能,因为额外的(据我所知,硬件没有合并)内存访问。 所以问题仍然存在。
答案 0 :(得分:0)
首先,重要的是,您需要注意itemcount
是本地工作大小的倍数,以避免在执行障碍时出现分歧。
在处理器上执行内核的工作组中的所有工作项必须先执行此功能,然后才允许继续执行超出障碍的执行。执行内核的工作组中的所有工作项都必须遇到此函数。
您可以按如下方式实现:
unsigned int itemcountrounded = get_local_size(0) * ((itemcount + get_local_size(0) - 1) / get_local_size(0));
for(unsigned int id = get_global_id(0); id < itemcountrounded; id += globalsize, groupid += groupcount)
{
// ...
if (id < itemcount)
result[id] = (float) offset;
}
你说代码是为了简单而减少了,如果你运行你发布的内容会发生什么?只是想知道你是否也需要把障碍放在全球记忆上。