为什么OpenCL atomic_add浮点实现会产生不确定的结果?

为什么OpenCL atomic_add浮点实现会产生不确定的结果?,opencl,non-deterministic,gpu-atomics,Opencl,Non Deterministic,Gpu Atomics,我需要在OpenCL的多个线程中向相同的全局内存地址添加一个浮点值。对于任何两次模拟运行,结果都不相同,对原子添加函数的调用是此错误的根源。我使用的是Nvidia Titan Xp GPU和驱动程序436.02 由于OpenCL不支持带浮动的原子添加,因此使用原子cmpxchg: void原子添加(易失性全局浮点*addr,常量浮点val){ 联合{ uint u32; 浮动f32; }其次,预期,当前; current.f32=*addr; 做{ next.f32=(预期的.f32=当前的.f

我需要在OpenCL的多个线程中向相同的全局内存地址添加一个
浮点值。对于任何两次模拟运行,结果都不相同,对
原子添加函数的调用是此错误的根源。我使用的是Nvidia Titan Xp GPU和驱动程序436.02

由于OpenCL不支持带浮动的
原子添加
,因此使用
原子cmpxchg

void原子添加(易失性全局浮点*addr,常量浮点val){
联合{
uint u32;
浮动f32;
}其次,预期,当前;
current.f32=*addr;
做{
next.f32=(预期的.f32=当前的.f32)+val;/…*val表示原子的
current.u32=atomic_cmpxchg((易失性全局uint*)addr,expected.u32,next.u32);
}while(当前的.u32!=预期的.u32);
}
但是,此代码确实会产生不确定的结果。每次跑步的结果略有不同,类似于比赛条件出现时的情况

我也试过这个版本

void原子添加(易失性全局浮点*addr,常量浮点val){
私人浮动旧,金额;
做{
old=*地址;
总和=旧的+val;
}而(原子的((易失性全局int*)addr,as_int(old),as_int(sum))!=as_int(old));
}
这也不能正常工作。提供的版本也不起作用


这是怎么回事,怎么解决呢?

由于浮点运算的工作方式,
(a+b)+c
a+(b+c)
不一定产生完全相同的结果。中间结果总是被截断或四舍五入。由于内核的不同工作项不是以确定的顺序运行的,因此您的总和不会是确定的

维基百科中的浮点计算,根据关联性的不同不会产生相同的结果

可能的解决办法:

  • 不要使用浮点累加器
  • 将结果写入数组,并在主机上使用串行求和或在GPU或加速器上使用确定性简化算法在单独的步骤中对数组求和

请注意,OpenCL不强制要求任何特定的舍入行为,因此即使您将累加更改为确定性,算法的其余部分也很可能不会在不同的OpenCL实现中产生一致的结果。如果您在任何情况下都必须为相同的输入获得相同的结果,请不要使用浮点运算,而是使用大小适当的整数。

减少是确定的。你总是把1和1+n/2,2和2+n/2相加。。。1和2。原子添加操作无法使用工作项id值对其所有访问重新排序,并且无法猜测工作项的顺序。