x64架构下int/uint64_t对应汇编代码性能差异原因咨询
x64架构下两组汇编代码的性能差异分析
背景与测试代码
对应的原始C++代码如下:
for (int i = 1; i <= number; ++i) { if (number % i == 0) { std::cout << i << std::endl; } }
第一组汇编是将number和i声明为int时由编译器生成的;第二组则是将二者改为uint64_t(改用int64_t结果一致)时生成的。
测试发现:在11代Intel x64机器上,第一组指令执行速度比第二组快70%;在旧款Ivybridge机器上,第一组甚至比第二组快3倍——哪怕测试时number取的是大32位整数(在第二组中被视为64位类型)。
两组汇编指令对比
第一组(int版本)
mov ebx, 1 cmp edi, ebx jl SHORT $LN3@main npad 7 $LL4@main: mov eax, edi cdq idiv ebx test edx, edx je SHORT $LN61@main $LN2@main: inc ebx cmp ebx, edi jle SHORT $LL4@main $LN3@main:
第二组(uint64_t/int64_t版本)
mov ebx, 1 cmp rbx, rdi ja SHORT $LN3@main npad 4 $LL4@main: xor edx, edx mov rax, rdi div rbx test rdx, rdx je SHORT $LN61@main $LN2@main: inc rbx cmp rbx, rdi jbe SHORT $LL4@main $LN3@main:
性能差异的核心原因
1. 除法指令的硬件效率差距
x86-64 CPU的32位和64位除法指令在硬件层面的执行效率天差地别:
- 32位的
idiv ebx:在Intel酷睿系列(包括11代和Ivybridge)上,延迟通常在10-14个周期,而且除法执行单元的吞吐量较高,每3-4个周期就能启动一次新的除法操作。 - 64位的
div rbx:延迟高达20-40个周期,且吞吐量极低——大部分Intel CPU处理64位除法时,必须等上一次除法完全结束才能启动下一次,没法像32位除法那样并行调度操作。
哪怕测试用的是大32位整数,64位除法指令也会全程按64位宽度执行,完全没法利用32位除法的硬件优化,这是性能差距的核心原因。
2. 寄存器操作的额外开销
第二组使用64位寄存器带来了额外开销:
- 循环里的
inc rbx、cmp rbx, rdi这类操作,在Ivybridge这类旧CPU上,对比32位寄存器操作会有微小的延迟,循环几万次之后差距就被显著放大。 - 另外,64位无符号除法需要先执行
xor edx, edx清空高32位,而32位除法的cdq只是简单扩展符号位,操作更快、延迟更低。
3. 指令对齐的影响
第一组里的npad 7是编译器用来把循环体$LL4@main对齐到16字节边界的填充指令,第二组只做了4字节对齐。更严格的指令对齐能减少CPU取指时的缓存停顿,提升循环执行的流畅度,这点在旧CPU上的影响尤其明显。
内容的提问来源于stack exchange,提问作者arsha_ex
相关产品推荐
相关产品推荐

