我想看看在我们的编译中是否有写作之间的收获
x/3.0
对比
const double one_over_three = 1.0/3.0;
x * one_over_three;
但是我需要一些阅读汇编的帮助,我还没有学过。
我编译了
int div3(double x) {
const double three = 1/3.0;
return x*three;
}
得到了
div3(double):
pushq %rbp
movq %rsp, %rbp
movsd %xmm0, -24(%rbp)
movabsq $4599676419421066581, %rax
movq %rax, -8(%rbp)
movsd -24(%rbp), %xmm1
movsd .LC0(%rip), %xmm0
mulsd %xmm1, %xmm0
cvttsd2si %xmm0, %eax
popq %rbp
ret
.LC0:
.long 1431655765
.long 1070945621
然后编译
int div3(double x) {
return x/3.0;
}
得到了
div3(double):
pushq %rbp
movq %rsp, %rbp
movsd %xmm0, -8(%rbp)
movsd -8(%rbp), %xmm0
movsd .LC0(%rip), %xmm1
divsd %xmm1, %xmm0
cvttsd2si %xmm0, %eax
popq %rbp
ret
.LC0:
.long 0
.long 1074266112
Q1:有人可以帮我阅读程序集吗?
Q2:每个代码中最后两行的含义是什么?
如果这就是您进行优化的方式,那么您的做法错误。对不起,我在解释某事时通常不会使用否定词,但你走错了路。
你做的事情叫做过早优化和微优化。您正在尝试优化您不知道需要优化的东西。首先,这是一个破坏交易的因素:使用编译器优化。然后您通常会分析并尝试优化热点。
让我们看看您尝试进行的优化的相关性如何:
我首先使用 -O3
(gcc 6.1) 编译:让我们看看输出(也在 Godbolt compiler explorer 上):
mul_inverse3(double):
mulsd .LC0(%rip), %xmm0
cvttsd2si %xmm0, %eax
ret
div3(double):
divsd .LC1(%rip), %xmm0
cvttsd2si %xmm0, %eax
ret
其中 .LCO
和 .LC1
是常量:最接近 1/3.0 的 double
和 3.0
:
.LC0:
.long 1431655765
.long 1070945621
.LC1:
.long 0
.long 1074266112
好的,您已经看到编译器自己可以做多少事情了。这是您应该一直关注的内容,而不是嘈杂的 -O0
输出。
哪个更快?根据经验,在所有 CPU 上乘法都比除法快。该比率取决于特定的微体系结构。对于 x86,请参阅 Agner Fog's instruction tables and microarch pdf 和 x86 标签 wiki 中的其他性能链接。例如,英特尔 Sandybridge 上的延迟约为 3 倍,吞吐量约为 1/12。而且,div 甚至可能不是瓶颈,因此 div 与 mul 可能不会影响整体性能。缓存未命中、其他管道停顿,甚至其他类似 IO 的事情都可以完全隐藏差异。
但它们还是有区别的。我们可以从编译器中获取相同的代码吗? 是!添加-ffast-math
。使用此选项,编译器可以重新排列/更改 float 操作,即使它稍微改变了结果(这是您尝试手动执行的操作)。不过要小心,因为这适用于整个程序。
mul_inverse3(double):
mulsd .LC0(%rip), %xmm0
cvttsd2si %xmm0, %eax
ret
div3(double):
mulsd .LC0(%rip), %xmm0
cvttsd2si %xmm0, %eax
ret
我们可以做更多吗?是:添加 -march=native
并在编译器文档中搜索更多开关。
所以这是第一课:让编译器首先进行优化!
这是第二个:
您花了 1 周时间尝试优化随机操作。你终于做到了!经过一周的辛勤工作和不眠之夜,您的操作速度提高了 10 倍(哇!恭喜)。然后你运行你的程序,发现程序只快了 1%。惊恐的事件!这怎么可能?好吧,如果您的程序只花费 1% 的时间来执行该操作,那么……您明白了。或者可以有其他机制,如操作系统优化、CPU 管道等,使操作重复 100 次消耗远少于 100 倍。您需要做的是首先分析!找到热循环并对其进行优化!优化程序花费 60% 时间的循环/函数。
更重要的是,寻找高级优化,这样您的代码就可以做更少的工作,而不是更快地完成同样的工作。