← 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並調用函數,不省略循環。

此外,必須開啟編譯器最佳化選項才能嘗試如上所述的最佳化。