【问题标题】:What does gcc's ffast-math actually do?gcc 的 ffast-math 实际上是做什么的?
【发布时间】:2011-11-17 06:03:19
【问题描述】:

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

我确实尝试过挖掘 S.O.对于类似的问题,但找不到任何解释 ffast-math 工作原理的东西。

【问题讨论】:

    标签: performance math gcc floating-point fast-math


    【解决方案1】:

    -ffast-math 所做的不仅仅是打破严格的 IEEE 合规性。

    首先,当然,它确实违反了严格的 IEEE 合规性,例如允许将指令重新排序为数学上相同(理想情况下)但浮点不完全相同的东西。

    其次,它禁用在单指令数学函数之后设置errno,这意味着避免写入线程局部变量(这对于某些架构上的这些函数可能产生 100% 的差异)。

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

    第四,它为除法和倒数平方根启用倒数近似

    此外,它禁用有符号零(代码假定不存在有符号零,即使目标支持它)和舍入数学,这在编译时启用了常量折叠等功能。

    最后,它生成的代码假定不会由于信号/陷阱数学而发生硬件中断(也就是说,如果无法在目标架构上禁用这些中断并因此确实发生,它们将不会发生处理)。

    【讨论】:

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

    正如您所提到的,它允许不保持严格的 IEEE 合规性的优化。

    一个例子是这样的:

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

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

    由于浮点运算不具有关联性,运算的排序和因式分解会因四舍五入而影响结果。因此,这种优化不是在严格的 FP 行为下进行的。

    我实际上并没有检查 GCC 是否真的进行了这种特定的优化。但想法是一样的。

    【讨论】:

    • @Andrey:在这个例子中,你从 7 乘到 3。
    • @Andrey:从数学上讲,它是正确的。但是由于四舍五入的不同,最后几位的结果可能会略有不同。
    • 在大多数情况下,这种细微的差异并不重要(相对于double 的 10^-16 的顺序,但取决于应用程序)。需要注意的一件事是 ffast-math 优化不一定会添加“更多”舍入。它不符合 IEEE 的唯一原因是答案与所写的不同(尽管略有不同)。
    • @user:错误的大小取决于输入数据。相对于结果,它应该很小。例如,如果x 小于 10,则 Mystical 示例中的误差将下降 10^-10 左右。但是如果x = 10e20,错误可能是几百万。
    • @stefanct 实际上是关于-fassociative-math,它包含在-funsafe-math-optimizations 中,而-ffast-math Why doesn't GCC optimize a*a*a*a*a*a to (a*a*a)*(a*a*a)? 又启用了Why doesn't GCC optimize a*a*a*a*a*a to (a*a*a)*(a*a*a)?
    猜你喜欢
    • 2013-12-10
    • 2019-09-22
    • 2021-11-26
    • 2011-06-01
    • 2011-03-11
    • 2010-11-26
    • 2015-06-23
    • 2017-12-26
    • 2016-10-19
    相关资源
    最近更新 更多