OpenCL:多个工作项将结果保存到相同的全局内存地址

时间:2016-04-05 03:58:28

标签: opencl gpu gpgpu pyopencl

我正在尝试进行类似缩小的累积计算,其中根据特定条件需要存储4个不同的值。我的内核接收长数组作为输入,只需要存储4个值,这些值是从输入上的每个数据点获得的“全局和”。例如,我需要存储满足特定条件的所有数据值的总和,以及满足所述条件的数据点的数量。下面的内核使它更清晰:

__kernel void photometry(__global float* stamp, 
                         __constant float* dark,
                         __global float* output)
{
int x = get_global_id(0);
int s = n * n;

if(x < s){
    float2 curr_px = (float2)((x / n), (x % n));
    float2 center = (float2)(centerX, centerY);
    int dist = (int)fast_distance(center, curr_px);
    if(dist < aperture){
        output[0] += stamp[x]-dark[x];
        output[1]++;
    }else if (dist > sky_inner && dist < sky_outer){
        output[2] += stamp[x]-dark[x];
        output[3]++;
    }
 }
}

内核中未声明的所有值以前都是由宏定义的。 s是输入数组stampdark的长度,n x n矩阵被展平为1D。

我得到了结果,但它们与我的CPU版本不同。我当然想知道:这是我正在做的事情的正确方法吗?我可以确定每个像素数据只被添加一次吗?我想不出任何其他方法来保存累积结果值。

2 个答案:

答案 0 :(得分:3)

在您的情况下需要进行原子操作,否则数据争用将导致结果无法预测。

问题在于:

output[0] += stamp[x]-dark[x];
output[1]++;

您可以想象同一波中的线程可能仍然遵循相同的步骤,因此,对于同一波内的线程可能没问题。因为它们使用全局加载指令(广播)读取相同的输出[0]值。然后,当他们完成计算并尝试将数据存储到相同的存储器地址(输出[0])时,写入操作将被序列化。到目前为止,您仍然可以获得正确的结果(对于同一波内的工作项)。

但是,由于您的程序很可能会启动多个wave(在大多数应用程序中,情况就是这样)。不同的波可以以未知的顺序执行;然后,当他们访问相同的内存地址时,行为变得更加复杂。例如,wave0和wave1可以在任何其他计算发生之前在开始时访问输出[0],这意味着它们从输出[0]获取相同的值;然后他们开始计算。经过计算,它们将累积结果保存到输出[0];显然,其中一个波浪的结果将被另一个波浪覆盖,好像只有后来写入记忆的人才会被执行。想象一下,在实际应用程序中你有更多的波,所以有一个错误的结果并不奇怪。

答案 1 :(得分:0)

您可以同时在 O(log2(n))中执行此操作。一个概念:

您有16(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16)个输入,并且您想要这些输入的总和同时进行。

您可以同时求和1、2、3、4、5、6、7、8、9、10、11、12、13、14、15、16

然后您同时对4、2、4、6、8、10、12、14、16进行求和

然后总是同时8、4、10、16

最后是16中的8

在我们的案例中, O(log2(n))中的所有操作均以 4个段落为例。