Linux 核心設計 2022
contributed by < DokiDokiPB >
附註: intel 第 12 代 CPU 可以關閉 E Core
在 Calculating Fibonacci Numbers by Fast Doubling 網誌中,提及 fast doubling 手法
例如 的計算過程,以 top-down 方法去觀察
使用第一第二計算方法是取決當下 的 n 是否為偶數
使用 bottom-up 手法去觀察其對應關係
乘 2 即左移一項 k << 1,因此可以理解為從 F(0) 開始,經過以下 4 個步驟可以得到 F(10),這也是為何 fast doubling 實作上會根據 bit 來決定計算的步驟
(0000 << 1) + 1 = 0001
(0001 << 1) + 0 = 0010
(0010 << 1) + 1 = 0101
(0101 << 1) + 0 = 1010
i | start | 4 | 3 | 2 | 1 | result |
---|---|---|---|---|---|---|
n | - | 1010 | 1010 | 1010 | 1010 | - |
F(m) | F(0) | F(0*2+1) | F(1*2) | F(2*2+1) | F(5*2) | F(10) |
a | 0 | 1 | 1 | 5 | 55 | 55 |
b | 1 | 1 | 2 | 8 | 89 | - |
引用 Calculating Fibonacci Numbers by Fast Doubling 文章底部的實作
使用 __builtin_clz
的改進實作
其中 unsigned long long mask = ULLONG_MAX ^ (ULLONG_MAX >> 1);
為將最高位元設為 1
使用 bitwise 的技巧,再改寫成以下程式碼
考量上述的 fast doubling 手法,分為是否使用 bitwise 技巧的實作。在 user space 測試是否有差異。
新增 fibseq.h, fibseq.c
,用於實作上述使用 branch 與 branchless 的程式碼、新增 uclient.c
,實作如下:
在 MakeFile 中新增 make uclient
指令
新增 gnuplot 檔案 scripts/uclient_plot.gp
將以上流程寫成寫入 MakeFile 中,新增 make ucheck
:
最終產生以下圖片,可見在 userspace 中使用 bitwise 技巧比原本的實作多耗時
透過 gcc -S fibseq.c
輸出 x86_64 的組合語言程式碼,可參考線上網站 Godbolt Complier Explorer
fibseq_basic_fast_doubling_branchless
fibseq_basic_fast_doubling_branch
目前初步推論使用 bitwise 技巧產生較多的組合語言程式碼跟更多的計算,導致效能比單純使用 branch 指令來得費時。
以下程式碼實作在分支 decnum,為第二個版本。
第一版本 744e95c3a987a3b5822cd887234d9a667c5d9591
第二版本 decnum head 將malloc
從decnum_t
函式移除,改進記憶體使用方式
為了測試程式碼,先實作於 user space。
decnum.c decnum.h
新增兩個檔案,用於實作大數 struct decnum
結構體
dclient.c
隨著大數 decnum
相關的加法、減法、乘法實作,在每次 git commit 中,皆有 dclient.c
為對應的程式碼測試。
Makefile
內容新增 dclient
用於測試 decnum
程式碼內容
fibseq.h fibseq.c
在 fibseq.h fibseq.c
中新增 void decnum_fast_doubling
函式
decnum.c decnum.h
程式碼內容在原本的作業敘述中, 之後的數字會因為 overflow 導致輸出錯誤,採用 struct decnum
結構體表示一個數字。
size
:紀錄大數的最高位有效位數(非零位數)cap
: 實際使用 malloc
的記憶體量digits
: 表示一個數字。使用 int32_t
紀錄數字,使用 進位模式,因此每個位數最大能儲存 ,最小為 例如以下程式碼,若以 decnum
要表示 ,digits[0]
表示最低位數字,而 digits[1]
表示第二位數字
參考 Fast Doubling 方法
要實作的函式功能為:加法、減法、乘法、平方
以下的實作,程式碼主要分為兩個部份
size
與 cap
因為加法的特性,可以將運算完成的數字直接指派給原本的變數,例如 decnum_add(a,b,a)
將結果重新指派回 a
,就不需要新的空間指派。
處理兩數加法運算,將重疊的位數相加。再透過變數 carry
處理剩餘的位數
在 fast doubling 的計算過程中, 用到減法運算,並且 ,因此實作中只有考慮 decnum_t b1
大於 decnum_t b2
下的減法運算
同樣因為減法的特性,可以將運算完成的數字直接指派給原本的變數,例如 decnum_add(a,b,a)
將結果重新指派回 a
,就不需要新的空間指派。
在第 21 ~ 26 行中,因為乘法的特性,會向更高位數傳遞 carry
,因此不能重新指派數值給原有的變數。
提供大數一個乘以 2 的實作
神秘的數字 42.25
透過觀察 在 decnum_t
實作,將輸出的 decnum_t size
除以 會得出一組穩定的數字 ,例如 取以下範圍
int32_t
int32_t
int32_t
, 950 / 23 = 41.304、992 / 23 = 43.130int32_t
, 34842 / 810 = 43.066、1035 / 24 = 43.148選出一個 magic number:,將 作為每次使用 malloc
的大小
第 52 ~ 77 行中,只能使用 a
, b
, tmp
, tmp2
四個變數去取得計算結果,並且盡量不使用乘法實作。
使用 make dclient
確認輸出
交叉比對後,列出到 皆為正確的數字。
以下程式碼實作於
kdecnum
分支
kdecnum.h kdecnum.c
檔案decnum_t
加上前綴 k
,改名成 kdecnum_t
void decnum_new(decnum_t *ptr)
改成 void kdecnum_new(kdecnum_t *ptr)
malloc
改為 kmalloc
, free
改為 kfree
, realloc
改為 krealloc
Makefile
檔案make
指令時發生錯誤eclient.c
測試 fibonacci driverfibdrv.c
fibseq.c
的 decnum_fib_fast_doubling
函式移植到 fibdrv.c
中fibdrv.c
中 ssize_t fib_read
,將 b1.digits
大數數值指派給 buf
#define MAX_LENGTH 92
改為 #define MAX_LENGTH 150
參考 K04: fibdrv
核心驅動裝置不能直接寫入該硬體,需透過copy_to_user
方能將數值內容複製,使得 client 端接收。
剛開始撰寫程式碼時,我使用以下程式碼,導致 Ubuntu 系統螢幕顯示卡頓,即使是只有一行也是卡頓。意外的是系統沒有崩潰。
eclient.c
在實作上幾乎與原本的 client.c
相同
以下實作於
timemeasure
分支
以下實作的版本為
timemeasure 60268ca7215f84a3f0a973d05f7fda03bab3f2f5
將 fibdrv.c
的程式碼的回傳改為計算 decnum_fib_fast_doubling
需要的時間,單位為 nano second
在尚未移除影響效能的因素下,多次使用指令 make eclient
計算 ,可得到以下輸出
cpu19 為 Intel effective core,用途為省電,使用 Performance core 在執行時間上較短
以下實作為timemeasure 719aa1ef02dc949dbd4c65979a2698ae726acdc9
在 Makefile
中新增測試流程
在多次執行 make eall
後,無論是否使用第四行或是第五行程式碼所繪製的圖片,皆有週期性的跳動
或是遠高於平均、低於平均的數值
參考 2022 年 Linux 核心設計/實作課程作業 fibdrv Linux 效能分析的提示,保留特定的 CPU
參考 grub2 - How do I add a kernel boot parameter? - Ask Ubuntu
參考 What is the difference between GRUB_CMDLINE_LINUX and GRUB_CMDLINE_LINUX_DEFAULT in /etc/default/grub
在 /etc/default/grub
檔案中加入 GRUB_CMDLINE_LINUX_DEFAULT="quiet splash isolcpus=19"
,並且 sudo update-grub
並修改 Makefile
使用 make eall
後,產生以下圖片
好像什麼沒變化
在這次作業中,我使用 @KYWeng 的筆記 作為對照組去檢驗我的實作。忽略第 0 ~ 5 次的 計算,並顯示於以下圖片,此實驗獲得平穩的結果,表示確實移除影響效能分析的因素。
使用 trace-cmd
追蹤我的實作:使用 sudo trace-cmd record --max-graph-depth 1 -e all taskset -c 19 ./eclient > ./eclient_time
,並將結果輸出為下圖。執行時間驚人的增加 7 倍。
使用 trace-cmd report
輸出的報告如下
然而同樣的使用 ftrace 追蹤 @KYWeng 的實作,卻是沒有變化(下圖)
在觀察 trace-cmd report
輸出的報告後,我注意到我的實作在每次加法、減法、乘法、都會使用 kmalloc
並且很快就 kfree
,因 ftrace 追蹤行為導致在執行時間大幅增加。
對比於 @KYWeng 的實作輸出的報告,出現 kmalloc
, kfree
的頻率不多,參照其程式碼的實作中,使用 krealloc
而不是多次使用 kmalloc
這一次的修改包含
改進之後產生的輸出圖片如下:
成為穩定執行時間成本的線段 38000 ns 下降至 11500 ns
參考 @KYWeng perf 說明
取得以下結果(省略部份輸出),可以得出實作中乘法與 memset_erms
指令絕大多數執行時間
使用 sudo perf stat -r 10 -e cycles,instructions,cache-misses,cache-references,branch-instructions,branch-misses ./eclient
取得以下輸出, cpu_core/cache-misses/
佔去 59.19%