在 GCC 5.3 中,以下代码使用 -O3 -fma
float mul_add(float a, float b, float c) {
return a*b + c;
}
生成以下程序集
vfmadd132ss %xmm1, %xmm2, %xmm0
ret
I noticed GCC doing this with -O3
already in GCC 4.8 .
带有 -O3 -mfma
的 Clang 3.7 产生
vmulss %xmm1, %xmm0, %xmm0
vaddss %xmm2, %xmm0, %xmm0
retq
但是使用 -Ofast -mfma
的 Clang 3.7 生成的代码与使用 -O3 fast
的 GCC 生成的代码相同。
我很惊讶 GCC 使用 -O3
因为来自 this answer它说
The compiler is not allowed to fuse a separated add and multiply unless you allow for a relaxed floating-point model.
This is because an FMA has only one rounding, while an ADD + MUL has two. So the compiler will violate strict IEEE floating-point behaviour by fusing.
但是,来自 this link它说
Regardless of the value of FLT_EVAL_METHOD, any floating-point expression may be contracted, that is, calculated as if all intermediate results have infinite range and precision.
所以现在我很困惑和担心。
- GCC 将 FMA 与
-O3
结合使用是否合理? - 融合是否违反严格的 IEEE 浮点行为?
- 如果融合确实违反了 IEEE 浮点行为并且因为 GCC returns
__STDC_IEC_559__
这不是自相矛盾吗?
自 FMA can be emulated in software FMA似乎应该有两个编译器开关:一个告诉编译器在计算中使用FMA,一个告诉编译器硬件有FMA。
显然这可以通过选项 -ffp-contract
来控制。对于 GCC,默认值为 -ffp-contract=fast
而对于 Clang,则不是。 -ffp-contract=on
和 -ffp-contract=off
等其他选项不会产生 FMA 指令。
例如,带有 -O3 -mfma -ffp-contract=fast
的 Clang 3.7 生成 vfmadd132ss
。
我检查了一些 #pragma STDC FP_CONTRACT
设置为 ON
和 OFF
与 -ffp-contract
的排列设置为 on
、off
和 fast
。在所有情况下,我还使用了 -O3 -mfma
。
有了 GCC,答案就很简单了。 #pragma STDC FP_CONTRACT
ON 或 OFF 没有区别。只有 -ffp-contract
很重要。
GCC 它使用 fma
和
-ffp-contract=fast
(默认)。
对于 Clang,它使用 fma
- 使用
-ffp-contract=fast
。 - 使用
-ffp-contract=on
(默认)和#pragma STDC FP_CONTRACT ON
(默认为OFF
)。
换句话说,对于 Clang,您可以使用 #pragma STDC FP_CONTRACT ON
获得 fma
(因为 -ffp-contract=on
是默认设置) 或使用 -ffp-contract=fast
。 -ffast-math
(因此 -Ofast
)设置 -ffp-contract=fast
。
我研究了 MSVC 和 ICC。
对于 MSVC,它使用带 /O2/arch:AVX2/fp:fast
的 fma 指令。对于 MSVC,/fp:precise
是默认值。
对于 ICC,它使用带 -O3 -march=core-avx2
的 fma(实际上 -O1
就足够了)。这是因为 ICC 默认使用 -fp-model fast
。但是 ICC 即使使用 -fp-model precise
也使用 fma。要使用 ICC 禁用 fma,请使用 -fp-model strict
或 -no-fma
。
因此默认情况下 GCC 和 ICC 在启用 fma 时使用 fma(GCC/Clang 使用 -mfma
或 ICC 使用 -march=core-avx2
)但 Clang 和MSVC 没有。
最佳答案
它不违反 IEEE-754,因为 IEEE-754 在这一点上遵从语言:
A language standard should also define, and require implementations to provide, attributes that allow and disallow value-changing optimizations, separately or collectively, for a block. These optimizations might include, but are not limited to:
...
― Synthesis of a fusedMultiplyAdd operation from a multiplication and an addition.
在标准 C 中,STDC FP_CONTRACT
pragma 提供了控制这种值更改优化的方法。因此,GCC 被许可在默认情况下执行融合,只要它允许您通过设置 STDC FP_CONTRACT OFF
来禁用优化。不支持意味着不遵守 C 标准。
关于c - 融合乘加和默认舍入模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34436233/