我正在从两个通道更新缓冲区中的单个元素,并且需要一个float4类型的原子。 (更具体地说,我启动的线程是缓冲区元素的两倍,并且每对连续的线程更新同一元素。)
例如(此伪代码无用,但希望能说明我的问题):
int idx = get_global_id(0);
int mapIdx = floor (idx / 2.0);
float4 toAdd;
// ...
if (idx % 2)
{
toAdd = (float4)(0,1,0,1);
}
else
{
toAdd = float3(1,0,1,0);
}
// avoid race condition here?
// I'd like to atomic_add(map[mapIdx],toAdd);
map[mapIdx] += toAdd;
在此示例中,map[0]
应该增加(1,1,1,1)
。 (0,1,0,1)
来自线程0,(1,0,1,0)
来自线程1。
建议?我在CL文档中没有找到对矢量原子的任何引用。我想我可以分别对每个矢量分量进行此操作:
atomic_add(map[mapIdx].x, toAdd.x);
atomic_add(map[mapIdx].y, toAdd.y);
atomic_add(map[mapIdx].z, toAdd.z);
atomic_add(map[mapIdx].w, toAdd.w);
...但是那感觉真是个坏主意。 (并且由于没有float原子,因此需要cmpxchg hack。
建议?
答案 0 :(得分:1)
或者,您可以尝试使用本地内存,例如:
__local float4 local_map[LOCAL_SIZE/2];
if(idx < LOCAL_SIZE/2) // More optimal would be to use work items together than every second (idx%2) as they work together in a warp/wavefront anyway, otherwise that may affect the performance
local_map[mapIdx] = toAdd;
barrier(CLK_LOCAL_MEM_FENCE);
if(idx >= LOCAL_SIZE/2)
local_map[mapIdx - LOCAL_SIZE/2] += toAdd;
barrier(CLK_LOCAL_MEM_FENCE);
哪种更快(原子或本地内存)还是可能(本地内存的大小可能太大)取决于实际内核,因此您需要进行基准测试并选择正确的解决方案。
更新:
从评论中回答您的问题-稍后写回全局缓冲区可以:
if(idx < LOCAL_SIZE/2)
map[mapIdx] = local_map[mapIdx];
或者您可以尝试不引入本地缓冲区而直接写入全局缓冲区:
if(idx < LOCAL_SIZE/2)
map[mapIdx] = toAdd;
barrier(CLK_GLOBAL_MEM_FENCE); // <- notice that now we use barrier related to global memory
if(idx >= LOCAL_SIZE/2)
map[mapIdx - LOCAL_SIZE/2] += toAdd;
barrier(CLK_GLOBAL_MEM_FENCE);
除此之外,我现在可以看到索引问题。要使用我的答案中的代码,先前的代码应如下所示:
if(idx < LOCAL_SIZE/2)
{
toAdd = (float4)(0,1,0,1);
}
else
{
toAdd = (float4)(1,0,1,0);
}
尽管如果您需要使用id%2
,那么所有代码都必须遵循此规则,否则您将必须执行一些索引运算,以便将值放入map
中的正确位置。
答案 1 :(得分:1)
如果我正确理解问题,我将接下来做。 通过使数组具有偏移量来摆脱ifs
float4[2] = {(1,0,1,0), (0,1,0,1)}
并使用idx%2作为偏移量
将map
移动到本地内存中,并使用mem_fence(CLK_LOCAL_MEM_FENCE)
确保组中的所有线程都已同步。