在icc 19上,点积编译成fma指令的循环。在clang和gcc上,fma只与-ffast-math一起生成。
然而,-ffast-math破坏了IEEE-754的遵从性,但是fma完全符合IEEE-754 2008,所以如果我必须使用-ffast-math编译,那么我会引发其他问题。
为什么gcc和clang不生成没有-ffast-math的fma指令
哥德波特;编译器标志是-O3 -march=skylake-avx512,+- -ffast-math。
发布于 2021-02-12 07:23:19
编译器是否应该使用合并乘法/加法来实现用dot({a,c}, {b,d}) := a*b + c*d编写的点积,从而给出fl(⋅+ fl(⋅)),就好像它是写的fma(a,b, c*d)一样?一般不会!
下面是从W. Kahan关于IEEE 754的讲稿改编的几个例子
dot({x,y}, {-x,y}) = x*x - y*y。这种天真的公式在≈时会被灾难性地取消,但至少当=时,它可靠地返回零,因为fl(fl(2)−fl(2 2))=fl(M2)−fl(2)= fl( 0 ) =0。
这可以用FMA作为fma(x,x, -y*y)来计算。但如果= fl(1.234) = 0x1.3be76c8b43958p+0,则结果是IEEE 754 binary64算法中的−1.3532e7b3d8ep−55≈−3.352 × 10⁻⁷,而不是我们所希望的零。
它不仅是非零的,而且是负的,所以如果你试图取下游的平方根,即使你能保证上游的NaN,你也会遇到≥。
(当然,保理(x + y)*(x - y)更好地避免中间灾难性取消,但这个问题是关于在不附加假设的情况下评估点积。)dot({a,d}, {b,c}) = a*d + b*c。它可以用FMA作为fma(a,d, b*c)来计算。
您可能会期望复数+的乘积+及其复共轭−是实的,零虚部,如果用fma(a,d, b*c)计算,它是实的,而不是用fma(a,d, b*c)计算的。例如,如果= fl(1.234) = 1.3be76c8b43958p+0和= fl(5.678) = 1.6b645a1cac083p+2,则fl(⋅(−) +fl(⋅))=−1.6f6512a94ffp−55≈3.983 × 10⁻=⁷)。因此,在这些场景中使用FMA将是糟糕的形式,而不需要您显式地要求使用来自fma(a,b, c*d)的fma函数编写FMA,或者添加#pragma STDC FP_CONTRACT ON来授权这样的恶作剧。
上面写着…仅仅通过传递说服GCC 10.2滥用vfmadd231sd (即使是使用显式#pragma STDC FP_CONTRACT OFF和国际刑事法院也是如此21.1.9 )似乎并不困难。在我看来,这就像一个buggy优化器!相反,#pragma STDC FP_CONTRACT ON,但没有将语用省略或设置为OFF。
https://stackoverflow.com/questions/55974090
复制相似问题