对于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。叮当声3.7与
-O3 -mfma
产生vmulss %xmm1, %xmm0, %xmm0
vaddss %xmm2, %xmm0, %xmm0
retq
但是带有
-Ofast -mfma
的clang 3.7生成的代码与带有-O3 fast
的gcc相同。我很惊讶gcc使用
-O3
是因为它说编译器不允许合并单独的加法和乘法运算,除非允许使用宽松的浮点模型。
这是因为fma只有一个舍入,而add+mul有两个舍入。因此编译器将通过融合违反严格的ieee浮点行为。
然而,从this answer开始
不管flt_eval_方法的值是多少,任何浮点表达式都可能被压缩,也就是说,所有的中间结果都具有无限的范围和精度。
所以现在我感到困惑和担心。
GCC是否有理由在使用FMA时使用
-O3
?熔合是否违反了严格的ieee浮点行为?
如果熔合确实违反了ieee浮点运算,那么既然this link这不是一个矛盾吗?
因为fmaGCC returns
__STDC_IEC_559__
所以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
打开或关闭没有区别。只有-ffp-contract
才重要。它使用的gcc
fma
与-ffp-contract=fast
(默认)。它使用的是
fma
使用
-ffp-contract=fast
。使用
-ffp-contract=on
(默认)和#pragma STDC FP_CONTRACT ON
(默认为OFF
)。换句话说,使用clang可以使用
fma
或#pragma STDC FP_CONTRACT ON
获得-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,它使用fma和
-O3 -march=core-avx2
(实际上-O1
就足够了)。这是因为默认情况下,icc使用-fp-model fast
。但icc使用fma,即使使用-fp-model precise
。要使用icc禁用fma,请使用-fp-model strict
或-no-fma
。因此,默认情况下,gcc和icc在启用fma时使用fma(对于gcc/clang使用
-mfma
或对于icc使用-march=core-avx2
),但clang和msvc不使用fma。 最佳答案
这并不违反ieee-754,因为ieee-754在这一点上遵从于语言:
语言标准还应定义并要求实现提供允许或禁止对块单独或集体更改值的优化的属性。这些优化可能包括但不限于:
…
―由乘法和加法合成fusedmultiplyadd运算。
在标准c中,STDC FP_CONTRACT
杂注提供了控制此值更改优化的方法。因此,gcc在默认情况下被授权执行融合,只要它允许您通过设置STDC FP_CONTRACT OFF
禁用优化。不支持这意味着不遵守c标准。
关于c - 融合乘法加法和默认舍入模式,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/34436233/