跳到內容

CPU分支預測

執行根據判斷跳轉到不同區塊的語句時,cpu會先預測應該會走哪一條分支來提升指令管線的效能,現代cpu採用動態預測的方式,根據先前分支跳轉的歷史來預測,如果猜錯的話會產生一些pipeline flush的性能成本,猜對且經常正確的話可以提升效能,寫程式時基於效能考量有兩種情況,使用分支和使用無分支(避免分支),以下將說明詳細內容。

bigger:
mov rax, rdi ; 假設rdi大
cmp rdi, rsi
jl less
ret
less:
mov rax, rsi
ret

這是比較 rdirsi 哪一個比較大的例子,默認 rdi 大如果小則改成 rsi,cpu在分支沒有歷史首次執行的時候靜態分支預測如下

名稱 說明 例子 行為
向前分支(forward branches) 目標地址更大的分支,程式碼一般在更下面 if 預測不跳
向後分支(backward branches) 目標地址更小的分支,程式碼一般在更上面 loop 預測跳躍

所以這種 jl 到更下方的 less 做法默認不會跳轉,當rdi經常比rsi大的時候這樣寫
高階語言loop一般會被編譯成向後分支,if一般會被編譯成向前分支

只有依執行時內容決定的跳躍敘述才會需要預測接下來往哪裡執行,像是cmovcc指令和setcc(根據旗標0或1設定到暫存器最低位元)就不會有分支跳躍敘述,高階語言常見的像是根據內容簡單賦值的三元運算子 (condition)? true:false,但如果true或false的選項是複雜的比如一個函數那麼還是會被編譯為分支跳躍才能實現。

除了這些用指令可替換的方式還有一些進階技巧請參考這篇文章

如果一個判斷跳躍經常是可以被cpu預測對的,使用分支預測效能高於無分支,如果一個內容隨機性高經常進入不同分支的話使用無分支寫法更佳。

if(error) {
return msg;
}
/* 繼續執行 */

像是一般不太發生那樣的情況寫成if跳轉,同時符合上面的cpu靜態分支預測方式,而且後續不常發生在動態預測一般是正確的。