2

我注意到编译器链接器的标志以我无法理解的方式影响正在运行的代码的一个有趣现象。

我有一个库,它提供相同算法的不同实现,以测试这些不同实现的运行速度。

最初,我用一对相同的实现测试了这种情况,以检查是否发生了正确的事情(两者的运行速度大致相同)。我首先使用以下编译器标志编译对象(每个实现一个):

-g -funroll-loops -flto -Ofast -Werror

然后在链接期间通过 gcc 以下标志:

-Ofast -flto=4 -fuse-linker-plugin

这提供了一个运行速度极快的库,但奇怪的是,对于链接期间包含在参数中的第一个对象,它可靠且可重复地快了约 7%(因此,如果先链接,则任一实现都更快)。

所以:

gcc -o libfoo.so -O3 -ffast-math -flto=4 -fuse-linker-plugin -shared support_obj.os obj1.os obj2.os -lm

对比

gcc -o libfoo.so -O3 -ffast-math -flto=4 -fuse-linker-plugin -shared support_obj.os obj2.os obj1.os -lm

第一种情况是 obj1 中的实现比 obj2 中的实现运行得更快。在第二种情况下,情况正好相反。需要明确的是,除了函数条目名称外,两种情况下的代码都是相同的。

-Ofast现在我通过在链接期间删除标志来删除这个奇怪的链接参数顺序差异(实际上加快了一点) 。

-Ofast我可以通过更改为来复制几乎相同的情况-O3 -ffast-math,但在这种情况下,我需要-ffast-math在链接期间提供,这再次导致奇怪的订购速度差异。我不确定为什么在链接期间保持加速-Ofast而不是-ffast-math-ffast-math没有传递时保持加速,但我可以接受这可能是由于链接时间优化在一种情况下传递相关信息而不是在另一种情况下传递。但这并不能解释速度差异。

删除-ffast-math意味着它的运行速度慢了约 8 倍。

是否有人能够阐明可能导致这种影响的原因?我真的很想知道是什么导致了这种有趣的行为,所以我不能不小心触发它。

运行速度测试是在 python 中使用库和 timeit 的包装器执行的,我相当确定这是在做正确的事情(我可以​​旋转订单和显示 python 副作用的东西可以忽略不计)。

我还测试了库的输出正确性,所以我也可以相当有信心。

4

1 回答 1

0

评论太长了,所以发布为答案:

由于在数学运算中获得错误结果的风险,我建议不要使用它。

使用-ffast_math和/或-Ofast可能导致不正确的结果,如 gcc 手册的以下摘录所示:

选项:-ffast-math设置选项:

  1. -fno-math-errno,
  2. -funsafe-数学优化,
  3. -仅限有限数学,
  4. -fno-四舍五入数学,
  5. -fno-signaling-nans 和
  6. -fcx-有限范围。

此选项导致__FAST_MATH__定义预处理器宏。

任何 -O 选项都不会打开此选项,-Ofast因为它可能导致依赖于数学函数的 IEEE 或 ISO 规则/规范的精确实现的程序的错误输出。但是,对于不需要这些规范保证的程序,它可能会产生更快的代码。"

选项:-Ofast

无视严格的标准合规性。-Ofast启用所有-O3优化。它还支持并非对所有符合标准的程序都有效的优化。它打开-ffast-math和特定于 Fortran 的-fno-protect-parens-fstack-arrays.

于 2015-12-19T04:19:21.513 回答