如何判断哪段x86汇编代码的执行速度更快?
判断x86汇编执行速度的通用准则及代码对比分析
我知道不同编译器针对不同机器生成的x86汇编代码不一样,同一段x86汇编在不同机器上运行的时钟周期数也存在差异。想问问判断x86汇编代码执行速度的通用准则有哪些?
比如我现在有下面两段代码,希望实现「零开销抽象」,但没法验证作为bar抽象的foo是不是真的比bar慢,以及具体慢多少:
foo(ComputeArray<double, 4ul>&, double): mov rax, QWORD PTR [rdi] movapd xmm1, xmm0 addsd xmm0, xmm0 unpcklpd xmm1, xmm1 mov rax, QWORD PTR [rax] unpcklpd xmm0, xmm0 movupd xmm3, XMMWORD PTR [rax+224] movupd xmm2, XMMWORD PTR [rax+240] movupd xmm4, XMMWORD PTR [rax+272] mulpd xmm3, xmm1 mulpd xmm2, xmm1 movupd xmm1, XMMWORD PTR [rax+256] mulpd xmm1, xmm0 mulpd xmm0, xmm4 addpd xmm1, xmm3 addpd xmm0, xmm2 movups XMMWORD PTR [rax+192], xmm1 movups XMMWORD PTR [rax+208], xmm0 ret bar(double (*) [4], double): movapd xmm2, xmm0 movupd xmm1, XMMWORD PTR [rdi+256] movupd xmm0, XMMWORD PTR [rdi+272] movupd xmm4, XMMWORD PTR [rdi+224] movupd xmm6, XMMWORD PTR [rdi+240] unpcklpd xmm2, xmm2 addpd xmm1, xmm1 addpd xmm0, xmm0 addpd xmm1, xmm4 addpd xmm0, xmm6 mulpd xmm1, xmm2 mulpd xmm0, xmm2 movups XMMWORD PTR [rdi+192], xmm1 movups XMMWORD PTR [rdi+208], xmm0 ret
通用判断准则
- 内存访问效率:重点看内存操作的次数、寻址方式和缓存命中率。比如避免不必要的间接寻址,优先用对齐内存访问(
movapd在内存对齐时比movupd效率更高),减少缓存未命中的情况。 - 指令并行度:观察指令是否能被CPU的超标量流水线并行执行。尽量缩短关键数据依赖链,把无依赖的指令放在一起,让CPU能同时执行多条指令。
- 指令延迟与吞吐量:区分指令的延迟(指令执行完成到结果可用的时钟周期)和吞吐量(CPU每周期能执行的该类指令数量)。比如乘法指令延迟高于加法,要尽量减少关键路径上的高延迟指令。
- 寄存器使用效率:减少不必要的寄存器拷贝,避免寄存器数据溢出到内存。让常用数据尽可能留在寄存器中,降低内存往返的开销。
- 指令选择合理性:优先使用高效的SIMD指令批量处理数据(比如
mulpd、addpd这类打包指令),避免用标量指令重复处理相同逻辑。
针对foo和bar的代码分析
- 内存寻址差异:
foo多了两次间接寻址操作:mov rax, QWORD PTR [rdi]和mov rax, QWORD PTR [rax],这意味着要多两次内存读取操作。如果这两次访问的内存没命中缓存,开销会显著增加。而bar直接用rdi作为基地址做直接寻址,少了这两次额外的内存访问。 - 指令逻辑效率:
foo里先用标量加法addsd xmm0, xmm0,再通过unpcklpd扩展成打包数据;而bar直接用addpd对打包数据执行加法,一步完成批量操作,避免了标量转打包的额外指令,减少了延迟。 - 流水线调度差异:
foo中后续的内存加载指令依赖前面间接寻址得到的rax值,导致这些加载指令不能提前进入流水线执行;而bar的内存加载指令可以更早被CPU调度,提升了并行执行的可能性。
如果要量化两者的速度差异,可以:
- 用性能分析工具统计两段代码的执行周期数、缓存命中率、指令吞吐量等指标。
- 编写循环调用foo和bar的测试程序,统计大量迭代下的总执行时间,计算单次调用的平均耗时差。
内容的提问来源于stack exchange,提问作者user22056011
相关产品推荐
相关产品推荐

