contributed by < cwl0429
>
lsmod
的輸出結果有一欄名為 Used by
,這是 "each module's use count and a list of referring modules",但如何實作出來呢?模組間的相依性和實際使用次數 (reference counting) 在 Linux 核心如何追蹤呢?
搭配閱讀 The Linux driver implementer’s API guide » Driver Basics
fibdrv.c
存在著 DEFINE_MUTEX
, mutex_trylock
, mutex_init
, mutex_unlock
, mutex_destroy
等字樣,什麼場景中會需要呢?撰寫多執行緒的 userspace 程式來測試,觀察 Linux 核心模組若沒用到 mutex,到底會發生什麼問題。嘗試撰寫使用 POSIX Thread 的程式碼來確認。參考 AdrianHuang/fibdrv,讓數字以 char 的型態儲存
接著研究一下如何將 char 型態的大數相加
觀察以下程式碼,發現在相加之前會需將 string 反轉,原因是 string 在反轉後 大數 a
及 b
的最低位數便可對齊,以下假設 a 為 1234 ; b 為 534
反轉前:
反轉後:
此時 a 及 b 陣列開頭存放的 char 就會是個位數
在與老師討論後,發現以 char 存放數字的方式會有幾點問題
浪費大量記憶體空間
一個 char 的大小為 1 byte 也就是 8 個 bits,能表示的數字範圍為 0 ~ 255
,而 char 能表示的數字範圍為 '0' ~ '9'
而此範圍能用 4 個 bits 表示,所以在每個 char 中都至少浪費一半的 bits
基於以上理由,我嘗試實作以 int *
的方式存放的大數
參考 KYG-yaya573142 的報告 的大數資料結構
利用可動態配置的 number
存放大數 size
紀錄 number
的長度,而 sign
會紀錄大數的正負號但目前實作還沒用上。
為了實作 fib
及 fib_fast_doubling
,我實作了以下函式
bign_to_string
參考 KYG-yaya573142 的報告 的方法,利用此函式將計算完畢的 fib number 從 kernel 端複製到 user 端,過程如下圖:
先假設 bign a 的 a->number[0]
為 0...001011
, a->size
為 1
, 對應的 string
長度為 12
以下圖示為了作圖方便,省略 number 的前 28 個數值為 0 的 bits
從 number
的 msb 開始往右掃描,若當前位元為 1
則 carry
為 1
,並更新整個 string
,對每個位元做 c[j] += c[j] - '0' + carry;
若過程中發現 c[j]
會大於 9
則設置 carry
為 1
用以進位到 c[j-1]
否則設置為 0
接著重複執行上述動作
掃描完後,得到最後結果 11
bign_add
及 bign_sub
add_BigN
number[i]
的 lsb 開始計算,將 a->number[i], b->number[i]
及 carry
加至 c->number[i]
,並在過程利用巨集 DETECT_OVERFLOW
偵測是否有溢位發生,若是溢位便將 carry
設置為 1
否則為 0
sub_BigN
sub_BigN
和 add_BigN
想法類似,主要差別為偵測溢位的方式bign_mul
令 a
為被乘數 b
為乘數,之後便用乘法直式的過程計算
需要注意的地方,在 mul_BigN
中我使用了 __fls
來省略乘數中的 leading zeros ,避免不必要的計算
__fls - find last (most-significant) set bit in a long word
更新 bign_mul
原先:
從乘數的 lsb 開始往左掃描 bits,若 bit 為 1 則將被乘數加入 c 並在每回合結束時將乘數左移一格
需花費 (由於兩版本實作主要差別為兩層 for 內部,故只計算迴圈內的執行時間)
bign_add
會隨著資料大小而改變運算時間後來:
參考 KYG-yaya573142 的 mul
實作方式
將乘數的 a->number[0~size]
分別和被乘數的 b->number[0~size]
兩兩相乘即可
需花費
可以發現後來的實作方式因避免使用 bign_add
而改善效能
最後將 bign_fib
及 bign_fib_fast
實作
回去翻閱 lab0 作業說明的「變數和函式命名力求簡潔且精準」,上述程式碼在函式名稱中混用大小寫,對理解行為沒有幫助。
已改正 coding style
根據 fibdrv/linux 效能分析的提示 設定測量環境
利用 ktime
來測量效能,以下為使用自己實作的 bign 執行 naive 及 fast doubing 的結果
先撇除影響效能測量的因素,可以發現 fast doubing 比起 naive 要慢上不少,顯示了我實作的 bign 有效能問題
經由 ktime
測量後,發現是 bign_mul
執行速度過慢,因此拖垮了 bign_fib_fast
的執行速度
下圖為優化了 bign_mul
的效能後的結果
可以發現在改善 bign_mul
效能後,有正確產生預期結果
測量 fib(0)~fib(1000) 效能
額外寫一個 performance_stattistic.c
想法是,將每個 fib(n)
及 fib_fast(n)
分別執行 1000 次,得到數據後,算出其標準差,將超過三個標準差的離群值去除,也就是取 99.7% 的資料
最後取去掉離群值後的平均值作圖,結果如下圖:
因為去除離群值的緣故,可以看出數據抖動的狀況大幅減少,但是在前 100 個費氏數仍有抖動的情況發生,推測原因是