# 2020q1 Homework2 (fibdrv) contributed by < `ldotrg` > ###### tags: `linux2020` `dutsai` 系統資訊: ```shell #uname -a Linux 5.3.0-42-generic 18.04.1-Ubuntu SMP #lscpu Architecture: x86_64 CPU(s): 6 On-line CPU(s) list: 0-5 Model name: Intel(R) Core(TM) i7-8700 CPU @ 3.20GHz ``` ## 建立用以對照結果的 Fibonacci 數列 運用 Leetcode: [2. Add two number](https://leetcode.com/problems/add-two-numbers/) 的概念。 每個位數單獨處理,換句話說就是用一個 byte 來表示一個十進位數字。 用這最基本的方式實做,來作為效能評比的對照組。 ```cpp static inline void add_BigN(struct BigN *output, struct BigN x, struct BigN y) { u8 carry = 0; for (int ii = 0; ii < MAX_DIGITS; ii++) { u8 tmp = x.val[ii] + y.val[ii] + carry; output->val[ii] = tmp % 10; carry = 0; if (tmp > 9) carry = 1; } } ``` # 利用 eBPF 一行指令測量 `debug_read` 執行時間 [bpftrace](https://github.com/iovisor/bpftrace) 變更很快,可從原始程式碼安裝 - [bpftrace install instructions](https://github.com/iovisor/bpftrace/blob/master/INSTALL.md) - 參考步驟: ```shell $ ./build.sh $ cd build-release $ make install ``` - 共筆: [BCC & bpftrace - 安裝](https://hackmd.io/@0xff07/BJ6vxInlU) 對照看 Brendan Gregg 的解說 [Jump start from Intermediate](http://www.brendangregg.com/blog/2019-01-01/learn-ebpf-tracing.html#Intermediate) - [bpftrace Reference Guide](https://github.com/iovisor/bpftrace/blob/master/docs/reference_guide.md) - [`uprobe`/`uretprobe`: Dynamic Tracing, User-Level](https://github.com/iovisor/bpftrace/blob/master/docs/reference_guide.md#3-uprobeuretprobe-dynamic-tracing-user-level) 一切就緒後,只需要以下一行命令即可輸出 Fibonacci 數 (記得左右移動捲軸): ```shell sudo bpftrace -e 'BEGIN { @start = nsecs; } uprobe:'$PWD'/client:debug_read /@start != 0/ { @start = nsecs; } uretprobe:'WD'/client:debug_read { @cnt++; @stop = nsecs; printf("%d ,%llu\n", (@cnt - 1), (@stop - @start)); } END { clear(@start); clear(@stop); clear(@cnt);}' | tee performance.csv && sed -i 's/Attaching 4 probes...//g' performance.csv ``` 會看到以下輸出`Attaching 4 probes...` 就表示成功了 拆解命令 `bpftrace`: 請參閱 [bpftrace-reference-guide](https://github.com/iovisor/bpftrace/blob/master/docs/reference_guide.md#bpftrace-reference-guide) ```shell #!/usr/bin/env bash echo "Data will be preserved in performance.csv... Press Ctrl+C to stop." sudo bpftrace -e ' BEGIN { @start = nsecs; } uprobe:'$PWD'/client:debug_read /@start != 0/ { @start = nsecs; } uretprobe:'$PWD'/client:debug_read { @cnt++; @stop = nsecs; printf("%d ,%llu\n", (@cnt - 1), (@stop - @start)); } END { clear(@start); clear(@stop); clear(@cnt); }' ``` 同時輸出至檔案與終端畫面 `| tee performance.csv` 透過 `sed` 刪除不必要的資訊 `&& sed -i 's/Attaching 4 probes...//g' performance.csv` > 原始程式碼: [ldotrg/fibdrv](https://github.com/ldotrg/fibdrv) > 腳本檔案 [bpf_scripts.sh](https://github.com/ldotrg/fibdrv/blob/master/bpf_scripts.sh) 最後結果會輸出到 `performance.csv` 檔,再用 gnuplot 作圖即可 >`performance.csv` 第一行為 Fibonacci 數列第 n 項 > 第 2 行為每次 function 被呼叫到返回的時間 畫圖: ```shell $ gnuplot \ -e 'in="performance.csv";out="fibtime.png";gtitle="Fibonacci Sequence Performance"' \ plot.gp ``` 產生 1000 項 ![](https://i.imgur.com/Iw7HNQr.png) 產生 100 項 ![](https://i.imgur.com/6b0fdlI.png) 但這種大數表示方式實在是沒有效率,當計算到 10,000 項以上時就發生 kernel panic。目前只先支援到 1000 項。 :::danger 為何上圖會有顯著的抖動現象呢?請確認 [SMP IRQ affinity](https://www.kernel.org/doc/Documentation/IRQ-affinity.txt) 充分設定,並搭配 `isolcpus`,以排除硬體中斷的干擾 :notes: jserv ::: # 排除干擾效能分析的因素 ### SMP IRQ affinity - 檢查 irq 與 cpu 的統計資訊 `$ cat /proc/interrupts` - 列出所有的 irq `ls /proc/irq/` - 將特定中斷綁定到指定的CPU `sudo sh -c "echo <cpu bitmask> > /proc/irq/<IRQ number>/smp_affinity"` e.g. Bind irq 128 to CPU 0-3 `sudo sh -c "echo f > /proc/irq/128/smp_affinity"` - Applies to all non-active IRQs.Once IRQ is allocated/activated its affinity bitmask will be set to the default mask. `sudo sh -c "echo f > /proc/irq/default_smp_affinity"` ### taskset ```bash sudo taskset 0x10 ./client ``` ### 針對 intel 處理器,關閉 turbo mode `sudo sh -c "echo 1 > /sys/devices/system/cpu/intel_pstate/no_turbo"` 執行時間會增加 ### CPU Isolation ```bash sudo vim /etc/default/grub GRUB_CMDLINE_LINUX_DEFAULT="isolcpus=4,5" sudo update-grub sudo reboot ``` ![](https://i.imgur.com/zqoGbrd.png) # 嘗試移植 [bignum](https://github.com/sysprog21/bignum) - 參照 [4.2 Composite Host Programs](https://elixir.bootlin.com/linux/latest/source/Documentation/kbuild/makefiles.rst#L621) 支援多個原始檔的 kbuild ```shell obj-m := fibdrv.o fibdrv-objs := bignum_k/apm.o \ bignum_k/bignum.o \ bignum_k/format.o \ bignum_k/mul.o \ bignum_k/sqr.o ``` 結果 `insmod fibdrv.ko` 遇到下列問題 ``` [ 111.811807] fibdrv: module license 'unspecified' taints kernel. [ 111.811810] Disabling lock debugging due to kernel taint ``` `obj-m` 定義的是最後核心模組經過 `linker` 連結後的檔名。 所以編譯的時候並不會將`fibdrv.c`加入編譯。 如果我們在 `firdrv-objs` 加入 `fibdrv.o`,還是會遇到相同狀況。 藉由`objdump -tT fibdrv.ko` 發現根本沒有 `fibdrv.c` 裡的符號。 此外,由 `modinfo` 發現,`fibdrv.c` 裡`MODULE_LICENSE` `MODULE_AUTHOR` ...等宣告的字串也沒有出現。 ```shell $ modinfo fibdrv.ko filename: fibdrv/fibdrv.ko srcversion: 23EEA8BE9D31043C12427A0 depends: retpoline: Y name: fibdrv vermagic: 5.3.0-42-generic SMP mod_unload ``` 因此 `fibdrv.c` 其實根本沒有編譯。 嘗試將 `fibdrv.c` 改為 `fibdrv_main.c`。 Makefile 也做以下修改: ```shell obj-m := $(TARGET_MODULE).o $(TARGET_MODULE)-objs := fibdrv_main.o \ bignum_k/apm.o \ bignum_k/bignum.o \ bignum_k/format.o \ bignum_k/mul.o \ bignum_k/sqr.o ``` 就可以恢復正常。 結論:由多個 C 原始檔創造的核心模組,C 原始檔的名字不能和核心模組名字一樣。 ### 需尋找`realloc` 在核心程式碼的替代方案 ```cpp static void *(*orig_malloc)(size_t, gfp_t) = kmalloc; static void *(*orig_realloc)(void *, size_t) = NULL; static void (*orig_free)(const void *) = kfree; ``` :::warning Linux 核心的 slab 介面提供 `krealloc`,注意使用規範 :notes: jserv ::: ### 利用 `procfs` 動態改變程式行為 為了測試方便, 使用一個變數來判斷該使用哪一種 `Fibonacci` 的演算法 這樣就不用一直重新編譯原始碼。 [Creating a proc and intrfacing with user space](https://devarea.com/linux-kernel-development-creating-a-proc-file-and-interfacing-with-user-space/) ```shell $ cat /proc/fibonacci/fib_flag fib_flag = 0 $ echo 2 > /proc/fibonacci/fib_flag $ cat /proc/fibonacci/fib_flag fib_flag = 2 ``` > 原諒我使用如此醜陋寫法,得先趕快解決其它主線問題 ```clike switch (fib_flag) { case 1: result = fast_doubling(*offset); snprintf(kbuf, MAX_DIGITS, "%llu", result); len = copy_to_user(buf, kbuf, MAX_DIGITS); break; case 2: bignum_k_fast_doubling(buf, size, *offset); break; default: fib_sequence(buf, size, *offset); len = size; } ``` > TODO: sysfs 利用 kobject 包裝核心內部的資料結構並揭露給使用者。 > [問題影片解說連結](https://youtu.be/Fo-3MtrXr3E?t=7053) > - [檔案系統概念及實作手法](https://beta.hackfoldr.org/linux/https%253A%252F%252Fhackmd.io%252Fs%252FBypqEyF6N) > - [sysfs.txt](https://github.com/torvalds/linux/blob/c309b6f24222246c18a8b65d3950e6e755440865/Documentation/translations/zh_CN/filesystems/sysfs.txt) > - [sysfs example](https://hackmd.io/@colinyoyo26/2020q1fibdrv#%E5%BE%9E-sysfs-%E7%95%8C%E9%9D%A2%E8%AE%80%E5%8F%96%E5%9F%B7%E8%A1%8C%E6%99%82%E9%96%93) ### 將輸出資料的結果導入 user spcace 的記憶體 bignum fast doubling 初版完成:[b721239](https://github.com/ldotrg/fibdrv/commit/b721239cca3c508ada83d77ba17149d5b0b567d1) > 不知道有什麼好東西可以取代 `FILE` 的角色 ``` void bn_fprint(const bn *n, unsigned int base, FILE *fp) ``` # 效能分析三部曲 ## 效能 - 取樣 ![](https://i.imgur.com/42cx4hc.png) - bignum 有抖動的現象 值得注意 ![](https://i.imgur.com/8elX5Ef.png) ![](https://i.imgur.com/Z5LOtrS.png) :::danger 在 Ubuntu Linux 18.04-4 搭配 Linux node1 4.15.0-91-generic 編譯失敗: ``` In file included from /tmp/fibdrv/bignum_k/format.c:1:0: ./arch/x86/include/asm/fpu/api.h:28:8: error: unknown type name ‘bool’ extern bool irq_fpu_usable(void); ^~~~ ./arch/x86/include/asm/fpu/api.h:37:30: error: unknown type name ‘u64’ extern int cpu_has_xfeatures(u64 xfeatures_mask, const char **feature_name); ^~~ ``` :notes: jserv ::: > Fix at commit [6e95b9](https://github.com/ldotrg/fibdrv/commit/6e95b90c4ceea9bfa4ff05d0e327cf0cb82629a9) > 當時為了修理有浮點運算的編譯錯誤,嘗試了兩種方法,最後選擇第二種,忘記移除第一個所使用的標頭檔。 ```shell bignum_k/format.c:143:67: error: SSE register return with SSE disabled return (size_t)(radix_sizes[radix] * (size * APM_DIGIT_SIZE)) + 2; ``` > 1. 程式裡必須使用浮點運算的區塊,需由`kernel_fpu_begin(void)` `kernel_fpu_end(void)`做保護, 需要`#include <asm/fpu/api.h>` => 但似乎還有其他問題 > 2. 直接在compile flag 裡添加 `-msse2` ## 效能 - 分析 ## 效能 - 改善 ## 參考資訊 - [2020q1 quiz2](https://hackmd.io/@sysprog/linux2020-quiz2#2020q1-%E7%AC%AC-2-%E9%80%B1%E6%B8%AC%E9%A9%97%E9%A1%8C) - [folly string](https://github.com/facebook/folly/blob/master/folly/FBString.h#L208) - [自我檢查清單](https://hackmd.io/@sysprog/linux2020-fibdrv#%E8%87%AA%E6%88%91%E6%AA%A2%E6%9F%A5%E6%B8%85%E5%96%AE) - [作業要求](https://hackmd.io/@sysprog/linux2020-fibdrv#-%E4%BD%9C%E6%A5%AD%E8%A6%81%E6%B1%82)