# 2024q1 Homework5 (assessment) contributed by < [YiChiChao](https://github.com/YiChiChao) > ## Quiz 1 ### 測驗 `1` [利用 Linux Kernel API 改寫](https://hackmd.io/0hIrCST8QGea8-_3w5rCfg#%E5%88%A9%E7%94%A8-Linux-Kernel-API-%E6%94%B9%E5%AF%AB) ### 測驗 `2` ## Quiz 2 ### 測驗 `2` [實作測試之相關程式](https://github.com/YiChiChao/LinuxKernelLecture-Lab/commit/d3324bc8c286d632f178c57c9933918af27577f5) #### 觀察實作 在當前的程式中,hhead[] 的大小和快取的最大存量等大。我修改 `lRUCacheCreate` 的參數,將 `hhead` 的大小和 `capacity` 作區隔,去測試觀察看縮減 hash table 的大小對搜尋時間的影響。 ![mod_methed_result](https://hackmd.io/_uploads/B1S7e4U-0.png) 單就 cache capacity = 1000 的情形來觀察,hlist 的大小在500之後趨於平緩,可認 hlist 的大小設定宜做調整。 ![multi_method_without_calculationtime](https://hackmd.io/_uploads/S15Fe4UZA.png) 參考 [Linux 核心的 hash table 實作](https://hackmd.io/@sysprog/linux-hashtable) 將 hash function 改以 Multiplication Method 計算,從搜尋時間來看並無太大的差異。 ## Quiz 3 ### 測驗 `4` $$S_t=\begin{cases} \begin{flalign*} &Y_0 \qquad &t = 0 \\ &\alpha Y_t+(1-\alpha )\cdot S_{t-1} \qquad &t > 0 \end{flalign*} \end{cases} $$ ```c struct ewma{ unsigned long internal; unsigned long factor; unsigned long weight; } ``` * `internal` 是紀錄當前時間之計算出的 EWMA * `factor` 的定義我不清楚作用,按照老師給的註解,是用於對 `internal` 數值的縮放 * `weight` 為 EWMA 中的衰變率,從後面的程式碼回推`weight` 和 公式中 $\alpha$ 的關係為 $\alpha = \frac{1}{weight}$ ```c /* Add a sample to the average.*/ struct ewma *ewma_add(struct ewma *avg, unsigned long val) { avg->internal = avg->internal ? (((avg->internal << EEEE) - avg->internal) + (val << FFFF)) >> avg->weight : (val << avg->factor); return avg; } ``` 將函式拆解會變成這樣 $$\tiny avg\rightarrow internal=\begin{cases} \begin{flalign*} &(avg\rightarrow val) \cdot 2^{avg\rightarrow factor } \qquad &avg\rightarrow internal = 0 \\ &\frac{1}{weight} \cdot (avg\rightarrow val) \cdot 2^{avg\rightarrow factor }+(1-\frac{1}{weight} )\cdot avg\rightarrow internal \qquad &avg\rightarrow internal \not = 0 \end{flalign*} \end{cases} $$ 為了在程式碼中用shift來實現除法運算,程式碼加了對 `factor` 以及 `weight` 必須是2的冪的限制。將 $1$ 用 $\frac{weight}{weight}$ 替代。 $$\tiny avg\rightarrow internal=\begin{cases} \begin{flalign*} &(avg\rightarrow val) \cdot 2^{avg\rightarrow factor } \qquad &avg\rightarrow internal = 0 \\ &\frac{1}{{\color{red}{weight}}} \cdot (avg\rightarrow val) \cdot 2^{avg\rightarrow factor }+(\frac{weight}{{\color{red}{weight}}}-\frac{1}{{\color{red}{weight}}} )\cdot avg\rightarrow internal \qquad &avg\rightarrow internal \not = 0 \end{flalign*} \end{cases} $$ 再將 $\frac{1}{weight}$ 提出,以 `>> weight` 表示。 $$\tiny avg\rightarrow internal=\begin{cases} \begin{flalign*} &(avg\rightarrow val) \cdot 2^{avg\rightarrow factor } \qquad &avg\rightarrow internal = 0 \\ &\frac{1}{{\color{red}{weight}}} \cdot [(avg\rightarrow val) \cdot 2^{avg\rightarrow factor }+(weight-1 )\cdot avg\rightarrow internal] \qquad&avg\rightarrow internal \not = 0 \end{flalign*} \end{cases} $$ #### 觀察實作 在 [drivers/net/virtio_net.c](https://elixir.bootlin.com/linux/latest/source/drivers/net/virtio_net.c) 透過 `DECLARE_EWMA(pkt_len, 0, 64)` 來定義一個結構體 `struct ewma_pkt_len` 來利用 EWMA 調整 mergeable receive buffer 的封包大小。 ## 閱讀學習資料問題 [Data Alignment](https://hackmd.io/@sysprog/c-memory#%E9%87%8D%E6%96%B0%E7%9C%8B-Heap) ```c uint32_t v = *(uint32_t*) (csrc & 0xfffffffc); ``` 執行時,透過 gdb 觀察: ```c Program received signal SIGSEGV, Segmentation fault. unaligned_get8 (src=0x7fffffffd600) at unaligned_get32.c:9 uint32_t v = *(uint32_t*) (csrc & 0xfffffffc); ``` > POSIX.1-2017, "References to unmapped addresses shall result in a SIGSEGV signal." :::danger 不要參照中文 Wikipedia,其資訊通常過時,參照 IEEE / Open Group 的 POSIX 規範文件。 > 了解[name=YiChi Chao] ::: 當在使用遮罩取最後兩位以外的位元後,由於取得的指標未初始化,在此情況下對指標取值 (`*(uint32_t*)`),為無效的記憶體<s>訪問</s> 。 :::danger 注意用語,參見[資訊科技詞彙翻譯](https://hackmd.io/@sysprog/it-vocabulary)及[詞彙對照表](https://hackmd.io/@l10n-tw/glossaries) ::: 即使此強置轉型沒有 `SIGSEGV` 報錯,這個取值在此程式中也不合理。 改進方式是直接將 `csrc & 0xfffffffc` 強置轉型為 `(uint32_t)` :::danger **中文和英文字元之間要有空白字元** (對排版和文字搜尋有利) ::: --- #### x86-64 ABI IEEE 754, Assume float is 32-bit ```c float float_div2(float x) { if(x == 0)return 0; // using bitwise operations. No mul, div __uint32_t calculate = *(__uint32_t*) &x; __uint32_t exponent = calculate & 0x7F800000; calculate &= 0x807FFFFF; exponent -= 0x800000; exponent &= 0x7F800000; calculate |= exponent; return *(float*) &calculate; } ``` > TODO: 實作程式碼並解釋 實作程式碼:[445afd8](https://github.com/YiChiChao/LinuxKernelLecture-Lab/commit/445afd88fec6f38afedc1cdb238de1936004bc6b) ```graphviz digraph G { // Set graph attributes graph [rankdir=LR, splines=line, nodesep=0.5] // Define node shapes and styles node [shape=plaintext, fontsize=15] // Combined box with three compartments combined_box [label=<<table border="0" cellborder="1" cellspacing="0"> <tr><td>sign</td><td><font color="blue"><b>exponent </b></font></td><td>mantissa</td></tr> <tr><td>0</td><td><font color="blue"><b>1110 0001</b></font> </td><td>1000 0000 0000 0000 0000 000 </td></tr> </table>>, width=0, height=0] } ``` 按照 IEEE-754 對浮點數的表示方法: $$ (-1)^{sign} \times 1.mantissa\times2^{exponent-bias}$$ 在不使用除法和乘法的前提下,透過位元操作將 `exponent` 的數值減一便能完成浮點數除以二的運算。 首先透過位元遮罩將 `exponent` 的部份單獨取出, $$ \begin{array}{ccc} &01110000110000000000000000000000\\ \And&01111111100000000000000000000000\\ \hline =&0\color{blue}{11100001}00000000000000000000000 \end{array}$$ 並且將 `calculate` 中的 exponent 部份先清零: $$ \begin{array}{ccc} &01110000110000000000000000000000\\ \And&10000000011111111111111111111111\\ \hline =&0\color{blue}{00000000}10000000000000000000000 \end{array}$$ 再來將 `exponent` 部份減一,也就是對此浮點數表示方式減 `0x800000` : $$ \begin{array}{ccc} &0\color{blue}{11100001}00000000000000000000000\\ -&00000000\color{red}100000000000000000000000\\ \hline =&0\color{blue}{11100000}00000000000000000000000 \end{array}$$ 最後將 `calculate` 和計算好之 `exponent` OR 在一起。 值得注意的部份,在 IEEE-754 對特定數值有特例的表示方法,像是 `INF` 的表示為 `exponent` 全為一,`mantissa` 全為 0 。在測試我寫的函式是否正確時,發現當輸入值為 0 時,運算出的結果為 `INF` 。 ``` x = 0.000000 float_div2(x) = inf x/2 = 0.000000 ``` 所以在函式中單獨對 0 作判斷。 參考資料:[IEEE-754 與浮點數運算](https://hackmd.io/@czPKboGUQZi6-txq9HcDqw/BkzftBYAv) TODO: quiz3+quiz4