gcc的ffast-math实际上做了什么?

Pon*_*nml 138 math floating-point performance gcc fast-math

我理解gcc的--ffast-math标志可以大大提高浮动操作的速度,并超出IEEE标准,但我似乎无法找到有关它正在发生的事情的信息.任何人都可以解释一些细节,并可能给出一个明确的例子,说明如果标志开启或关闭会有什么变化?

我确实尝试过挖掘SO以寻找类似的问题,但却找不到任何解释ffast-math工作原理的东西.

Dam*_*mon 240

-ffast-math 不仅仅是打破严格的IEEE合规性.

首先,当然,它确实打破了严格的IEEE合规性,允许例如将指令重新排序到数学上相同(理想情况下)但在浮点上不完全相同的事物.

其次,它禁用errno单指令数学函数后的设置,这意味着避免写入线程局部变量(这可以使某些体系结构上的那些函数有100%的差异).

第三,它假设所有数学都是有限的,这意味着没有对NaN(或零)进行检查,因为它们会产生不利影响.简单地假设这不会发生.

第四,它实现了除法和倒数平方根的倒数近似.

此外,它禁用有符号零(代码假定有符号零不存在,即使目标支持它)和舍入数学,这使得在编译时可以进行常量折叠.

最后,它生成的代码假定由于信令/捕获数学不会发生硬件中断(也就是说,如果在目标体系结构上无法禁用这些中断并因此确实发生,则不会处理它们).

  • 达蒙,谢谢!你能加点引用吗?像[gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html](http://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html)"``-ffast-math`*设置-fno -math-errno,-funsafe-math-optimizations,-finite-math-only,-fno-rounding-math,-fno-signaling-nans和-fcx-restricted-range.此选项导致预处理器宏__FAST_MATH__被定义.*"和glibc中的东西一样,(`math.h`在math_errhandling附近)"*默认情况下,所有函数都支持errno和异常处理.在gcc的快速数学模式中,如果定义了内联函数,则可能不是这样.*" (13认同)
  • @javapowered:它是否"危险"取决于你需要什么保证.`-ffast-math`允许编译器削减一些角落并打破一些承诺(如所解释的),这通常不是危险的,对大多数人来说不是问题.对于大多数人来说,它是相同的,只是更快.但是,如果您的代码_asume并依赖于这些承诺,那么您的代码可能会表现出与预期不同的行为.通常,这意味着程序看起来会很好地工作,但是某些结果可能是"意外的"(例如,在物理模拟中,两个对象可能不会正常碰撞). (3认同)
  • @Royi:两者应该相互独立.`-O2`通常会启用"每个"合法优化,除了那些交易速度的大小.`-O3`还支持优化交易大小以获得速度.它仍然保持100%的正确性.`-ffast-math`试图通过允许"略微不正确"的行为来加快数学运算,这通常是无害的,但是标准的措辞会被认为是不正确的.如果你的代码确实在两个编译器(不仅仅是1-2%)上的速度有很大差异,那么请检查你的代码是否严格符合标准...... (2认同)
  • ... 产生零警告。另外,请确保您不会妨碍别名规则和诸如自动矢量化之类的东西。原则上,GCC 的性能应该至少与 MSVC 一样好(根据我的经验通常更好)。如果不是这种情况,您可能犯了一个微妙的错误,MSVC 会忽略该错误,但会导致 GCC 禁用优化。如果你想要这两个选项,你应该给出这两个选项,是的。 (2认同)
  • @Royi:对我来说,该代码看起来并不小且简单,不是可以在几分钟(甚至几小时)内深入分析的东西。除其他外,它涉及一个看似无害的“#pragma omp parallel for”,并且在循环体内,您既可以读取函数参数指向的地址,也可以写入函数参数指向的地址,并执行大量的分支。作为一个未经教育的猜测,您可能会在实现定义的线程调用中破坏缓存,并且 MSVC 可能会错误地避免别名规则要求的中间存储。不可能说清楚。 (2认同)

Mys*_*ial 77

如您所述,它允许优化不保持严格的IEEE合规性.

一个例子是:

x = x*x*x*x*x*x*x*x;
Run Code Online (Sandbox Code Playgroud)

x *= x;
x *= x;
x *= x;
Run Code Online (Sandbox Code Playgroud)

由于浮点运算不是关联的,因此操作的排序和分解会因舍入而影响结果.因此,这种优化不是在严格的FP行为下完成的.

我实际上没有检查过GCC是否真的进行了这种特殊的优化.但这个想法是一样的.

  • @Andrey:对于这个例子,你从7乘以3变为3. (23认同)
  • @Andrey:数学上,这是正确的.但由于不同的舍入,结果可能在最后几位略有不同. (4认同)
  • @stefanct实际上是关于-fassociative-math的,它包含在`-funsafe-math-optimizations`中,而后者又通过`-ffast-math`启用[为什么GCC不优化`a * a * a * a * a * a`到`(a * a * a)*(a * a * a)`?](/sf/ask/450131391/) (3认同)
  • 在大多数情况下,这种细微的差异无关紧要(相对于 `double` 的数量级为 10^-16,但因应用程序而异)。需要注意的一件事是,ffast-math 优化不一定会增加“更多”的舍入。它不符合 IEEE 标准的唯一原因是答案与所写的不同(尽管略有不同)。 (2认同)