融合乘加和默认舍入模式
Fused multiply add and default rounding modes
使用 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 看来 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
(因为 -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 即使 -fp-model precise
也使用 fma。要使用 ICC 禁用 fma,请使用 -fp-model strict
或 -no-fma
.
因此默认情况下 GCC 和 ICC 在启用 fma 时使用 fma(-mfma
用于 GCC/Clang 或 -march=core-avx2
用于 ICC)但 Clang 和 MSVC 不使用。
当您引用允许融合乘加时,您遗漏了重要条件 "unless pragma FP_CONTRACT is off"。这是 C 中的一个新特性(我认为是在 C99 中引入的)并且是 PowerPC 绝对必要的,all 从一开始就融合了乘加法——实际上,x*y 是等价的到 fma (x, y, 0) 和 x+y 相当于 fma (1.0, x, y)。
FP_CONTRACT 是控制融合的 multiply/add,而不是 FLT_EVAL_METHOD。尽管如果 FLT_EVAL_METHOD 允许更高的精度,那么契约总是合法的;只是假装这些操作是以非常高的精度执行的,然后四舍五入。
如果您不想要速度,而是想要精度,那么 fma 函数很有用。它会缓慢但正确地计算合同结果,即使它在硬件中不可用。如果它在硬件中可用,则应该内联。
它不违反 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
编译指示提供了控制此 value-changing 优化的方法。所以 GCC 被许可在默认情况下执行融合,只要它允许您通过设置 STDC FP_CONTRACT OFF
来禁用优化。不支持那意味着不遵守 C 标准。
使用 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 使用 -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
显然这可以通过选项 -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
(因为 -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 即使 -fp-model precise
也使用 fma。要使用 ICC 禁用 fma,请使用 -fp-model strict
或 -no-fma
.
因此默认情况下 GCC 和 ICC 在启用 fma 时使用 fma(-mfma
用于 GCC/Clang 或 -march=core-avx2
用于 ICC)但 Clang 和 MSVC 不使用。
当您引用允许融合乘加时,您遗漏了重要条件 "unless pragma FP_CONTRACT is off"。这是 C 中的一个新特性(我认为是在 C99 中引入的)并且是 PowerPC 绝对必要的,all 从一开始就融合了乘加法——实际上,x*y 是等价的到 fma (x, y, 0) 和 x+y 相当于 fma (1.0, x, y)。
FP_CONTRACT 是控制融合的 multiply/add,而不是 FLT_EVAL_METHOD。尽管如果 FLT_EVAL_METHOD 允许更高的精度,那么契约总是合法的;只是假装这些操作是以非常高的精度执行的,然后四舍五入。
如果您不想要速度,而是想要精度,那么 fma 函数很有用。它会缓慢但正确地计算合同结果,即使它在硬件中不可用。如果它在硬件中可用,则应该内联。
它不违反 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
编译指示提供了控制此 value-changing 优化的方法。所以 GCC 被许可在默认情况下执行融合,只要它允许您通过设置 STDC FP_CONTRACT OFF
来禁用优化。不支持那意味着不遵守 C 标准。