jump label機制進入Linux內核已經很多很多年了,它的目的是 消除分支。 為了達到這個目的,jump label的手段是 修改分支處的代碼。
~把代碼當做數據,代碼和數據在馮諾伊曼計算機中得到了統一~
本質上,jump label作用于下面的邏輯:
靜態拆分成了下面的兩個邏輯,其一是:
或者,其二是:
但二者不能同時共存。顯然,這破壞了通用性和靈活性,帶來了高效!
這相當于一個硬熔斷,具體詳情參見:
本文來一點可以看得見的東西,演示一下真實的jump label & static key。
先看下面的C代碼:
很簡單的代碼,也很正確。然而, 如果main函數是一個高頻調用的函數,并且在E1,E2是不隨著代碼邏輯而發生變化,僅僅參數設定的情況下, 那么if語句盡量消除以消除不必要的分支預測,而這正是jump label的用武之地!
我們下面用jump label機制來重寫上面的代碼,請看:
定義JUMP_LABEL宏編譯之,看看效果:
如何做到的呢?static_branch_true內聯函數是如何判斷true or false的呢?
事實上,jump label邏輯修改了代碼段,取消了條件判斷!這一切都是在update_branch中發生的。我們看下update_branch調用之前,main函數的匯編碼:
在執行了update_branch之后,main函數發生了變化:
看樣子就是這么回事!
之所以這件事可以發生得如此簡單,多虧了一個新的section,即__jump_table,我們通過objdump看看__jump_table的內容:
通過jump_label_demo.c的struct entry結構體,我們直到這個section中包含了多個3元組,包含3個字段:
需要修改的代碼地址。
需要jmp到的代碼地址。
匹配健。
我們看67064000 00000000按照小端就是0x400667,它就是需要修改的代碼地址,而6e064000 00000000按照小端則是0x40066e:
看來,這個__jump_table的item會將jmpq 40066c修改為jmpq 40066e,從而實現了 永久靜態分支。
最后,__jump_table的內容就是在每一個內聯的static_branch_true函數中被填充的,該參數的參數是一個key,它指示了branch entry三元組中的最后一個字段。
static_branch_true函數的內聯非常重要,它實現了將branch entry三元組數據直接插入到__jump_table section,而不是共享同一個函數體。
總之,如果你看代碼還是覺得別扭,手敲一遍我上面的示例程序,就理解了,內核里面的也就這么回事,總結一句話:
依靠運行時修改代碼而不是依靠狀態數據來控制執行流。
我不知道這對于所謂的 通用計算機程序設計 是不是反其道而行之,但在效果上,它確實是一匹好馬。不禁感嘆, 硬編碼讀起來是丑陋的,但執行起來卻是高效的!
靈活性換高效率,得不償失,我是這樣以為。jump label的本質在于, 將同時刻存在的一套代碼沿著時間線在可預期的固定時間點上分割成邏輯相反的兩套代碼。
硬件性能的提升將會證明jump label就是個笑話。
說兩句好話,Linux內核參數,sysctl變量基本上就可以通過jump label來運作,從而替代if判斷。
原文鏈接:https://blog.csdn.net/dog250/article/details/106715700
編輯:lyn
-
數據
+關注
關注
8文章
7002瀏覽量
88941 -
邏輯
+關注
關注
2文章
833瀏覽量
29464 -
代碼
+關注
關注
30文章
4779瀏覽量
68521 -
LINUX內核
+關注
關注
1文章
316瀏覽量
21644
原文標題:Linux內核jump label與static key的原理與示例
文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論