免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
Linux內(nèi)核中常用的提升性能的小技巧
來自公眾號(hào):開發(fā)內(nèi)功修煉

大家好,我是飛哥!

今天我給大家分享一個(gè)內(nèi)核中常用的提升性能的小技巧。理解了它對你一定大有好處。

在內(nèi)核中很多地方都充斥著 likely、unlikely 這一對兒函數(shù)的使用。隨便揪兩處,比如在 TCP 連接建立的過程中的這兩個(gè)函數(shù)。

//file: net/ipv4/tcp_ipv4.c
int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
{
 if (likely(!do_fastopen)) 
  ...
}
//file: net/ipv4/tcp_input.c
int tcp_rcv_established(struct sock *sk, ...)
{
 if (unlikely(sk->sk_rx_dst == NULL))
  ......
}

在剛開始看源碼的時(shí)候,我也沒弄懂這一對兒函數(shù)的玄機(jī)。后來才搞懂它原來對性能提升是有幫助的。今天我就來和大家聊聊 likely、unlikely 是如何幫助性能提升的。

1. likely 和 unlikely

咱們先來挖挖這對兒函數(shù)的底層實(shí)現(xiàn)。

//file: include/linux/compiler.h
#define likely(x)   __builtin_expect(!!(x),1)
#define unlikely(x) __builtin_expect(!!(x),0)

可以看到其實(shí)它們就是對 __builtin_expect 的一個(gè)封裝而已。__builtin_expect 這個(gè)指令是 gcc 引入的。該函數(shù)作用是允許程序員將最有可能執(zhí)行的分支告訴編譯器,來輔助系統(tǒng)進(jìn)行分支預(yù)測。(參見 https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html)

它的用法為:__builtin_expect(EXP, N)。意思是:EXP == N的概率很大。那么上面 likely 和 unlikely 這兩句的具體含義就是:

  • __builtin_expect(!!(x),1) x 為真的可能性更大
  • __builtin_expect(!!(x),0) x 為假的可能性更大

當(dāng)正確地使用了__builtin_expect后,編譯器在編譯過程中會(huì)根據(jù)程序員的指令,將可能性更大的代碼緊跟著前面的代碼,從而減少指令跳轉(zhuǎn)帶來的性能上的下降。

2. 實(shí)際動(dòng)手驗(yàn)證

那我們實(shí)際動(dòng)手寫兩個(gè)例子,來窺探一下編譯器是如何進(jìn)行優(yōu)化的。為此我寫了一段簡單的測試代碼,如下:

#include <stdio.h>
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x)  __builtin_expect(!!(x), 0)

int main(int argc, char *argv[])
{
 int n;
 n = atoi(argv[1]);

 if (likely(n == 10)){
  n = n + 2;
 } else {
  n = n - 2;
 }
 printf('%d\n', n);
 return 0;
}

這段代碼非常簡單,對用戶的輸入做一個(gè) if else 判斷。在 if 中使用了 likely,也就是假設(shè)這個(gè)條件為真的概率更大。那么我們來看看它編譯后的匯編碼來看看。

圖中上面紅框內(nèi)是對 if 的匯編結(jié)果,可見它使用的是 jne 指令。它的作用是看它的上一句比較結(jié)果,如果不相等則跳轉(zhuǎn)到 4004bc 處,相等的話則繼續(xù)執(zhí)行后面的指令。

在 jne 指令后面緊挨著的是 n = n + 2 對應(yīng)的匯編代碼, 也就是說它把 n = n + 2 這段代碼邏輯放在了緊挨著自己身邊。而把 n = n - 2 的執(zhí)行邏輯放在了離當(dāng)前指令較遠(yuǎn)的 4004bc 處。

我們再把 likey 換成 unlikey 看一下,發(fā)現(xiàn)結(jié)果是正好相反,這次把 n = n - 2 的執(zhí)行邏輯放在前面了,如圖。

注意,編譯時(shí)需要加 -O2 選項(xiàng),使用 objdump -S 來查看匯編指令。為了方便大家使用,我把它寫到 makefile 里了,和測試代碼一起放到了咱們的 Github 上了。大家想動(dòng)手的可以玩玩,地址:https://github.com/yanfeizhang/coder-kung-fu/tree/main/tests/cpu/test01

3. 性能提升原理

