一尘不染

为什么 GCC 不将 a*a*a*a*a*a 优化为 (a*a*a)*(a*a*a)?

javascript

我正在对科学应用程序进行一些数值优化。我注意到的一件事是 GCC 会pow(a,2)通过将调用编译成来优化调用a*a,但调用pow(a,6)并没有优化,实际上会调用库函数pow,这大大降低了性能。(相比之下,英特尔 C++ 编译器可执行文件icc将消除对 的库调用pow(a,6)。)

我很好奇的是,当我替换pow(a,6)a*a*a*a*a*a使用 GCC 4.5.1 和选项“ -O3 -lm -funroll-loops -msse4”时,它使用了 5mulsd条指令:

movapd  %xmm14, %xmm13
mulsd   %xmm14, %xmm13
mulsd   %xmm14, %xmm13
mulsd   %xmm14, %xmm13
mulsd   %xmm14, %xmm13
mulsd   %xmm14, %xmm13

而如果我写(a*a*a)*(a*a*a),它会产生

movapd  %xmm14, %xmm13
mulsd   %xmm14, %xmm13
mulsd   %xmm14, %xmm13
mulsd   %xmm13, %xmm13

这将乘法指令的数量减少到 3。icc具有类似的行为。

为什么编译器无法识别这种优化技巧?


阅读 141

收藏
2022-02-09

共1个答案

一尘不染

因为浮点数学不是关联的。在浮点乘法中对操作数进行分组的方式会影响答案的数值准确性。

结果,大多数编译器对重新排序浮点计算非常保守,除非他们可以确定答案将保持不变,或者除非您告诉他们您不关心数值准确性。例如:允许 gcc 重新关联浮点操作的 gcc 选项,或者甚至允许-fassociative-math-ffast-math积极地在精度与速度之间进行权衡的选项。

2022-02-09