提问人:Marty 提问时间:9/24/2020 最后编辑:Marty 更新时间:9/27/2020 访问量:2107
x86_64 和 ARMv8.2-A 之间的浮点计算结果不同
Differing Floating Point Calculation Results between x86_64 and ARMv8.2-A
问:
我在 aarch64 和 x86_64 中编译了相同的 Fortran 库和代码。它是一个跨 n 维数组/矩阵运行算法的模型。ARM CPU 是 Amazon Graviton2。AWS中的AMD和Intel选项在编译代码并运行x86_64时会产生相同的结果。
我正在使用带有以下标志的 gcc / g++ / gfortran / mpich(所有版本 8.3.0,来自 debian buster 的主要存储库)
-O2 -ftree-vectorize -funroll-loops -w -ffree-form -ffree-line-length-none -fconvert=big-endian -frecord-marker=4
这一切都编译和运行良好,但是,我注意到在模型的输出中,结果略有不同。这似乎是精度或舍入的问题,因为输出之间的大多数值都是相同的。但是,在整个输出中(看似)存在随机值,其中看起来像是为一个 arch 编译的代码向下舍入或截断,而另一个 arch 向上舍入。
输出存储为 NetCDF(使用 NetCDF-Fortran 版本 4.5.3),文件的 md5sum 在 x86_64 CPU 上是相同的,但在 aarch64 上有所不同。
关于为什么会发生这种情况的任何想法?或者我可以在编译过程中使用任何标志来确保我在各个架构中获得相同的结果?
我现在看到的值的精度为小数点后 5 位,即 123.12345
这是输出中的一个片段,您可以看到大多数值是相同的,但有些值似乎舍入不同(我用 ** 标记了不同的值):diff
657c657
< 18.83633, 18.83212, 18.82778, **18.82337**, 18.81886, 18.81425, 18.80956,
---
> 18.83633, 18.83212, 18.82778, **18.82336**, 18.81886, 18.81425, 18.80956,
1151c1151
< 17.35448, 17.37331, 17.39206, 17.41071, 17.42931, **17.4478**, 17.46622,
---
> 17.35448, 17.37331, 17.39206, 17.41071, 17.42931, **17.44779**, 17.46622,
1711c1711
< 19.77562, 19.77532, 19.77493, 19.77445, 19.77386, 19.77319, **19.77241**,
---
> 19.77562, 19.77532, 19.77493, 19.77445, 19.77386, 19.77319, **19.77242**,
2130c2130
< 20.06532, 20.06839, **20.07135**, 20.07423, 20.07702, 20.0797, 20.0823,
---
> 20.06532, 20.06839, **20.07136**, 20.07423, 20.07702, 20.0797, 20.0823,
2140c2140
< 20.04788, 20.04424, 20.04047, **20.03661**, 20.03268, 20.02863, 20.02448,
---
> 20.04788, 20.04424, 20.04047, **20.03662**, 20.03268, 20.02863, 20.02448,
2600c2600
< 11.54104, 11.57732, 11.61352, 11.6497, 11.68579, **11.72186**, 11.75784,
---
> 11.54104, 11.57732, 11.61352, 11.6497, 11.68579, **11.72185**, 11.75784,
答:
如果代码仅使用 +、-、* 和 sqrt 等基本算术运算,并且编译器处于IEEE754一致性模式,则无论使用何种 CPU,输出都应位相同。 此IEEE754一致性模式通常是默认设置。
否则,该问题可能是由编译器或 CPU 错误引起的。
选项,例如将编译器置于非 IEEE 754 一致性模式。
它使用数学等价规则来优化代码,这些代码不一定在数值上等价(例如,等等)。
如果是这种情况,并且编译器对 ARM 代码的优化方式与 x86_64 不同,这可能是一种解释。-ffast-math
((a*a)*a)*a -> (a*a)*(a*a)
Also if the code uses functions such as , , and such, the output will only be bit-identical if the exact same run-time library is used. This is because these functions are not correctly rounded and results typically have a tiny error (which may amplify in the calculation and show up in the way you observe it).
It also might be the case that for x86_64 special CPU instructions for these functions are used and for ARM a software implementation or vice versa. Note that even if these functions are implemented on the CPU/FPU they are also not correctly rounded and very likely different algorithms are used.sin
cos
exp
atan2
TL/DR: check the compiler flags for or try adding at the end of the options.-ffast-math
-fno-fast-math
EDIT: As @Rob mentioned in the comment another flag that could be added . In gcc it is by default 'fast' (independent on ) which may generate the FMA instruction even when not explicitly requested. This also breaks 754 conformance.-ffp-contract=off
-ffast-math
评论