gcc的ffast-math到底是做什么的?

wecizke3  于 12个月前  发布在  其他
关注(0)|答案(2)|浏览(369)

我知道gcc的--ffast-math标志可以大大提高浮点运算的速度,并且超出了IEEE标准,但是我似乎找不到关于它打开时到底发生了什么的信息。有人能解释一些细节吗?也许给予一个明确的例子,说明如果标志打开或关闭,某些东西会发生什么变化?
我确实试着在S.O.中挖掘类似的问题,但找不到任何解释快速数学工作原理的东西。

trnvg8h3

trnvg8h31#

-ffast-math不仅仅是违反严格的IEEE合规性。
请注意,-fno-rounding-math是默认值,因此GCC假设舍入模式是IEEE默认的最近和偶数,允许编译时常量折叠。
首先,当然,它 * 确实打破了 * 严格的IEEE合规性,允许例如将指令重新排序为数学上相同(理想情况下)但在浮点中不完全相同的东西。
其次,它在单指令数学函数之后 * 禁用 * 设置errno,这意味着避免对线程局部变量进行写入(这可能会使某些体系结构上的这些函数产生100%的差异)。-fno-math-errno在数学调用后不读取errno的程序中是完全安全的,并且还允许更好地内联函数,如lrint。(例如在x86上使用SSE 4:Godbolt。)在ISO C中,从math.h函数设置errno是可选的,因此这部分快速数学仍然符合标准。
第三,它假设 * 所有的数学都是有限的 *,这意味着在会产生不利影响的地方不会对NaN(或零)进行检查。它只是假设这不会发生。(-ffinite-math-only
第四,它支持除法和平方根倒数的倒数近似(-funsafe-math-optimizations支持这一点和其他事情)。
此外,它还禁用了有符号零(代码假设有符号零不存在,即使目标支持它)和舍入数学,这使得在编译时可以进行常量折叠。(-fno-signed-zeros)。例如,这允许将x + 0.0优化为x。如果没有该选项,只有x - 0.0x * 1.0可以优化为x
最后,它生成的代码假设没有硬件中断可以发生,由于信令/陷阱数学(也就是说,如果这些不能在目标架构上禁用,因此 * 确实发生 *,它们将不会被处理)。
-fno-trapping-math的另一个影响是是否设置fenv标志(当异常被屏蔽时)不被认为是可观察到的副作用。(默认情况下,所有FP异常都被屏蔽,不管是否快速数学,例如sqrt(-1)给出NaN而不是提高SIGFPE。)GCC的默认值是-ftrapping-math,但它并不完美,有时允许优化,将可能的FP异常的数量从0更改为非零,反之亦然(如果这是它一开始就试图保护的东西?)。更糟糕的是,有时会阻塞安全优化。对于不使用fenv stuff的代码,如feclearexcept()fetestexcept()-fno-trapping-math是安全的(至少在普通ISA上),并且可以实现重要的优化。例如,请参阅 * Why gcc is so much worse at std::vector vectorization of a conditional multiply than clang? *。
当链接时使用-ffast-math时,GCC将与设置FPU标志的CRT启动代码链接。例如,在x86上,它设置SSE mxcsr FTZ和DAZ控制位,以将子法线刷新为0,而不是进行逐渐下溢(在许多CPU上需要微码辅助。)(FTZ = Flush To Zero,用于次正常结果,DAZ = Denormales Are Zero,用于包括比较在内的指令的次正常输入。)

大多数代码都可以使用-O3 -fno-math-errno -fno-trapping-math。与-ffast-math的其他部分不同,它们永远不会影响数值结果,只会影响其他副作用是否被认为是优化器试图保留的重要因素。(-fno-signaling-nans已经是默认值,不需要指定。)

jaql4c8m

jaql4c8m2#

正如您提到的,它允许不严格遵守IEEE规范的优化。
例如:

x = x*x*x*x*x*x*x*x;

字符串

x *= x;
x *= x;
x *= x;


由于浮点运算不是关联的,运算的顺序和因式分解会因为舍入而影响结果。因此,这种优化不是在严格的FP行为下进行的。
我还没有真正检查GCC是否真的做了这种特殊的优化,但想法是一样的。

相关问题