--- tags: 2022 linux kernel --- # 2022q1 Homework3 (fibdrv) contributed by < [`Risheng1128`](https://github.com/Risheng1128) > > [作業要求](https://hackmd.io/@sysprog/linux2022-fibdrv) > [作業區](https://hackmd.io/@sysprog/linux2022-homework3) ## 實驗環境 ```shell $ gcc --version gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0 $ lscpu Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian Address sizes: 39 bits physical, 48 bits virtual CPU(s): 4 On-line CPU(s) list: 0-3 Thread(s) per core: 2 Core(s) per socket: 2 Socket(s): 1 NUMA node(s): 1 Vendor ID: GenuineIntel CPU family: 6 Model: 142 Model name: Intel(R) Core(TM) i5-7200U CPU @ 2.50GHz Stepping: 9 CPU MHz: 1439.885 CPU max MHz: 3100.0000 CPU min MHz: 400.0000 BogoMIPS: 5399.81 Virtualization: VT-x L1d cache: 64 KiB L1i cache: 64 KiB L2 cache: 512 KiB L3 cache: 3 MiB NUMA node0 CPU(s): 0-3 ``` ## 研讀自我檢查清單 :::info 作業要求 - [x] 研讀上述 ==Linux 效能分析的提示== 描述,在自己的實體電腦運作 GNU/Linux,做好必要的設定和準備工作 $\to$ 從中也該理解為何不希望在虛擬機器中進行實驗; - [ ] 研讀上述費氏數列相關材料 (包含論文),摘錄關鍵手法,並思考 [clz / ctz](https://en.wikipedia.org/wiki/Find_first_set) 一類的指令對 Fibonacci 數運算的幫助。請列出關鍵程式碼並解說 - [ ] 複習 C 語言 [數值系統](https://hackmd.io/@sysprog/c-numerics) 和 [bitwise operation](https://hackmd.io/@sysprog/c-bitwise),思考 Fibonacci 數快速計算演算法的實作中如何減少乘法運算的成本; - [ ] 研讀 [KYG-yaya573142 的報告](https://hackmd.io/@KYWeng/rkGdultSU),指出針對大數運算,有哪些加速運算和縮減記憶體操作成本的舉措? - [ ] `lsmod` 的輸出結果有一欄名為 `Used by`,這是 "each module's use count and a list of referring modules",但如何實作出來呢?模組間的相依性和實際使用次數 ([reference counting](https://en.wikipedia.org/wiki/Reference_counting)) 在 Linux 核心如何追蹤呢? > 搭配閱讀 [The Linux driver implementer’s API guide » Driver Basics](https://www.kernel.org/doc/html/latest/driver-api/basics.html) - [ ] 注意到 `fibdrv.c` 存在著 `DEFINE_MUTEX`, `mutex_trylock`, `mutex_init`, `mutex_unlock`, `mutex_destroy` 等字樣,什麼場景中會需要呢?撰寫多執行緒的 userspace 程式來測試,觀察 Linux 核心模組若沒用到 mutex,到底會發生什麼問題。嘗試撰寫使用 [POSIX Thread](https://en.wikipedia.org/wiki/POSIX_Threads) 的程式碼來確認。 ::: ### 研讀 Linux 效能分析的提示 看完了==Linux 效能分析的提示==後,了解為何要將要執行的 process 綁定在特定 CPU 上,以下為規劃的實作步驟 :::warning 1. 限定 CPU 給特定的程式使用,為了不讓其他的 process 干擾我要執行的程式 2. 排除干擾效能分析的因素 - 抑制 [Address space layout randomization (ASLR)](https://en.wikipedia.org/wiki/Address_space_layout_randomization) - 設定 scaling_governor 為 performance - 針對 Intel 處理器,關閉 turbo mode ::: 首先進行 CPU Core 保留的動作,在 boot loader 上新增參數 `isolcpus=cpu_id` ,這邊選擇 core `0` 作為測試,因此新增: > `isolcpus=0` 進入目錄 `/etc/default` ,並且輸入命令 `sudo vim grub` 開啟檔案 `grub` ,並修改以下指令 > `GRUB_CMDLINE_LINUX="isolcpus=0"` 接著輸入命令 `sudo update-grub` 更新 `/boot/grub/grub.cfg` ,以下為結果 ```shell $ sudo update-grub Sourcing file `/etc/default/grub' Sourcing file `/etc/default/grub.d/init-select.cfg' Generating grub configuration file ... Found linux image: /boot/vmlinuz-5.13.0-28-generic Found initrd image: /boot/initrd.img-5.13.0-28-generic Found linux image: /boot/vmlinuz-5.11.0-27-generic Found initrd image: /boot/initrd.img-5.11.0-27-generic Found Windows Boot Manager on /dev/sdb1@/EFI/Microsoft/Boot/bootmgfw.efi Adding boot menu entry for UEFI Firmware Settings done ``` 重開機後,輸入命令 `taskset -p 1` ,可以看到 core `0` 已經被保留 ```shell $ taskset -p 1 pid 1's current affinity mask: e ``` 也可以從 System Monitor 觀察,如以下所示: ![](https://i.imgur.com/8uO9rjT.png) 接著抑制 ASLR > sudo sh -c "echo 0 > /proc/sys/kernel/randomize_va_space" 設定 scaling_governor 為 performance ,建立 performance.sh ``` for i in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor do echo performance > ${i} done ``` 接著輸入命令 `sudo sh performance.sh` 執行 `performance.sh` 最後針對 Intel 處理器,關閉 turbo mode > sudo sh -c "echo 1 > /sys/devices/system/cpu/intel_pstate/no_turbo" :::warning 回答問題: 為何不希望在虛擬機器中進行實驗 參考[Linux 核心設計: 不只挑選任務的排程器](https://hackmd.io/@sysprog/linux-scheduler)裡的連結 [Hypervisor](https://en.wikipedia.org/wiki/Hypervisor) ,裡頭提到 hypervisor 的種類 (native or bare-metal hypervisors 及 hosted hypervisors) ,如下圖所示 > ![](https://i.imgur.com/LK1ZvZx.png) > 參考 [Hypervisor](https://en.wikipedia.org/wiki/Hypervisor) 如果使用虛擬機器進行實驗的話,電腦架構會類似上圖的 Tpye `2` ,當執行程式時,會經過虛擬機器及原本的作業系統,因此效能會比較差 ::: ### 研讀上述費氏數列相關材料 這邊將論文的分析放在額外開的筆記: [研讀 Algorithms for Computing Fibonacci Numbers Quickly](https://hackmd.io/@Risheng/rkdoM1YWq) :::warning ToDo: 補上 Fast doubling ,以及 clz/ctz 指令的幫助 ::: ## 撰寫 Linux 核心模組 :::info 作業要求 - [x] 原本的程式碼只能列出到 Fibonacci(100) 而且部分輸出是錯誤的數值,請修改程式碼,列出後方更多數值 (注意: 檢查正確性和數值系統的使用) - [x] 務必研讀上述 Linux 效能分析的提示 的描述,降低效能分析過程中的干擾; - [x] 在 Linux 核心模組中,可用 ktime 系列的 API; - [x] 在 userspace 可用 [clock_gettime](https://linux.die.net/man/2/clock_gettime) 相關 API; - [x] 善用統計模型,除去極端數值,過程中應詳述你的手法 - [x] 分別用 [gnuplot](https://hackmd.io/@sysprog/Skwp-alOg) 製圖,分析 Fibonacci 數列在核心計算和傳遞到 userspace 的時間開銷,單位需要用 us 或 ns (自行斟酌) - [ ] 嘗試解讀上述實驗的時間分佈,特別是隨著 Fibonacci 數列增長後,對於 Linux 核心的影響。可修改 VFS 函式,允許透過寫入 /dev/fibonacci 裝置檔案來變更不同的 Fibonacci 求值的實作程式碼。 ::: ### 前期準備 這邊跟著[作業說明](https://hackmd.io/@sysprog/linux2022-fibdrv#-%E6%92%B0%E5%AF%AB-Linux-%E6%A0%B8%E5%BF%83%E6%A8%A1%E7%B5%84),一步一步設定 首先,將 UEFI Secure Boot 的功能關閉: ==已關閉== 檢查 Linux 核心版本,輸入命令 `uname -r` ```shell $ uname -r 5.13.0-28-generic ``` 安裝 `linux-headers` 套件,輸入命令 `sudo apt install linux-headers-‵uname -r‵` 接著確認 `linux-headers` 套件已經正確安裝在開發環境,輸入命令 `dpkg -L linux-headers-5.13.0-28-generic | grep "/lib/modules"` ,可以得知符合預期 ```shell $ dpkg -L linux-headers-5.13.0-28-generic | grep "/lib/modules" /lib/modules /lib/modules/5.13.0-28-generic /lib/modules/5.13.0-28-generic/build ``` 檢驗目前使用者身份,輸入命令 `whoami` ,可以得知符合預期 (非 `root`) ```shell $ whoami benson ``` 一起檢查命令 `sudo whoami` ,可以得知符合預期 ```shell $ sudo whoami root ``` 繼續安裝後續會用到的工具 ```shell $ sudo apt install util-linux strace gnuplot-nox ``` 取得原始程式碼 ```shell $ git clone https://github.com/Risheng1128/fibdrv.git $ cd fibdrv ``` 編譯並測試,輸入命令 `make check` ,根據作業說明可以得知以下結果符合預期 ```shell Passed [-] f(93) fail input: 7540113804746346429 expected: 12200160415121876738 ``` 觀察產生的 `fibdrv.ko` 核心模組,輸入命令 `modinfo fibdrv.ko` ```shell $ modinfo fibdrv.ko filename: /home/benson/fibdrv/fibdrv.ko version: 0.1 description: Fibonacci engine driver author: National Cheng Kung University, Taiwan license: Dual MIT/GPL srcversion: 9A01E3671A116ADA9F2BB0A depends: retpoline: Y name: fibdrv vermagic: 5.13.0-28-generic SMP mod_unload modversions ``` 接著觀察 `fibdrv.ko` 核心模組在 Linux 核心掛載後的行為,需要先輸入命令 `make load` 掛載核心模組 ```shell $ ls -l /dev/fibonacci crw------- 1 root root 507, 0 三 15 21:55 /dev/fibonacci $ cat /sys/class/fibonacci/fibonacci/dev 507:0 $ cat /sys/module/fibdrv/version 0.1 $ lsmod | grep fibdrv fibdrv 16384 0 $ cat /sys/module/fibdrv/refcnt 0 ``` :::warning ToDo: 理解命令 `cat /sys/class/fibonacci/fibonacci/dev` 回傳 `507` 的意義 ::: ### 計算 F~93~ (包含) 之後的 Fibonacci 數 為了能夠計算 Big number 以及有效管理程式,這邊新增兩個檔案 `bignumber.c` `bignumber.h` 開始修改 `Makefile` ```diff CONFIG_MODULE_SIG = n TARGET_MODULE := fibdrv obj-m := $(TARGET_MODULE).o ccflags-y := -std=gnu99 -Wno-declaration-after-statement + $(TARGET_MODULE)-objs := bignumber.o ``` 輸入命令 `Make` 試試,出現了奇怪的錯誤 ```shell ERROR: modpost: missing MODULE_LICENSE() in /home/benson/fibdrv/fibdrv.o make[2]: *** [scripts/Makefile.modpost:150: /home/benson/fibdrv/Module.symvers] Error 1 make[2]: *** Deleting file '/home/benson/fibdrv/Module.symvers' make[1]: *** [Makefile:1794: modules] Error 2 make[1]: Leaving directory '/usr/src/linux-headers-5.13.0-28-generic' make: *** [Makefile:14: all] Error 2 ``` 後來發現是編譯的檔案重複了,後來參考 [Linux Kernel driver modpost missing MODULE_LICENSE](https://stackoverflow.com/questions/56662176/linux-kernel-driver-modpost-missing-module-license),把原本的檔名從 `fibdrv` 改成 `fibdrv_core` ,並且再次修改 Makefile ```diff -$(TARGET_MODULE)-objs := bignumber.o +$(TARGET_MODULE)-objs := fibdrv_core.o bignumber.o ``` 再次輸入命令 `make` ,成功! ```shell $ make make -C /lib/modules/5.13.0-28-generic/build M=/home/benson/fibdrv modules make[1]: Entering directory '/usr/src/linux-headers-5.13.0-28-generic' LD [M] /home/benson/fibdrv/fibdrv.o MODPOST /home/benson/fibdrv/Module.symvers CC [M] /home/benson/fibdrv/fibdrv.mod.o LD [M] /home/benson/fibdrv/fibdrv.ko BTF [M] /home/benson/fibdrv/fibdrv.ko Skipping BTF generation for /home/benson/fibdrv/fibdrv.ko due to unavailability of vmlinux make[1]: Leaving directory '/usr/src/linux-headers-5.13.0-28-generic' ``` 接著可以開始實作程式碼,這邊是選擇使用字串將數字儲存,以下為 `bignumber.c` 實作結果,參考 [415. Add Strings](https://leetcode.com/problems/add-strings/) ,主要功能為將兩個字串相加 :::spoiler bignumber.c ```c /** * @file bignumber.c * @brief 實作費氏數列 Big number 計算 */ #include "bignumber.h" /** * @fn - str_size * @brief - 回傳字傳長度 */ long int str_size(char *str) { int size = 0; if (!str) return 0; while (*str++) size++; return size; } /** * @fn - char_swap * @brief - 字元兩兩交換 */ static void char_swap(char *char1, char *char2) { *char1 ^= *char2; *char2 ^= *char1; *char1 ^= *char2; } /** * @fn - str_reverse * @brief - 翻轉字串 */ static void str_reverse(char *str, int size) { int head = 0, tail = size - 1; while ((head != tail) && (tail > head)) { char_swap(str + head, str + tail); head++; tail--; } } /** * @fn - str_cpy * @brief - 將字串 src 複製到字串 dst */ void str_cpy(char *dst, char *src, int size) { for (int i = 0; i < size; i++) *(dst + i) = *(src + i); *(dst + size) = '\0'; } /** * @fn - addString * @brief - 將兩個字串相加,並儲存到 kbuf 裡 */ void addString(char *kmin_str, char *kmax_str, char *kbuf) { char min_str[BUF_SIZE], max_str[BUF_SIZE]; long int min_size = str_size(kmin_str); long int max_size = str_size(kmax_str); str_cpy(min_str, kmin_str, min_size); str_cpy(max_str, kmax_str, max_size); str_reverse(min_str, min_size); str_reverse(max_str, max_size); int carry = 0, sum; long int index; for (index = 0; index < min_size; index++) { sum = max_str[index] - '0' + min_str[index] - '0' + carry; kbuf[index] = sum % 10 + '0'; carry = sum / 10; } for (index = min_size; index < max_size; index++) { sum = max_str[index] - '0' + carry; kbuf[index] = sum % 10 + '0'; carry = sum / 10; } if (carry) kbuf[index++] = '1'; kbuf[index] = '\0'; str_reverse(kbuf, index); } ``` ::: 接著修改 `fibdrv_core.c` 裡的函式 `fib_sequence()` ```c= static long int fib_sequence(long int k, char *buf) { char kbuf[BUF_SIZE], fn[BUF_SIZE] = "1", fn_1[BUF_SIZE] = "0"; if (!k || k == 1) { kbuf[0] = k + '0'; kbuf[1] = '\0'; copy_to_user(buf, kbuf, 2); return k; } for (long int i = 2; i <= k; i++) { addString(fn_1, fn, kbuf); str_cpy(fn_1, fn, str_size(fn)); str_cpy(fn, kbuf, str_size(kbuf)); } long int res_size = str_size(fn); copy_to_user(buf, fn, res_size + 1); return res_size; } ``` 最特別的部份主要是第 18 行的 `copy_to_user` ,可以參考 [`copy_to_user`](https://www.cs.bham.ac.uk/~exr/lectures/opsys/13_14/docs/kernelAPI/r4037.html) ,主要是因為變數 `buf` 是 user space 的變數,且 `kbuf` 則是 kernel space 的參數,這裡需要 `copy_to_user` 作為兩者資料複製的橋樑 接著把 `fibdrv_core.c` 的巨集 `MAX_LENGTH` 改成 `500` ```c #define MAX_LENGTH 500 ``` 最後根據作業說明的連結 [The first 500 Fibonacci numbers](https://blog.abelotech.com/posts/first-500-fibonacci-numbers/) 新增新的測試檔 `fibnacci500.txt` ,裡頭存放從第 1 個到第 500 個數字 ``` ... 483 39043184998122354968635474677330498542864661988399598709411830703425204209650825540787157763668286722 484 63173200356011969809443437297358849022080673265589795452673441480303628721313666802004216758598573763 485 102216385354134324778078911974689347564945335253989394162085272183728832930964492342791374522266860485 ... ``` 修改 `Makefile` ```diff -@diff -u out scripts/expected.txt && $(call pass) +@diff -u out scripts/fibnacci500.txt && $(call pass) ``` 最後輸入命令 `make check` ,確認執行結果,成功新增 big number! ```diff make check make -C /lib/modules/5.13.0-28-generic/build M=/home/benson/fibdrv modules make[1]: Entering directory '/usr/src/linux-headers-5.13.0-28-generic' make[1]: Leaving directory '/usr/src/linux-headers-5.13.0-28-generic' make unload make[1]: Entering directory '/home/benson/fibdrv' sudo rmmod fibdrv || true >/dev/null make[1]: Leaving directory '/home/benson/fibdrv' make load make[1]: Entering directory '/home/benson/fibdrv' sudo insmod fibdrv.ko make[1]: Leaving directory '/home/benson/fibdrv' sudo ./client > out make unload make[1]: Entering directory '/home/benson/fibdrv' sudo rmmod fibdrv || true >/dev/null make[1]: Leaving directory '/home/benson/fibdrv' + Passed [-] ``` ## 測量 user space 與 kernel space 時間 在實作之前,先學習 `gettime` 和 `ktime` 的方法,參考去年學員 [bakudr18](https://hackmd.io/@MR-9Qa9wQLWglSAUyd6UhQ/HJ363p_Qd#%E6%B8%AC%E9%87%8F-user-space-%E8%88%87-kernel-space-%E6%99%82%E9%96%93) 的共筆 ### 測量 user space 這邊使用的範例為上述實作的結果,且使用的時間單位為 `ns` ,以下為實作程式碼 (`client.c`) ```c struct timespec start, end; for (int i = 0; i <= offset; i++) { lseek(fd, i, SEEK_SET); clock_gettime(CLOCK_MONOTONIC, &start); read(fd, buf, 256); clock_gettime(CLOCK_MONOTONIC, &end); printf("%d %lld\n", i, elapse(&start, &end)); } ``` 實作用來計算時間差的函式 `elapse()` ```c long long elapse(struct timespec *start, struct timespec *end) { return (long long) (end->tv_sec - start->tv_sec) * 1e9 + (long long) (end->tv_nsec - start->tv_nsec); } ``` ### 測量 kernel space 這邊參考作業說明裡的[核心模式的時間測量](https://hackmd.io/@sysprog/linux2022-fibdrv#%E6%A0%B8%E5%BF%83%E6%A8%A1%E5%BC%8F%E7%9A%84%E6%99%82%E9%96%93%E6%B8%AC%E9%87%8F) ,了解 [hrtimers](https://www.kernel.org/doc/Documentation/timers/hrtimers.txt) 的使用方法 首先,一樣跟著作業說明,將 `write` 挪用來輸出上一次 fibnacci 的執行時間,並且修改 `fibdrv_core.c` 的程式碼 ```c static ktime_t kt; /* calculate the fibonacci number at given offset */ static ssize_t fib_read(struct file *file, char *buf, size_t size, loff_t *offset) { kt = ktime_get(); long int res = fib_sequence(*offset, buf); kt = ktime_sub(ktime_get(), kt); return (ssize_t) res; } /* write operation is skipped */ static ssize_t fib_write(struct file *file, const char *buf, size_t size, loff_t *offset) { return (ssize_t) ktime_to_ns(kt); } ``` 接著修改 `client.c` 的程式碼 ```c struct timespec start, end; for (int i = 0; i <= offset; i++) { lseek(fd, i, SEEK_SET); clock_gettime(CLOCK_MONOTONIC, &start); read(fd, buf, 256); clock_gettime(CLOCK_MONOTONIC, &end); long int ktime = write(fd, buf, 256); printf("%lld %ld %lld\n", elapse(&start, &end), ktime, elapse(&start, &end) - ktime); } ``` ### 用統計手法去除極端值 這邊寫了一個 `eliminate.py` ,用來實現移除 `out` 產生出的極端值,同時可以執行多次測試並取平均值 ```python import subprocess import numpy as np def process(datas, samples, threshold = 2): datas = np.array(datas) res = np.zeros((samples, 3)) # 分別計算 kernel user 及 kernel to user 的平均 mean = [datas[:,0].mean(), datas[:,1].mean(), datas[:,2].mean()] # 分別計算 kernel user 及 kernel to user 的標準差 std = [datas[:,0].std(), datas[:,1].std(), datas[:,2].std()] cnt = 0 # 計算有幾組資料被捨去 for i in range(samples): for j in range(runs): tmp = np.abs((datas[j * samples + i] - mean) / std) # 計算資料是多少標準差 # 如果某一組的某個資料過大,整組捨去 if tmp[0] > threshold or tmp[1] > threshold or tmp[2] > threshold: cnt += 1 datas[j * samples + i] = [0, 0, 0] res[i] += datas[j * samples + i] res[i] /= (runs - cnt) # 剩下的資料取平均 cnt = 0 # count 歸 0 return res if __name__ == '__main__': runs = 50 # 執行次數 samples = 100 # 一次有幾個點 datas = [] for i in range(runs): # 執行採樣 subprocess.run('sudo ./client > out', shell = True) # 讀取資料 fr = open('out', 'r') for line in fr.readlines(): datas.append(line.split(' ', 2)) fr.close() datas = np.array(datas).astype(np.double) # 存回資料 np.savetxt('final', process(datas, samples)) ``` 為了方便執行這邊修改 `Makefile` ,新增 `plot` 及 `splot` 命令,如下所示 - `plot`: 執行多次後經過 python 計算後畫出的圖 - `splot`: 單獨執行一次所畫出的圖 ``` run: $(MAKE) unload $(MAKE) load sudo ./client > out $(MAKE) unload splot: all run gnuplot stime.gp plot: all $(MAKE) unload $(MAKE) load @python3 eliminate.py $(MAKE) unload gnuplot time.gp ``` 這邊列出只執行一次及執行 50 後經過 python 處理的結果,都是取前 100 個數 > 輸入命令 `make splot` (一共測試 3 次) > ![](https://i.imgur.com/HwhC4UW.png) > ![](https://i.imgur.com/0SWXis4.png) > ![](https://i.imgur.com/a88aOxl.png) > 輸入命令 `make plot` ,經過去除極端值,且結果為執行 50 次的平均結果 (一樣測試 3 次) > ![](https://i.imgur.com/UNprMf9.png) > ![](https://i.imgur.com/SHPHKAi.png) > ![](https://i.imgur.com/XfkqbQj.png) 可以看到有經過極端值處理的結果平緩且穩定許多 ### 測量結果 #### 指定 CPU 這邊有點好奇沒有指定 CPU 和有指定 CPU 所量測出來的結果,因此分別測試,分別測試前 100 、 500 和 1000 個數,且都只執行一次 > 測量沒有指定 CPU 的結果 > ![](https://i.imgur.com/sjtWntW.png) > ![](https://i.imgur.com/u4MN93P.png) > ![](https://i.imgur.com/2aSL0Eq.png) 接著測量有指定 CPU 的結果,從一開始的設定已經把 core 0 暫停,使用 core 0 進行量測 稍微修改 `Makefile` ```diff - sudo ./client > out + sudo taskset 0x1 ./client > out ``` > 測量有指定 CPU 的結果 > ![](https://i.imgur.com/omyfeQY.png) > ![](https://i.imgur.com/bI4fjcj.png) > ![](https://i.imgur.com/0acilI3.png) 兩者看起來沒有很明顯的差異,這邊節錄一段輸出資料 ```shell 無指定CPU n | user(ns) kernel(ns) kernel to user(ns) ---------------------------------------------------- 990 | 955769 954460 1309 991 | 927021 926313 708 992 | 1014242 1013048 1194 993 | 956964 955948 1016 994 | 938805 938039 766 995 | 987807 986673 1134 996 | 953144 952281 863 997 | 938464 937838 626 998 | 939117 938514 603 999 | 951948 951198 750 1000 | 942860 942245 615 ---------------------------------------------------- 有指定CPU n | user(ns) kernel(ns) kernel to user(ns) ---------------------------------------------------- 990 | 929553 928730 823 991 | 925940 925314 626 992 | 927896 927300 596 993 | 933285 932558 727 994 | 938535 937697 838 995 | 971580 970247 1333 996 | 944953 943327 1626 997 | 944706 943950 756 998 | 949182 948439 743 999 | 940326 939734 592 1000 | 942646 941641 1005 ``` :::success 從圖片看來兩者差異不大,但是以實際數據來看,大部分的資料顯示有指定 CPU 的時間有稍微快一點,但是沒有差非常多 ::: #### 採用不同的演算法計算 Fibnacci number :::warning To DO: 改成 Fast Doubling 並測量時間 ::: ## 參考資料 [Linux 核心設計: 不只挑選任務的排程器](https://hackmd.io/@sysprog/linux-scheduler) [費氏數列分析](https://hackmd.io/@sysprog/fibonacci-number) [The Linux Kernel Module Programming Guide](https://sysprog21.github.io/lkmpg/) [Introduction to Linux kernel driver programming](https://events19.linuxfoundation.org/wp-content/uploads/2017/12/Introduction-to-Linux-Kernel-Driver-Programming-Michael-Opdenacker-Bootlin-.pdf)