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

9 浏览
0 Comments

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

使用GCC 5.3编译以下代码时,使用-O3 -fma选项:\n

float mul_add(float a, float b, float c) {
  return a*b + c;
}

\n产生以下汇编代码:\n

vfmadd132ss     %xmm1, %xmm2, %xmm0
ret

\n我注意到GCC在GCC 4.8中已经使用-O3进行了此操作。\n使用-O3 -mfma选项的Clang 3.7会生成以下代码:\n

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

\n但是使用-Ofast -mfma选项的Clang 3.7会生成与GCC的-O3 fast相同的代码。\n我对GCC在使用-O3时的行为感到惊讶,因为根据这个答案,它说:\n

\n除非允许使用松散的浮点模型,否则编译器不允许融合分开的加法和乘法。这是因为FMA只有一个舍入,而ADD + MUL有两个。因此,编译器将通过融合违反严格的IEEE浮点行为。\n

\n然而,根据这个链接,它说:\n

\n不管FLT_EVAL_METHOD的值如何,任何浮点表达式都可以被合并,即被计算为如果所有中间结果具有无限范围和精度。\n

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

    \n

  1. GCC在使用-O3时是否是合理的?
  2. \n

  3. 融合是否违反了严格的IEEE浮点行为?
  4. \n

  5. 如果融合确实违反了IEEE浮点行为,并且由于GCC返回__STDC_IEC_559__这不是矛盾吗?
  6. \n

\n由于FMA可以在软件中模拟,似乎应该有两个编译器开关用于FMA:一个用于告诉编译器在计算中使用FMA,一个用于告诉编译器硬件支持FMA。\n


\n显然,可以通过选项-ffp-contract来控制此行为。对于GCC,默认值为-ffp-contract=fast,而对于Clang,则不是。其他选项如-ffp-contract=on-ffp-contract=off不会产生FMA指令。\n例如,使用-O3 -mfma -ffp-contract=fast选项的Clang 3.7会生成vfmadd132ss。\n


\n我检查了一些#pragma STDC FP_CONTRACT设置为ONOFF,并使用-ffp-contract设置为onofffast的排列组合。在所有情况下,我也使用了-O3 -mfma。\n对于GCC来说,答案很简单。无论#pragma STDC FP_CONTRACT是ON还是OFF都没有影响。只有-ffp-contract起作用。\nGCC使用fma的情况有:\n

    \n

  1. -ffp-contract=fast(默认值)。
  2. \n

\n对于Clang来说,它使用fma的情况有:\n

    \n

  1. 使用-ffp-contract=fast
  2. \n

  3. 使用-ffp-contract=on(默认值)和#pragma STDC FP_CONTRACT ON(默认值为OFF)。
  4. \n

\n换句话说,对于Clang,你可以使用#pragma STDC FP_CONTRACT ON(因为-ffp-contract=on是默认值)或使用-ffp-contract=fast来获得fma。而-ffast-math(因此-Ofast)设置-ffp-contract=fast。\n


\n我研究了MSVC和ICC。\n在MSVC中,使用/O2 /arch:AVX2 /fp:fast选项会使用fma指令。在MSVC中,默认值为/fp:precise。\n在ICC中,使用-O3 -march=core-avx2(实际上只需要-O1)会使用fma,这是因为默认情况下ICC使用-fp-model fast。但是,即使使用-fp-model precise,ICC也会使用fma。要禁用ICC中的fma,请使用-fp-model strict-no-fma。\n因此,默认情况下,GCC和ICC在启用fma时使用fma(对于GCC/Clang来说是-mfma,对于ICC来说是-march=core-avx2),而Clang和MSVC则不使用。

0
0 Comments

在C99中引入了一项新功能,即"pragma FP_CONTRACT",它允许使用融合乘加(fused multiply-add)。然而,使用此功能时必须满足一个重要的条件,即"pragma FP_CONTRACT"不能关闭。这是由于PowerPC处理器从一开始就支持融合乘加,实际上,x*y等同于fma(x, y, 0),x+y等同于fma(1.0, x, y)。

需要注意的是,控制融合乘加的是"pragma FP_CONTRACT",而不是"FLT_EVAL_METHOD"。尽管如果"FLT_EVAL_METHOD"允许更高精度,那么融合乘加总是合法的;只需假设操作是以非常高的精度执行并进行舍入。

fma函数在需要精度而不是速度时非常有用。即使硬件不支持,它也可以缓慢但正确地计算融合乘加结果。如果硬件支持,它应该被内联。

关于GCC是否可以仅在使用"-O3"编译选项时使用fma函数,我认为这在一定程度上回答了我的第一个问题。但仍不清楚它是否符合IEEE标准。由于GCC定义了"__STDC_IEC_559__",我可以假设它是符合IEEE标准的,但其他人声称fma函数违反了这一标准(这将意味着在定义了"__STDC_IEC_559__"时,GCC无权这样做)。所以我仍然感到困惑。

我注意到了我提供的链接中关于"pragma"的说明,但我不知道它有多新或广泛支持。这就是为什么我之前没有提到它的原因。

好吧,GCC似乎并不关心该"pragma",所以这个问题无关紧要。而且无论如何,文档中并没有提到它是否符合IEEE标准。GCC返回了"__STDC_IEC_559__",同时使用了"-ffp-contract=fast",所以我仍然想知道这是否存在矛盾之处。

0
0 Comments

问题:Fused multiply add和默认的舍入模式

Fused multiply add(FMA)是一种计算机指令,用于执行乘法和加法操作。它可以通过一个指令来实现乘法和加法的组合,从而提高计算效率。然而,对于FMA指令的默认舍入模式,存在一些争议。

根据IEEE-754标准,对于FMA指令的舍入模式,需要依赖于编程语言的规定。在标准C中,可以使用STDC FP_CONTRACT指令来控制这种舍入模式的优化。GCC编译器默认支持FMA指令的舍入模式优化,但可以通过设置STDC FP_CONTRACT OFF来禁用这种优化。如果不支持STDC FP_CONTRACT指令,则不符合C语言标准。

当问到“不支持STDC FP_CONTRACT指令意味着不符合C语言标准”时,意味着不符合C语言标准的意思。值得注意的是,GCC似乎忽略了STDC FP_CONTRACT指令,而是只使用了-ffp-contract选项。而Clang编译器则同时支持这两种指令。

这里所说的“不符合C语言标准”是指FP_CONTRACT是C语言标准的一部分,忽略它就是不符合标准。

所以,之前提到的回答是错误的,即“编译器将通过融合违反严格的IEEE浮点行为”。这就是我误解的原因。

IEEE标准将这个问题的决策权交给了编程语言,因此,如果一个实现不遵守编程语言的标准,那么至少违背了IEEE 754标准的精神。

如果GCC识别FP_CONTRACT指令,可以将其默认设置为ON,那么之前的回答就是错误的。而且无论如何,GCC都支持-ffp-contract选项,这实际上起到了相同的作用。换句话说,Clang识别FP_CONTRACT指令,并将其默认设置为OFF,如果将其默认设置为FP_CONTRACT ON,会违反IEEE标准吗?

默认值可以是ON或OFF,但是需要支持FP_CONTRACT指令才能符合标准。

0