我注意到编译器链接器的标志以我无法理解的方式影响正在运行的代码的一个有趣现象。
我有一个库,它提供相同算法的不同实现,以测试这些不同实现的运行速度。
最初,我用一对相同的实现测试了这种情况,以检查是否发生了正确的事情(两者的运行速度大致相同)。我首先使用以下编译器标志编译对象(每个实现一个):
-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 副作用的东西可以忽略不计)。
我还测试了库的输出正确性,所以我也可以相当有信心。