融合乘法加法和默认舍入模式

时间:2015-12-23 12:57:41

标签: c gcc clang ieee-754 fma

使用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

Clang 3.7与-O3 -mfma生成

vmulss  %xmm1, %xmm0, %xmm0
vaddss  %xmm2, %xmm0, %xmm0
retq

但是使用-Ofast -mfma的Clang 3.7与-O3 fast生成的代码与GCC相同。

我很惊讶GCC与-O3合作,因为它来自this answer

  

除非您允许使用宽松的浮点模型,否则不允许编译器融合分离的加法和乘法。

     

这是因为FMA只有一个舍入,而ADD + MUL有两个舍入。因此编译器会通过融合违反严格的IEEE浮点行为。

然而,从this link开始说

  

无论FLT_EVAL_METHOD的值如何,任何浮点表达式都可以收缩,也就是说,计算好像所有中间结果都具有无限范围和精度。

所以现在我感到困惑和担忧。

  1. GCC是否有理由将FMA与-O3
  2. 一起使用
  3. 融合是否违反了严格的IEEE浮点行为?
  4. 如果融合确实违反了IEEE浮点beahviour,并且GCC returns __STDC_IEC_559__这不是一个矛盾吗?
  5. 由于FMA can be emulated in software似乎应该有两个用于FMA的编译器开关:一个用于告诉编译器在计算中使用FMA,另一个用于告诉编译器硬件具有FMA。

    显然可以使用选项-ffp-contract来控制。使用GCC时,默认值为-ffp-contract=fast,而Clang则默认为-ffp-contract=on。其他选项(例如-ffp-contract=off-O3 -mfma -ffp-contract=fast)不会生成FMA指令。

    例如,包含vfmadd132ss的Clang 3.7会产生#pragma STDC FP_CONTRACT

    我检查了ON设置为OFF-ffp-contract的一些排列,on设置为offfast和{{1} }}。在所有情况下,我也使用了-O3 -mfma

    GCC答案很简单。 #pragma STDC FP_CONTRACT ON或OFF没有区别。只有-ffp-contract很重要。

    GCC使用fma

    1. -ffp-contract=fast(默认)。
    2. 使用Clang,它使用fma

      1. -ffp-contract=fast
      2. -ffp-contract=on(默认)和#pragma STDC FP_CONTRACT ON(默认为OFF)。
      3. 换句话说,对于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,它使用-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不使用。

2 个答案:

答案 0 :(得分:5)

它不违反IEEE-754,因为IEEE-754在这一点上遵循语言:

  

语言标准还应定义并要求实现提供允许和禁止对块进行单独或共同的值更改优化的属性。这些优化可能包括但不限于:

     

...

     

- 通过乘法和加法合成fusedMultiplyAdd运算。

在标准C中,STDC FP_CONTRACT编译指示提供了控制此值更改优化的方法。因此,GCC默认授权执行融合,只要它允许您通过设置STDC FP_CONTRACT OFF来禁用优化。不支持这意味着不遵守C标准。

答案 1 :(得分:4)

当你引用允许融合乘法加法时,你忽略了重要条件"除非pragma FP_CONTRACT关闭"。这是C中的一个新功能(我认为在C99中引入)并且由PowerPC绝对必要,其中所有从一开始就融合了乘法 - 实际上,x * y相当于fma( x,y,0)和x + y等于fma(1.0,x,y)。

FP_CONTRACT控制融合乘法/加法,而不是FLT_EVAL_METHOD。虽然如果FLT_EVAL_METHOD允许更高的精度,那么签约总是合法的;只是假装操作是以非常高的精度执行然后四舍五入。

如果您不想要速度,那么fma功能非常有用,但精度却很高。它将缓慢但正确地计算合同结果,即使它在硬件中不可用。如果它在硬件中可用,则应该内联。