OpenCL Atomic是否添加了矢量类型?

时间:2018-08-13 18:02:38

标签: vector opencl atomic

我正在从两个通道更新缓冲区中的单个元素,并且需要一个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。

建议?

2 个答案:

答案 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)确保组中的所有线程都已同步。