You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

cmp r14, qword ptr [rbx+8]是否比cmp rbx,r14更慢?循环性能疑问

两种汇编循环的性能差异分析

我想对比以下两个汇编循环是否存在性能差异:

.LOOP1:
        mov     edi, dword ptr [rbx]
        call    f(int)@PLT
        add     rbx, 4
        cmp     rbx, r14
        jne     .LOOP1
.LOOP2:
        mov     edi, dword ptr [r14]
        call    f(int)@PLT
        add     r14, 4
        cmp     r14, qword ptr [rbx + 8]
        jne     .LOOP2

两者的核心差异在于cmp指令:假设LOOP1中寄存器间的cmp仅耗时1周期,LOOP2中需要读取内存[rbx + 8]的cmp是否会耗时超过1周期?

补充说明:这段代码来源于C++ vector的两种元素访问方式,仅从cmp指令的性能差异来看,我认为范围for循环的表现略优。

对应的C++代码:

#include <vector>

using namespace std;
int f(int);

void range(const vector<int> &input) {
    for (int i : input) {
        f(i);
    }
}

void iter(const vector<int> &input) {
    for (auto i = input.begin(); i != input.end(); ++i) {
        f(*i);
    }
}

gcc -O3编译后的汇编代码:

range(std::vector<int, std::allocator<int> > const&):
        push    rbp
        push    rbx
        sub     rsp, 8
        mov     rbx, QWORD PTR [rdi]
        mov     rbp, QWORD PTR [rdi+8]
        cmp     rbp, rbx
        je      .L1
.L3:
        mov     edi, DWORD PTR [rbx]
        add     rbx, 4
        call    f(int)
        cmp     rbp, rbx
        jne     .L3
.L1:
        add     rsp, 8
        pop     rbx
        pop     rbp
        ret
iter(std::vector<int, std::allocator<int> > const&):
        push    rbp
        push    rbx
        sub     rsp, 8
        mov     rbx, QWORD PTR [rdi]
        cmp     rbx, QWORD PTR [rdi+8]
        je      .L7
        mov     rbp, rdi
.L9:
        mov     edi, DWORD PTR [rbx]
        add     rbx, 4
        call    f(int)
        cmp     QWORD PTR [rbp+8], rbx
        jne     .L9
.L7:
        add     rsp, 8
        pop     rbx
        pop     rbp
        ret

性能差异分析

  • LOOP1的寄存器间比较:cmp rbx, r14是寄存器到寄存器的操作,现代CPU微架构下这类指令延迟通常为1周期,且能被流水线完全吸收,没有额外内存开销。
  • LOOP2的寄存器-内存比较:cmp r14, qword ptr [rbx + 8]需要先从内存地址rbx+8加载数据到临时寄存器,再执行比较:
    • 若该地址已在L1缓存中,内存加载延迟约3-4周期(不同CPU略有差异),加上比较的1周期,总耗时明显超过1周期;
    • 若缓存未命中,需要从L2/L3或主存加载,耗时会大幅增加,但vector的end地址属于容器头部数据,大概率会被高频访问并留在L1缓存中,因此主要额外开销是L1加载延迟。

结合C++代码的对应逻辑:

  • 范围for循环生成的range函数,提前将vector的end地址加载到rbp寄存器,循环内仅用寄存器比较,避免了每次循环的内存读取;
  • 迭代器循环生成的iter函数,每次循环都要重新读取内存中的end地址,即使缓存命中,也会带来持续的额外延迟。

结论

在循环次数较多的场景下,范围for循环生成的汇编代码确实会比这类迭代器循环略优,累计的延迟差异会体现出性能差距。

内容的提问来源于stack exchange,提问作者A. K.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 00:06:28