# 2023q1 Homework3 (fibdrv)
contributed by < `koty6069` >
## 開發環境
```shell
$ gcc --version
gcc (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0
$ uname -r
5.19.0-35-generic
$ lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Address sizes: 39 bits physical, 48 bits virtual
Byte Order: Little Endian
CPU(s): 8
On-line CPU(s) list: 0-7
Vendor ID: GenuineIntel
Model name: Intel(R) Core(TM) i5-8250U CPU @ 1.60GHz
CPU family: 6
Model: 142
Thread(s) per core: 2
Core(s) per socket: 4
Socket(s): 1
Stepping: 10
CPU max MHz: 3400.0000
CPU min MHz: 400.0000
BogoMIPS: 3600.00
Caches (sum of all):
L1d: 128 KiB (4 instances)
L1i: 128 KiB (4 instances)
L2: 1 MiB (4 instances)
L3: 6 MiB (1 instance)
```
## 前期準備
觀察產生的 `fibdrv.ko` 模組
```shell
$ modinfo fibdrv.ko
```
得到以下輸出
```shell
filename: /home/ray/linux2023/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.19.0-35-generic SMP preempt mod_unload modversions
```
觀察 `fibdrv.ko` 核心模組在 Linux 核心掛載後的行為
```shell
$ sudo insmod fibdrv.ko
$ sudo ls -l /dev/fibonacci
crw------- 1 root root 509, 0 3月 13 12:50 /dev/fibonacci
$ sudo cat /sys/class/fibonacci/fibonacci/dev
509:0
$ sudo cat /sys/module/fibdrv/version
0.1
$ sudo lsmod | grep fibdrv
fibdrv 16384 0
$ sudo cat /sys/module/fibdrv/refcnt
0
```
掛載 `fibdrv.ko` 核心模組後,執行 `client` 觀察其輸出
```shell
$ sudo ./client
Reading from /dev/fibonacci at offset 0, returned the sequence 0.
Reading from /dev/fibonacci at offset 1, returned the sequence 1.
Reading from /dev/fibonacci at offset 2, returned the sequence 1.
...
Reading from /dev/fibonacci at offset 91, returned the sequence 4660046610375530309.
Reading from /dev/fibonacci at offset 92, returned the sequence 7540113804746346429.
Reading from /dev/fibonacci at offset 93, returned the sequence 7540113804746346429.
```
可看到如同作業說明所描述,在 `fibdrv.c` 中將最大長度限制在 92 ,因此 $F(92)$ 後的輸出皆相同
```c
#define MAX_LENGTH 92
```
## Linux 效能分析設定
> 參考 [作業說明(三)](https://hackmd.io/@sysprog/linux2023-fibdrv/%2F%40sysprog%2Flinux2023-fibdrv-c) 和 [`KYG-yaya573142`](https://hackmd.io/@KYWeng/rkGdultSU) 進行設定
### 限定 cpu 給特定程式使用
修改 `/etc/default/grub` ,在 `GRUB_CMDLINE_LINUX_DEFAULT` 加入 `isolcpus=3` 使開機時保留編號 3 的 cpu 核心。
```
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash isolcpus=3"
```
修改完輸入 `sudo update-grub` 以更新設定,重開機後確認設定是否生效。
使用 `taskset` 可發現 PID 1 的處理器親和性不包含編號 3 的核心。
```shell
$ cat /sys/devices/system/cpu/isolated
7
$ taskset -cp 1
pid 1's current affinity list: 0-2,4-7
```
後續可透過 `taskset` 指定編號 3 的核心給特定程式使用。
```shell
$ sudo taskset -c 3 ./client
```
### 排除干擾效能分析的因素
* 抑制 ASLR
```shell
$ sudo sh -c "echo 0 > /proc/sys/kernel/randomize_va_space"
```
* 使用 `performance.sh` 設定 scaliing_gorvernor 為 performance
```shell
$ sudo sh performance.sh
```
* 關閉 turbo mode
```shell
$ sudo sh -c "echo 1 > /sys/devices/system/cpu/intel_pstate/no_turbo"
```
* 關閉 SMT
```shell
$ sudo sh -c "echo off > /sys/devices/system/cpu/smt/control"
```
:::warning
執行完上述指令後,編號 4~7 的 cpu 會變成 offline 狀態。
```shell
$ lscpu
...
CPU(s): 8
On-line CPU(s) list: 0-3
Off-line CPU(s) list: 4-7
...
```
:::
### 時間量測
根據 [作業說明](https://hackmd.io/@sysprog/linux2023-fibdrv/%2F%40sysprog%2Flinux2023-fibdrv-c) 使用 `k_time` 相關 API 改寫 `fib_write` 和 `fib_read` 使其能在計算數列時紀錄所花費的時間,幫助後續實驗分析。
接著,在 `client.c` 中使用 `read()` 獲得費氏數列數值,以及使用 `write()` 取得該次計算所花費的時間。
```c
for (int i = 0; i <= offset; i++) {
long long sz;
lseek(fd, i, SEEK_SET);
sz = read(fd, buf, 1);
printf("Reading from " FIB_DEV
" at offset %d, returned the sequence "
"%lld.\n",
i, sz);
sz = write(fd, write_buf, strlen(write_buf));
printf("Time in ns is %lld\n", sz);
fprintf(fp, "%lld\n", sz);
}
```
對應的輸出節錄如下。
```
Reading from /dev/fibonacci at offset 0, returned the sequence 0.
Time in ns is 38
Reading from /dev/fibonacci at offset 1, returned the sequence 1.
Time in ns is 24
Reading from /dev/fibonacci at offset 2, returned the sequence 1.
Time in ns is 54
...
```
## 改進費氏數列
### Fast Doubling
參考作業說明[(一)](https://hackmd.io/@sysprog/linux2023-fibdrv/%2F%40sysprog%2Flinux2023-fibdrv-a#L04-fibdrv)[(四)](https://hackmd.io/@sysprog/linux2023-fibdrv/%2F%40sysprog%2Flinux2023-fibdrv-d)和 [Calculating Fibonacci Numbers by Fast Doubling ](https://chunminchang.github.io/blog/post/calculating-fibonacci-numbers-by-fast-doubling) 實作 Fast Doubling ,並結合 bitwise 操作和考慮硬體加速。
使用 clz / ctz 得到 leading 0s 以減少不必要的迭代次數。
```c
int lz = __builtin_clzll(k);
```
參考虛擬碼進行迴圈操作,從最高位的 1 開始往最低位進行判斷:
* 當目前位元為 0 :計算 $F(2k), F(2k+1)$
* 當目前位元為 1 :計算 $F(2k), F(2k+1)$ 以外,再計算 $F(2K+2)$
其中,使用 `k & 1UL << i - 1` 來判斷當前位元是否為 1,將 1 向左 shift 到當前位元再使用 `and` 得到當前位元。
```c
for (int i = 64 - lz; i >= 1; i--) {
long long t1 = a * (2 * b - a);
long long t2 = b * b + a * a;
a = t1;
b = t2;
if (k & 1UL << i - 1) {
t1 = a + b;
a = b;
b = t1;
}
}
```
因為每次都要將 `1UL` 向左 shift `i - 1` 位,因此可以將迴圈的起始條件和結束條件都減 1,使得下方 shift 可以改寫成 `1UL << i` 以免除每次都要進行的 `i - 1` 計算。
```c
for (int i = 63 - lz; i >= 0; i--)
```
```c
if (k & 1UL << i)
```
後續在測量時間時,發現使用此方法在 `k = 0` 所花費的時間特別長,導致繪出的比較圖無法看出兩者差異。
```shell
Reading from /dev/fibonacci at offset 0, returned the sequence 0.
Time in ns is 3620888938
Reading from /dev/fibonacci at offset 1, returned the sequence 1.
Time in ns is 41
```
![](https://i.imgur.com/zDCnA5n.png)
因此在最前方加上提前中止的條件以解決此問題。
```c
if (k <= 1)
return k;
```
```shell
Reading from /dev/fibonacci at offset 0, returned the sequence 0.
Time in ns is 38
Reading from /dev/fibonacci at offset 1, returned the sequence 1.
Time in ns is 24
```
### 效能比較(尚未排除效能分析干擾因素)
在排除效能分析干擾因素前,先試著比較原始版本和 Fast Doubling 版本的效能差異。
可看出隨著輸入數值增長,原始版本花費的時間也有明顯上升,而 Fast Doubling 版本耗時則幾乎維持在相同程度。
![](https://i.imgur.com/kJWviuc.png)
### 效能比較(排除效能分析干擾因素)
在排除干擾後,可看出綠色線的浮動有稍微減緩。
![](https://i.imgur.com/wX4dDEO.png)
### 計算 $F_{93}$ 之後的 Fibonacci 數