網站首頁 編程語言 正文
什么是likely和unlikely
既然程序是我們程序員所寫,在一些明確的場景下,我們應該比CPU和編譯器更了解哪個分支條件更有可能被滿足。我們是否可將這一先驗知識告知編譯器和CPU, 提高分支預測的準確率,從而減少CPU流水線分支預測錯誤帶來的性能損失呢?答案是可以!它便是likely和unlikely。在Linux內核代碼中,這兩個宏的應用比比皆是。下面是他們的定義:
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
likely,用于修飾if/else if分支,表示該分支的條件更有可能被滿足。而unlikely與之相反
以下為示例。unlikely修飾argc > 0分支,表示該分支不太可能被滿足。
#include <cstdio>
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
int main(int argc, char *argv[])
{
if (unlikely(argc > 0)) {
puts ("Positive\n");
} else
{
puts ("Zero or Negative\n");
}
return 0;
}
likely/unlikely的原理
接下來,我們從匯編指令分析likely/unlikely到底是如何起作用的?
首先我們將上述代碼中的unlikely去掉,然后反匯編,作為對照組
匯編如下,我們看到,if分支中的指令被編譯器放置于分支跳轉指令jle相鄰的位置,即CPU流水線在遇到jle指令所代表的的'岔路口'時,更傾向于走if分支
.LC0:
.string "Positive\n"
.LC1:
.string "Zero or Negative\n"
main:
sub rsp, 8
test edi, edi
jle .L2 ; 如果argc <= 0, 跳轉到L2
mov edi, OFFSET FLAT:.LC0 ; 如果argc > 0, 從這里執行
call puts
.L3:
xor eax, eax
add rsp, 8
ret
.L2:
mov edi, OFFSET FLAT:.LC1
call puts
jmp .L3
接著我們在if分支中加上unlikely, 反匯編如下。這里的情況正好與對照組相反,if分支下的指令被編譯器放置于遠離跳轉指令jg的位置。這意味著CPU此時更傾向于走else分支。
.LC0:
.string "Positive\n"
.LC1:
.string "Zero or Negative\n"
main:
sub rsp, 8
test edi, edi
jg .L6
mov edi, OFFSET FLAT:.LC1
call puts
.L3:
xor eax, eax
add rsp, 8
ret
.L6:
mov edi, OFFSET FLAT:.LC0
call puts
jmp .L3
因此,通過對分支條件使用likely和unlikely,我們可給編譯器一種暗示,即該分支條件被滿足的概率比較大或比較小。而編譯器利用這一信息優化其機器指令,從而最大限度減少CPU分支預測失敗帶來的懲罰。
likely/unlikely的適用條件
CPU有自帶的分支預測器,在大多數場景下效果不錯。因此在分支發生概率嚴重傾斜、追求極致性能的場景下,使用likely/unlikely才具有較大意義。
C++20中的likely/unlikely
C++20之前的,likely和unlikely只不過是一對自定義的宏。而C++20中正式將likely和unlikely確定為屬性關鍵字。
int foo(int i) {
switch(i) {
case 1: handle1();
break;
[[likely]] case 2: handle2();
break;
}
}
原文鏈接:https://www.cnblogs.com/lygin/p/16793828.html
相關推薦
- 2022-07-16 Spring Boot之Dao、Service、Controller通過注解委托給Spring容器
- 2023-01-08 Python?SQLAlchemy建立模型基礎關系模式過程詳解_python
- 2022-06-22 git用戶自定義變量查看修改及調用教程詳解_其它綜合
- 2022-06-11 Go1.18?新特性之多模塊Multi-Module工作區模式_Golang
- 2022-04-20 C語言數據結構與算法時間空間復雜度基礎實踐_C 語言
- 2023-01-19 GO的基礎知識掃盲注意事項_Golang
- 2023-04-20 npm ERR! 400 Bad Request - PUT xxx - Cannot publis
- 2022-09-08 redis?lua限流算法實現示例_Redis
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支