contributed by < SmallHanley
>
file_operations include/linux/fs.h
TODO
首先,根據不同的算法,畫出折線圖,橫軸是費氏數列第 n 項,縱軸是計算該項所花費的時間:
為了減少誤差,我嘗試將執行 10 次的結果取平均,再重新繪製,執行方式:
一個在 userspace 的程式,我們可以使用 taskset 命令,將指定的行程透過 PID 和 cpumask 固定在指定的 CPU 上執行,又或是將給定的新的程式或命令固定在指定的 CPU 上執行,用法如下:
至於 kernel module,在閱讀 Kernel_modules tutorial 後,發現 kernel module 可以使用 current
變數 (需要 include linux/sched.h
),該變數代表的是一個指標指向該行程的 struct task_struct
,這意味著 kernel module 是一個獨立且可以被排程的一個單位 (task),並用有自己的 PID,我們可以使用 taskset
根據 PID 固定在指定的 CPU 上執行。
不過我們又面臨一個問題,我們需要程式執行以後才會知道行程的 PID,因此我嘗試使用 linux/sched 的 sched_setaffinity()
,不過遇到以下問題,目前還沒解決:
後來改成在 client.c
使用系統呼叫 sched_setaffinity(2),先將 fibdv
的 PID 透過 write
傳至 client.c
,再呼叫 sched_setaffinity()
將行程固定在 CPU 0。
taskset 可指定行程所使用的 CPU 核心,但不代表其他的行程不會使用這些被指定的 CPU 核心,如果你不想讓其他的行程干擾你要執行的程式,讓指定的核心只能被自己設定的行程使用,可以使用 isolcpus
這個 Linux 核心起始參數,這個參數可以讓特定的 CPU 核心在開機時就被保留下來。
我在開機時使用 boot loader 所提供的自訂開機參數功能,手動加入 isolcpus=cpu_id
參數:
e
編輯開機參數。linux
開頭這行,加上 isolcpus=0
。Ctrl-x
或是 F10
以修改過後的設定開機。觀察 CPU 0 是否在開機後被保留下來:
使用 htop
觀察 CPU 0,不過 CPU 0 的使用率並不是一直維持在 0,推測有其他程式也有使用到 sched_setaffinity(),或是中斷處理 (?),不過可以大幅減低干擾:
抑制 address space layout randomization (ASLR)
設定 scaling_governor 為 performance。準備以下 shell script,檔名為 performance.sh
:
之後再用 $ sudo sh performance.sh
執行
針對 Intel 處理器,關閉 turbo mode
unsigned long
陣列,後續可以加入 size
來做優化。size
來做優化,更該於 commit 61cad9e 。一是降低運算成本,二是方便於乘法引入 ctz
加速手段。size
為表示此一數字所需的陣列大小 (即表示此數需要多少個 unsigned long
的空間), size
為 0
代表數字為 0
。0
。size
設成 0
。bn_t
變數的位址、num
和pos
。pos
作為陣列的 index,賦值成 num
。size
為 1
,我們又想要將陣列第 0
位置設成 0
,則將 size
歸零。ctzl
優化,目前實作是使用位移運算子 (<<
、>>
),只能處理小於等於 63 的右移,當 n 超過 64 (包含) 是未定義行為。size
的實作還是有瑕疵,如果左移超過儲存資料的上限,得到的 size
很可能是錯的。a
和 b
相加的結果存至 res
。__builtin_uaddl_overflow()
來檢查是否產生溢位,從最低位加到最高位,並將 carry 加至高一位。ADC
或是 ADCX
,不過沒有成功。ctzl
或是不同演算法優化。乘法除了加上 size
來優化,後續也增加 ctzl
的優化。原本的實作方式是模擬計算機組織教科書有關乘法器的實作方式,每次都只位移一個 bit。當除數的 bits 為 set 的數量很少的時候,這種作法的位移運算成本會高很多,於是我使用 ctzl
加速,可以一次位移多個 bits,實作如下:
對應的實驗和效能分析可以參考下面圖表。
引入 bignum 後,根據 Linux 效能分析的提示,繪製出不同實作方式對於不同輸入參數所花的時間比較:
將輸入參數擴增至 1000,發現 fast-doubling 和 exponentiating by squaring 與原本 時間複雜度相比,花費時間高很多,可見目前採用的乘法實作方式還有很大改進空間:
後續改進加入 size
和 ctzl
優化,前者優化各種運算的次數 (包含加法、減法、位移運算),後者大幅減少位移運算次數,實驗結果如下:
發現 exponentiating by squaring 的乘法運算量還是過大,fast-doubling 和 sequence 較為接近,不過在前 1000 項還是 sequence 的方式花費時間最少 (後續實驗發現輸入參數數量級要到 ,fast-doubling 才會追過 sequence)。
下圖以 fast-doubling 的實作方式,分析加上 size
(不含 ctzl
) 前後花費時間的比較:
在大數乘法的實作中加入 ctzl
優化,可以大幅減少位移運算的次數,下圖是實驗有無 ctzl
的比較,發現採用 ctzl
的程式碼執行時間大幅縮短:
進一步觀察整體程式 (fast-doubling 從 0 執行到 1000) 呼叫大數運算的次數,確實大幅減少 bn_sll()
、bn_srl()
的呼叫次數:
no-ctz | add | sub | sll | srl |
---|---|---|---|---|
times | 541118 | 8987 | 2316635 | 2307648 |
with-ctz | add | sub | sll | srl |
---|---|---|---|---|
times | 541118 | 8987 | 562141 | 553154 |
矩陣快速冪
Fast-doubling
The high-resolution timer API
Message logging with printk
oscarshiang 共筆
Other Built-in Functions Provided by GCC
Kernel modules
rt:labs Real-time properties of Linux
Built-in Functions to Perform Arithmetic with Overflow Checking