那這么做為什么就能夠提升程序運(yùn)行性能呢?原因有兩個(gè)。

首先第一個(gè)原因就是 CPU 高速緩存?,F(xiàn)代的 CPU 一般都有三級(jí)的緩存,用來解決內(nèi)存訪問過慢的問題。訪問數(shù)據(jù)時(shí)優(yōu)先從 L1 讀取,讀取不到再讀 L2、還讀不到就讀 L3,最后用內(nèi)存兜底。

圖中的數(shù)據(jù)來源于飛哥早期的一次測試,參見實(shí)際內(nèi)訪問延時(shí)

這里要注意的是,每一次緩存數(shù)據(jù)的單位都是以一個(gè) CacheLine 64 字節(jié)為單位進(jìn)行存儲(chǔ)的。假如說要查詢的數(shù)據(jù)在 L1 中不存在,那么 CPU 的做法是一次性從 L2 中把要訪問的數(shù)據(jù)及其后面的 64 個(gè)字節(jié)全部緩存進(jìn)來。

假如下一次再執(zhí)行的時(shí)候要訪問的指令在上一次已經(jīng)在 L1 中存在了,那么就直接訪問 L1,就不必再從 L2 來讀取了。回到上面的例子,假如說我們執(zhí)行完了 cmp 對比指令以后,判斷確實(shí)是要進(jìn)加法分支,那緊接著就會(huì)執(zhí)行 jne 后面的 mov xor 等指令大概率就可以從 L1 中取到了。

假如說 cmp 對比執(zhí)行后,發(fā)現(xiàn)是要跳到 4004bc 處的指令執(zhí)行。那大概率該位置處的指令還沒有被加載到緩存中(實(shí)踐中一個(gè)分支下可能會(huì)包含很多代碼,而不是像我這個(gè)例子中簡單的兩三行),就避免不了從 L2 L3 甚至是速度更慢的 L3 去讀取指令。

除了高速緩存這個(gè)原因以外,還有一個(gè)更底層的原理。那就是 CPU 的流水線技術(shù)。CPU 在執(zhí)行程序指令的時(shí)候,并不是先執(zhí)行一個(gè),執(zhí)行完了再運(yùn)行下一個(gè)這樣的。而是把每個(gè)指令都分成了多個(gè)階段,并讓不同指令的各步操作重疊,從而實(shí)現(xiàn)幾條指令并行處理。

還拿上面編譯出來的匯編碼來舉例,程序中 cmp、jne、mov 幾個(gè)指令是挨著的,那么 CPU 在執(zhí)行的時(shí)候?qū)嶋H是大概這樣的。

當(dāng) jne 指令正在執(zhí)行的時(shí)候,后面的兩個(gè) mov 指令都已經(jīng)分別進(jìn)入到譯碼和取址階段了都。假如說分支預(yù)測失敗,那么這工作就白干了。

4. 小結(jié)

總結(jié)一下,今天分享的 likely 和 unlikely 其實(shí)是屬于是輔助 CPU 分支預(yù)測的性能優(yōu)化方法。這就是 likely 和 unlikely 背后的這點(diǎn)小秘密。它是為了讓 CPU 的高速緩存命中率更高,同時(shí)也為了讓 CPU 的流水線更好地工作。

Linux 作為一個(gè)基礎(chǔ)程序,在性能上真的是考慮到了極致。內(nèi)核的作者們內(nèi)功都是非常的深厚,都深諳計(jì)算機(jī)的底層工作原理。為了極致的性能追求精心打磨每一個(gè)細(xì)節(jié),非常值得我們學(xué)習(xí)和借鑒。

不過這里要說的一點(diǎn)是,likely 和 unlikely 的概率判斷務(wù)必準(zhǔn)確。如果寫反了,反而會(huì)起到反作用。

今天分享到此結(jié)束,求贊,求再看!
--- EOF ---
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊舉報(bào)
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
內(nèi)核中的 likely() 與 unlikely()
內(nèi)核中的 likely() 與 unlikely() - 嵌入式Linux-內(nèi)核,底層 -...
linux中 likely與unlikely
linux內(nèi)核之likely()與unlikely()
likely,unlikely宏與GCC內(nèi)建函數(shù)__builtin_expect()
GCC 內(nèi)建函數(shù) __builtin_expect實(shí)現(xiàn)
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服