我正在尝试进行类似缩小的累积计算,其中根据特定条件需要存储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
是输入数组stamp
和dark
的长度,n
x n
矩阵被展平为1D。
我得到了结果,但它们与我的CPU版本不同。我当然想知道:这是我正在做的事情的正确方法吗?我可以确定每个像素数据只被添加一次吗?我想不出任何其他方法来保存累积结果值。
答案 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个段落为例。