--- tags: linux kernel --- # 2022q1 Homework3 (fibdrv) contributed by <`huang-me`> ## 排除干擾效能分析的因素 * 抑制 [address space layout randomization](https://en.wikipedia.org/wiki/Address_space_layout_randomization) (ASLR) ```shell $ sudo sh -c "echo 0 > /proc/sys/kernel/randomize_va_space" ``` * 設定 scaling_governor 為 performance。準備以下 shell script,檔名為 `performance.sh`: ```shell for i in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor do echo performance > ${i} done ``` 之後再用 `$ sudo sh performance.sh` 執行 * 針對 Intel 處理器,關閉 turbo mode ```shell $ sudo sh -c "echo 1 > /sys/devices/system/cpu/intel_pstate/no_turbo" ``` ## Fix variable-length array in `fib_sequence` ```c static long long fib_sequence(long long k) { /* FIXME: C99 variable-length array (VLA) is not allowed in Linux kernel. */ long long f[2] = {0, 1}; for (int i = 2; i <= k; i++) { f[1] += f[0]; f[0] = f[1] - f[0]; } return k==0 ? 0 : f[1]; } ``` - 因為永遠只需要前兩項的值,因此其實不需要這麼多記憶體位置記錄所有之前計算的值。 ## fast doubling 利用 F(n) 以及 F(n+1) 直接計算 F(2n) 之值。 $$ \begin{split} &F(2k) = F(k)[2F(k+1) - F(k)] \\ &F(2k+1) = F(k+1)^2+F(k)^2 \end{split} $$ ```c static long long fib_sequence(long long k) { if (k < 2) /* F(0) = 0, F(1) = 1 */ return k; long long f[2] = {0, 1}; for (unsigned int i = 1U << 31; i; i >>= 1) { /* F(2k) = F(k) * [ 2 * F(k+1) – F(k) ] */ long long k1 = f[0] * (f[1] * 2 - f[0]); /* F(2k+1) = F(k)^2 + F(k+1)^2 */ long long k2 = f[0] * f[0] + f[1] * f[1]; if (k & i) { f[0] = k2; /* F(2k+1) */ f[1] = k1 + k2; /* F(2k+2) */ } else { f[0] = k1; /* F(2k) */ f[1] = k2; /* F(2k+1) */ } } return f[0]; } ``` - 原本使用 iterative 的方式計算 Fibonacci number 的時間複雜度爲 O(n) 的時間複雜度。 - 使用 fast doubling 因為無論 n 的大小都只需要做計算32次,因此只需要 O(1) 的時間複雜度。 ### 效能分析 ![](https://i.imgur.com/9li9Wb4.png) > 滿足前面推論的 O(n) 以及 O(1),不過因為需要 shift 太多次,所以在 F(40) 以前並沒有任何的優勢。 ## fast doubling with clz ```c static long long fib_fast_clz(long long k) { if (k < 2) /* F(0) = 0, F(1) = 1 */ return k; long long f[2] = {0, 1}; for (unsigned int i = 1U << (32 - __builtin_clz(k)); i; i >>= 1) { /* F(2k) = F(k) * [ 2 * F(k+1) – F(k) ] */ long long k1 = f[0] * (f[1] * 2 - f[0]); /* F(2k+1) = F(k)^2 + F(k+1)^2 */ long long k2 = f[0] * f[0] + f[1] * f[1]; if (k & i) { f[0] = k2; /* F(2k+1) */ f[1] = k1 + k2; /* F(2k+2) */ } else { f[0] = k1; /* F(2k) */ f[1] = k2; /* F(2k+1) */ } } return f[0]; } ``` ![](https://i.imgur.com/9mZvJl7.png) > 因為大幅減少了前面無意義的 shift,也大幅的減少了計算所需的時間。