我知道gcc的--ffast-math标志可以大大提高浮点运算的速度,并且超出了IEEE标准,但是我似乎找不到关于它打开时到底发生了什么的信息。有人能解释一些细节吗?也许给予一个明确的例子,说明如果标志打开或关闭,某些东西会发生什么变化?我确实试着在S.O.中挖掘类似的问题,但找不到任何解释快速数学工作原理的东西。
--ffast-math
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.0和x * 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,用于包括比较在内的指令的次正常输入。)
-ffast-math
-fno-rounding-math
errno
-fno-math-errno
lrint
-ffinite-math-only
-funsafe-math-optimizations
-fno-signed-zeros
x + 0.0
x
x - 0.0
x * 1.0
-fno-trapping-math
fenv
sqrt(-1)
-ftrapping-math
feclearexcept()
fetestexcept()
mxcsr
大多数代码都可以使用-O3 -fno-math-errno -fno-trapping-math。与-ffast-math的其他部分不同,它们永远不会影响数值结果,只会影响其他副作用是否被认为是优化器试图保留的重要因素。(-fno-signaling-nans已经是默认值,不需要指定。)
-O3 -fno-math-errno -fno-trapping-math
-fno-signaling-nans
jaql4c8m2#
正如您提到的,它允许不严格遵守IEEE规范的优化。例如:
x = x*x*x*x*x*x*x*x;
字符串到
x *= x; x *= x; x *= x;
型由于浮点运算不是关联的,运算的顺序和因式分解会因为舍入而影响结果。因此,这种优化不是在严格的FP行为下进行的。我还没有真正检查GCC是否真的做了这种特殊的优化,但想法是一样的。
2条答案
按热度按时间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.0
和x * 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上,它设置SSEmxcsr
FTZ和DAZ控制位,以将子法线刷新为0,而不是进行逐渐下溢(在许多CPU上需要微码辅助。)(FTZ = Flush To Zero,用于次正常结果,DAZ = Denormales Are Zero,用于包括比较在内的指令的次正常输入。)大多数代码都可以使用
-O3 -fno-math-errno -fno-trapping-math
。与-ffast-math
的其他部分不同,它们永远不会影响数值结果,只会影响其他副作用是否被认为是优化器试图保留的重要因素。(-fno-signaling-nans
已经是默认值,不需要指定。)jaql4c8m2#
正如您提到的,它允许不严格遵守IEEE规范的优化。
例如:
字符串
到
型
由于浮点运算不是关联的,运算的顺序和因式分解会因为舍入而影响结果。因此,这种优化不是在严格的FP行为下进行的。
我还没有真正检查GCC是否真的做了这种特殊的优化,但想法是一样的。