【问题标题】:Fused multiply add and default rounding modes融合乘加和默认舍入模式
【发布时间】:2016-03-29 22:06:36
【问题描述】:

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

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

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

但是,this link

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

所以现在我很困惑和担心。

  1. GCC 是否合理地将 FMA 与 -O3 一起使用?
  2. 融合是否违反了严格的 IEEE 浮点行为?
  3. 如果融合确实违反了 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 设置为ONOFF 的一些排列,其中-ffp-contract 设置为onofffast。在所有情况下,我也使用了-O3 -mfma

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

GCC 它使用fma

  1. -ffp-contract=fast(默认)。

对于 Clang,它使用 fma

  1. -ffp-contract=fast
  2. 带有-ffp-contract=on(默认)和#pragma STDC FP_CONTRACT ON(默认为OFF)。

换句话说,使用 Clang,您可以使用 #pragma STDC FP_CONTRACT ON(因为 -ffp-contract=on 是默认值)或 -ffp-contract=fast 获得 fma-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(-mfma 用于 GCC/Clang 或 -march=core-avx2 用于 ICC)但 Clang 和 MSVC 不使用。

【问题讨论】:

  • 可能是编译器错误。考虑举报。
  • 我很确定 gcc 所做的一切正常。在阅读了有关收缩 FP 表达式的 FLT_EVAL_METHOD 文档后,我很惊讶 clang 这样做。我不会将此作为答案发布,因为它不是基于任何真正的标准文档,只是我对 认为事情应该如何工作/应该如何设计的理解,考虑到问题中的材料。
  • @FUZxxl,你认为浮点标签会比ieee-754更合适吗? (如果是这样,请随意更改)。我觉得我也应该使用浮点标签。
  • "融合是否违反严格的 IEEE 浮点行为?" --> 国际海事组织,是的。请改用double fma(double x, double y, double z);,因为这是一个函数调用,在优化的编译器中将调用预期的汇编代码。这并不违反“IEEE 浮点行为”。

标签: c gcc clang ieee-754 fma


【解决方案1】:

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

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

如果您不想要速度但想要精度,则 fma 函数很有用。即使它在硬件中不可用,它也会缓慢但正确地计算收缩结果。如果它在硬件中可用,则应该内联。

【讨论】:

  • 我认为这在一定程度上回答了我的第一个问题,即 GCC 是否在 -O3 的 fma 中是合理的。但目前尚不清楚它是否符合 IEEE 标准。而且由于 GCC 定义了__STDC_IEC_559__,那么我可以假设它符合 IEEE,但其他人声称 fma 打破了这一点(这会争辩说,当定义 __STDC_IEC_559__ 时,GCC 没有理由这样做)。所以我还是一头雾水。
  • @Zboson:我注意到我链接你的文档中关于编译指示的内容,但不知道它有多新或得到广泛支持。这就是我之前没有提到它的原因。
  • @PeterCordes,没关系,GCC 似乎并不关心那个 pragma,所以这是一个没有实际意义的问题。无论如何,它没有说明它符合 IEEE 标准。 GCC 返回__STDC_IEC_559__ 并同时使用-ffp-contract=fast 所以我还是想知道这是否矛盾。
【解决方案2】:

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

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

...

― 从乘法和加法合成 fusedMultiplyAdd 操作。

在标准 C 中,STDC FP_CONTRACT pragma 提供了控制这种值更改优化的方法。所以GCC默认被许可执行融合,只要它允许你通过设置STDC FP_CONTRACT OFF来禁用优化。不支持这意味着不遵守 C 标准。

【讨论】:

  • “不支持意味着不遵守 C 标准”是什么意思?顺便说一句,GCC 似乎忽略了STDC FP_CONTRACT。相反,它只使用-ffp-contract。 Clang 可以识别两者。
  • 我的意思是 FP_CONTRACT 是 C 标准的一部分。忽略它就是不符合。
  • 哦,我是否意识到您指的是 GCC 不支持 FP_CONTRACT(或任何不支持它的编译器)。现在我明白了。
  • 所以this answer 是错误的,那么“编译器将通过融合违反严格的 IEEE 浮点行为”?我就是这样。
  • 默认值可以是开或关。但是你需要支持pragma才能符合标准。
猜你喜欢
  • 2016-05-22
  • 2013-05-26
  • 1970-01-01
  • 1970-01-01
  • 2016-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-20
相关资源
最近更新 更多