Performance gcc';什么是数学?

Performance gcc';什么是数学?,performance,math,gcc,floating-point,fast-math,Performance,Math,Gcc,Floating Point,Fast Math,我知道gcc的--ffast math标志可以极大地提高浮点运算的速度,并且超出了IEEE标准,但我似乎找不到它启用时实际发生的情况的信息。任何人都可以解释一些细节,也许可以给出一个明确的例子,说明如果国旗打开或关闭,会发生什么变化 我确实试着在S.O.中寻找类似的问题,但找不到任何解释ffast数学工作原理的东西。正如您所提到的,它允许进行优化,而不会保持严格的IEEE合规性 例如: x = x*x*x*x*x*x*x*x; 到 由于浮点运算是非关联的,运算的顺序和因式分解将由于舍入而影响结

我知道gcc的
--ffast math
标志可以极大地提高浮点运算的速度,并且超出了IEEE标准,但我似乎找不到它启用时实际发生的情况的信息。任何人都可以解释一些细节,也许可以给出一个明确的例子,说明如果国旗打开或关闭,会发生什么变化


我确实试着在S.O.中寻找类似的问题,但找不到任何解释ffast数学工作原理的东西。

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

例如:

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

由于浮点运算是非关联的,运算的顺序和因式分解将由于舍入而影响结果。因此,这种优化不是在严格的FP行为下进行的


我还没有检查GCC是否真的进行了这种特殊的优化。但是想法是一样的。

-ffast math
所做的远远不止是破坏严格的IEEE合规性

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

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

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

第四,它支持除法和平方根倒数的倒数近似

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


最后,它生成的代码假设由于信令/捕获数学的原因不会发生硬件中断(也就是说,如果无法在目标体系结构上禁用这些中断,那么它们将不会被处理)。

@Andrey:对于本例,您将从7倍降到3倍。@Andrey:从数学上讲,这是正确的。但由于舍入不同,最后几位的结果可能略有不同。在大多数情况下,这一微小的差异无关紧要(对于
双精度
,相对而言约为10^-16,但取决于应用程序)。需要注意的一点是,ffast数学优化不一定会添加“更多”舍入。它不符合IEEE标准的唯一原因是,答案与所写内容不同(尽管略有不同)。@user:错误的大小取决于输入数据。相对于结果,它应该很小。例如,如果
x
小于10,则Mystical示例中的错误将降低约10^-10。但是如果
x=10e20
,则错误可能会高达数百万。@stefant它实际上是关于
-fasociative math
的,它包含在
-funsafe math optimizations
中,而
-ffast math
又会启用Damon,谢谢!你能添加一些参考资料吗?例如“
-ffast math
设置-fno math errno,-funsafe math optimizations,-ffinite math only,-fno舍入math,-fno信令nans和-fcx限制范围。此选项导致定义预处理器宏FAST\u math。”以及glibc中的一些内容,例如(
math
near math\u errhandling)“默认情况下,所有函数都支持errno和异常处理。在gcc的快速数学模式下,如果定义了内联函数,这可能不是真的。“@javapowered:它是否“危险”取决于您需要什么保证。
-ffast math
允许编译器走捷径,打破一些承诺(如前所述),一般来说,这并不危险,对大多数人来说也不是问题。对大多数人来说,这是一样的,只是速度更快。但是,如果你的代码假设并依赖这些承诺,那么你的代码的行为可能会与你预期的不一样。通常,这意味着程序看起来工作得很好,但有些结果可能“出乎意料”(比如说,在物理模拟中,两个物体可能不会正确碰撞)。@Royi:这两个物体应该相互独立。
-O2
通常启用“每一个”合法的优化,除了那些以大小换取速度的优化。
-O3
也支持以大小换取速度的优化。它仍然保持100%的正确性。
-ffast math
试图通过允许“稍微不正确”来加快数学运算通常是无害的行为,但根据标准的措辞,这种行为是不正确的。如果您的代码在两个编译器上的速度确实有很大差异(不仅仅是1-2%)然后检查您的代码是否严格符合标准,并且……不会产生任何警告。此外,请确保您不会妨碍别名规则和自动矢量化之类的事情。原则上,GCC的性能至少应该一样好(根据我的经验,通常会更好)作为MSVC。如果不是这样的话,你可能犯了一个MSVC忽略的微妙错误,但这会导致GCC禁用一个优化。如果你想要两个选项,你应该同时给出这两个选项,是的。@Royi:在我看来,这段代码并不是很小很简单,没有人能在几分钟(甚至几个小时)内深入分析。除此之外,它还涉及一个看似无害的
#pragma omp parallel for
,在循环体中,您都在读取和写入函数参数所指向的地址,并进行大量的分支。作为一个未经教育的猜测,您可能正在从您的实现定义的invokatio中敲打缓存n个线程,MSVC可能会错误地避免别名规则要求的中间存储。
x *= x;
x *= x;
x *= x;