1

我需要float在 OpenCL 的多个线程中添加一个相同的全局内存地址。对于任何两次模拟运行,结果永远不会相同,对atomic_add_f函数的调用是此错误的根源。我正在使用带有驱动程序 436.02 的 Nvidia Titan Xp GPU。

由于 OpenCL 不支持atomic_addwith float,因此可以使用以下方法atomic_cmpxchg

void atomic_add_f(volatile global float* addr, const float val) {
    union {
        uint  u32;
        float f32;
    } next, expected, current;
    current.f32 = *addr;
    do {
        next.f32 = (expected.f32=current.f32)+val; // ...*val for atomic_mul_f()
        current.u32 = atomic_cmpxchg((volatile global uint*)addr, expected.u32, next.u32);
    } while(current.u32!=expected.u32);
}

但是,此代码确实会产生不确定的结果。每次运行的结果略有不同,类似于出现竞态条件的情况。

我也试过这个版本

void atomic_add_f(volatile global float* addr, const float val) {
    private float old, sum;
    do {
        old = *addr;
        sum = old+val;
    } while(atomic_cmpxchg((volatile global int*)addr, as_int(old), as_int(sum))!=as_int(old));
}

这也不能正常工作。这里提供的版本也不起作用。

这怎么可能以及如何解决?

4

1 回答 1

2

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

Wikipedia提供了一些浮点计算示例,这些示例不会根据关联性产生相同的结果。

可能的解决方案:

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

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

于 2019-08-30T20:26:52.310 回答