← SEARCHABLE ARCHIVE
Assembly · 2026-05-12

编译器For语句优化

编译器在什么条件下优化For语句?

INTERACTIVE READING MODE →

该博客上撰写的文章是出于教学目的而创建的。 另外,文章中的信息可能不正确,请谨慎。

从CPU的角度来看,For语句中使用的JMP指令是: 因为当管道损坏或分支预测失败时会出现性能下降, 如果满足条件,编译器会尝试不使用 JMP 指令。

  1. 当编译时确认迭代次数为常数时。

可优化:for (int i = 0; i < 4; i++) (知道会重复4次) 优化难度:for(int i = 0; i < n; i++)(如果n是运行时确定的变量)

//原始代码
for (int i = 0; i < 3; i++) {
    arr[i] = 0;
}

//编译器优化后
arr[0] = 0;
arr[1] = 0;
arr[2] = 0;

优化前的汇编语言

.L2:
    cmp     DWORD PTR [rbp-4], 2    ;比较如果 i < 3
    jg      .L3                     ;如果不满足条件,则循环结束(跳转到L3)。
    mov     eax, DWORD PTR [rbp-4]
    cdqe
    mov     DWORD PTR [rbp-32+rax*4], 0 ;arr[i] = 0 赋值
    add     DWORD PTR [rbp-4], 1    ; i++
    jmp     .L2                     ;无条件跳回循环起点 (.L2) (JMP)
.L3:

优化的汇编语言

mov     DWORD PTR [rdi], 0      ; arr[0] = 0
mov     DWORD PTR [rdi+4], 0    ;arr[1] = 0(+4,因为 int 是 4 个字节)
mov     DWORD PTR [rdi+8], 0    ;arr[2] = 0(+8,因为 int 是 4 个字节)
  1. 最终结果何时可以在编译时提前计算出来。

当循环内部的逻辑不依赖于外部输入和输出时,结果值将被替换为常量。

//原始代码
int sum = 0;
for (int i = 1; i <= 100; i++) {
    sum += i;
}
return sum;

//编译器优化后
//JMP 在运行时甚至不执行加法运算。
return 5050;

优化前的汇编语言

.L5:
    cmp     DWORD PTR [rbp-4], 100  ;比较如果 i <= 100
    jg      .L6                     ;或者结束循环
    mov     eax, DWORD PTR [rbp-4]
    add     DWORD PTR [rbp-8], eax  ; sum += i
    add     DWORD PTR [rbp-4], 1    ; i++
    jmp     .L5                     ;跳回循环开始 (.L5) (JMP)
.L6:
    mov     eax, DWORD PTR [rbp-8]  ;准备返回结果

优化后的汇编语言

mov     eax, 5050                   ;将计算结果值5050插入eax寄存器中。
ret                                 ;立即返回
  1. 即使进行循环扩展,代码长度也很短。

像代码 1 那样展开循环称为循环展开或循环展开。 如果由于循环扩展而使代码长度变得过长,就浪费了CPU的指令高速缓冲存储器。 如果循环内的代码大小不小,则循环展开不会被优化。

  1. 当循环内部没有复杂的控制流时。

循环内有break、continue、goto或复杂的if-else语句,或者 如果包含外部函数调用,编译器不会不小心扩展循环。

如果有printf等I/O函数, 由于我们必须保护屏幕上显示的副作用,所以我们老老实实地运行JMP并调用函数,不省略循环。

此外,必须打开编译器优化选项才能尝试如上所述的优化。