# 2019q1 Homework1 (list) contributted by < [`chengWei1123`](https://github.com/chengWei1123) > --- ## 開發環境 ```shell $ uname -a Linux wei-X550JX 4.18.0-15-generic #16~18.04.1-Ubuntu SMP Thu Feb 7 14:06:04 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux $ gcc --version gcc (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0 ``` --- ## 作業要求 > 節錄自: [F02: list](https://hackmd.io/s/S12jCWKHN) * 回答「自我檢查清單」的所有問題,需要附上對應的參考資料和必要的程式碼 - [ ] 自我檢查事項: * 為何 Linux 採用 macro 來實作 linked list?一般的 function call 有何成本? * Linux 應用 linked list 在哪些場合?舉三個案例並附上對應程式碼,需要解說,以及揣摩對應的考量 * GNU extension 的 [typeof](https://gcc.gnu.org/onlinedocs/gcc/Typeof.html) 有何作用?在程式碼中扮演什麼角色? * 解釋以下巨集的原理 ```Clike #define container_of(ptr, type, member) \ __extension__({ \ const __typeof__(((type *) 0)->member) *__pmember = (ptr); \ (type *) ((char *) __pmember - offsetof(type, member)); \ }) ``` * 除了你熟悉的 add 和 delete 操作,`list.h` 還定義一系列操作,為什麼呢?這些有什麼益處? * `LIST_POISONING` 這樣的設計有何意義? * linked list 採用環狀是基於哪些考量? * 什麼情境會需要對 linked list 做排序呢?舉出真實世界的應用,最好在作業系統核心出現 * 什麼情境會需要需要找到 [第 k 大/小元素](https://www.geeksforgeeks.org/kth-smallestlargest-element-unsorted-array/) 呢?又,你打算如何實作? * `list_for_each_safe` 和 `list_for_each` 的差異在哪?"safe" 在執行時期的影響為何? * for_each 風格的開發方式對程式開發者的影響為何? * 提示:對照其他程式語言,如 Perl 和 Python * 程式註解裡頭大量存在 `@` 符號,這有何意義?你能否應用在後續的程式開發呢? * 提示: 對照看 Doxygen * `tests/` 目錄底下的 unit test 的作用為何?就軟體工程來說的精神為何? * `tests/` 目錄的 unit test 可如何持續精進和改善呢? * 從 [linux-list](https://github.com/sysprog21/linux-list) 學習裡頭的技巧,包含裡頭 unit test 的設計,透過給定的 linked list 操作,實作出 merge sort * 附上你的修改過程,也需要讓 `qtest` 得以支援 * 可將 [dict](https://hackmd.io/s/B1Bq5Jat7) 裡頭的測試資料拿來作效能分析的輸入 * 思考提升排序效率的方法,需要做平均和最壞狀況的分析 --- ## 自我檢查清單 ### 為何 Linux 採用 macro 來實作 linked list? macro 只是單純的文字替換,雖然會增加 code section 的大小,但能省去 call function 時對stack 的操作,進而加快執行速度。 另外,我覺得 macro 在某些情況會比 function 更具彈性,例如 list.h 中的 list_for_each ```clike #define list_for_each(node, head) \ for (node = (head)->next; node != (head); node = node->next) ``` programmer 只要在呼叫 macro 後加上對 list node 的操作就好,而這一點使用 function 可能就比較難做到(可能要把對 list node 的操作寫成 function ,再把 function pointer 當成參數傳入 list_for_each function )。 下面是我在 [Macro vs Function in C](https://stackoverflow.com/questions/9104568/macro-vs-function-in-c) 看到有人對 macro 和 function 的優缺點所做的統整 * Macro features: * Macro is Preprocessed * No Type Checking * Code Length Increases * Use of macro can lead to side effect * Speed of Execution is Faster * Before Compilation macro name is replaced by macro value * Useful where small code appears many time * Macro does not Check Compile Errors * Function features: * Function is Compiled * Type Checking is Done * Code Length remains Same * No side Effect * Speed of Execution is Slower * During function call, Transfer of Control takes place * Useful where large code appears many time * Function Checks Compile Errors :::danger 不要只摘錄網路上的討論文字,設計實驗來檢驗,附上組合語言和 cycle count 分析,及早脫離「文組^TM^」 :notes: jserv ::: 考慮以下程式碼 ```test.c``` ```clike= #include <stdlib.h> #define add_m(a,b,c) a+b+c int add_f(int a,int b,int c){ return a+b+c; } int main(int argc, char *argv[]) { int i,j,k,result; int opt = atoi(argv[1]); int times = atoi(argv[2]); if(opt == 0){ for(i=0;i<times;i++) for(j=0;j<times;j++) for(k=0;k<times;k++) result = add_m(i,j,k); } else{ for(i=0;i<times;i++) for(j=0;j<times;j++) for(k=0;k<times;k++) result = add_f(i,j,k); } } ``` argv[1] 為 0 則使用 macro, 否則用 function ; argv[2] 則為各層迴圈數。 **result :** * ``` $ sudo perf record ./test 0 1000 $ sudo perf report ``` ![](https://i.imgur.com/KrUS1ry.png) * ``` $ sudo perf record ./test 1 1000 $ sudo perf report ``` ![](https://i.imgur.com/27dB0um.png) * 不同迴圈次數下使用 macro 和 function 的 cycle count ![](https://i.imgur.com/BF2Ig8f.png) ### Linux 應用 linked list 在哪些場合? ### GNU extension 的 [typeof](https://gcc.gnu.org/onlinedocs/gcc/Typeof.html) 有何作用? typeof(x) 會回傳 x 的 type 其中 x 可以是變數也可以是 type 例如: 1. ```clike int x; typeof (x) y = 0; ``` 相當於 ```clike int y = 0; ``` 2. ```clike typeof (typeof (char *)[4]) y; ``` 相當於 ```clike char *y[4]; ``` ### 解釋以下巨集的原理 ```Clike= #define container_of(ptr, type, member) \ __extension__({ \ const __typeof__(((type *) 0)->member) *__pmember = (ptr); \ (type *) ((char *) __pmember - offsetof(type, member)); \ }) ``` * ```__extension__``` * 使用 GNU C extensions 編譯時可能會產生 warning ,可以在 expression 前加上 ```__extension__``` 來避免 * [Alternate Keywords](https://gcc.gnu.org/onlinedocs/gcc/Alternate-Keywords.html#Alternate-Keywords) * ```{ }``` * braced-group within expression, compiler 會把大括號中所有 statement 當成一個 expression,並用最後一則 statement 的值作為此 expression 的結果 * [Statements and Expressions](https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html#Statement-Exprs) * ```__typeof__(((type *) 0)->member) *__pmember = (ptr);``` * ```(type *) 0```: 把0位置強制轉型成為一個指向 type 型別的指標 * ```__typeof__(((type *) 0)->member)```: 取得 type 所有的 member 之型別 * ```__typeof__(((type *) 0)->member) *__pmember```: 將 __pmember 宣告為指向 member 的 pointer * ```__typeof__(((type *) 0)->member) *__pmember = (ptr)``` 將 ptr 的位址 assign 給 __pmembe ::: warning 雖然看得懂這行在做什麼,但不太明白其中的必要性 ::: * ```offsetof(type, member)```: struct 和 member 記憶體位置的差值 * ```(char *) __pmember - offsetof(type, member)```: 得到 struct 的起始位置 ### 為何除了你熟悉的 add 和 delete 操作,`list.h` 還定義一系列操作? ### `LIST_POISONING` 這樣的設計有何意義? ### linked list 採用環狀是基於哪些考量? ### 什麼情境會需要對 linked list 做排序呢? ### 什麼情境會需要需要找到 [第 k 大/小元素](https://www.geeksforgeeks.org/kth-smallestlargest-element-unsorted-array/) 呢? ### `list_for_each_safe` 和 `list_for_each` 的差異在哪? ### for_each 風格的開發方式對程式開發者的影響為何? ### 程式註解裡頭大量存在 `@` 符號,這有何意義? ### `tests/` 目錄底下的 unit test 的作用為何? ### `tests/` 目錄的 unit test 可如何持續精進和改善呢? --- ## Merge Sort :::danger 應列出你的思路和規劃,不是急著列出程式碼。 需要提及驗證正確性和時間/空間複雜度分析 :notes: jserv ::: * [程式碼](https://github.com/chengWei1123/linux-list/blob/master/examples/merge-sort.c) * 思路分析 * merge sort 主要有三個工作 1. 把 input list 分成 2 等分 2. 再分別對這兩個 list 做排序 3. 把排序過的兩個 list 合併成一個排序好的 list * 把 input list 分成 2 等分 * 首先要找到 list 的中點,我原本用的方法是先走訪 list 所有點的到總點數,再走訪一半的點找到中點,但後來改用直接把 list 長度當參數傳入,便可以少一次走訪全部點的時間 * 接著用 list_cut_position() 把 list 前半移到一個 list,另一半移到另一個 list * 分別對這兩個 list 做排序 * 遞迴呼叫 merge sort 就好 * 合併兩個 sorted list * 當其中一個 list 為空時,只要把另一個 list 用 list_splice_tail() 接在 result list 後面 * 當兩者都不為空時, 比較持有兩個 list 第一個 node 的 listitem 中 member i 的大小,將具有較小的 member i 者,移至 result list 的尾巴(使用 list_del()、list_add_tail() ) --- ## 效能分析 random_shuffle_array 所產生的數據 (average case for all) ![](https://i.imgur.com/aWu4Jcb.png) ---