# 2024q1 Homework1 (lab0) contributed by < `Libright1558` > :::danger 務必詳閱作業規範 ::: ## 開發環境 ``` $ gcc --version gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0 $ 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): 4 On-line CPU(s) list: 0-3 Vendor ID: GenuineIntel Model name: Intel(R) Core(TM) i5-5350U CPU @ 1.80GHz CPU family: 6 Model: 61 Thread(s) per core: 2 Core(s) per socket: 2 Socket(s): 1 Stepping: 4 CPU max MHz: 2900.0000 CPU min MHz: 500.0000 BogoMIPS: 3600.04 Virtualization: VT-x Caches (sum of all): L1d: 64 KiB (2 instances) L1i: 64 KiB (2 instances) L2: 512 KiB (2 instances) L3: 3 MiB (1 instance) NUMA: NUMA node(s): 1 NUMA node0 CPU(s): 0-3 ``` ## 佇列實作 ### `q_new` > [commit 93da0b0](https://github.com/Libright1558/lab0-c/commit/93da0b0ed3d7a729cad1d51a6ae1c811c06020c7) q_new 函式功能為新增一個 prev 指標與 next 指標均指向自身的佇列開頭。首先分配 struct list_head 大小的空間,如果分配失敗,則回傳 NULL,如分配成功,則使用 INIT_LIST_HEAD 函式將 prev 指標與 next 指標指向自身,最後回傳該佇列開頭。 ```c struct list_head *q_new() { struct list_head *head_node = malloc(1 * sizeof(struct list_head)); if (!head_node) return NULL; INIT_LIST_HEAD(head_node); return head_node; } ``` ### `q_free` > [commit 93da0b0](https://github.com/Libright1558/lab0-c/commit/93da0b0ed3d7a729cad1d51a6ae1c811c06020c7) > <s>3/2 更新</s> ,[commit bb886d6](https://github.com/Libright1558/lab0-c/commit/bb886d604bc813d180b386786498641bc2cbdc95) :::danger 1. 不需要在開發紀錄提到日期,因為 GitHub 和 HackMD 皆可追蹤變革。專注程式碼的改進。 2. 列出你 GitHub 帳號對應的超連結 ::: q_free 先檢查 head 是否存在,如果不存在則不做任何事。此處使用指標的指標 indirect,指向佇列開頭的 next 指標,指標 target 用以釋放節點空間,使用間接指標的好處在於 indirect 指標本身不需要在節點之間移動,可藉由改變 value of indirect 將節點上的 next 指標指向下一個節點,最後再釋放開頭節點空間。 :::danger 不需要在開發紀錄提到日期,因為 GitHub 和 HackMD 皆可追蹤變革。專注程式碼的改進。 ::: > <s>3/2 更新</s> ,`q_free` 因為考量到 `element_t` 物件的加入,故實作方式改為先取得 element_t 物件的位置,`並藉由q_release_element` 來刪除節點。 ```c void q_free(struct list_head *head) { if (head) { struct list_head *node = NULL, *safe = NULL; list_for_each_safe (node, safe, head) { q_release_element(container_of(node, element_t, list)); } free(head); } } ``` :::danger 改進你的漢語表達。 ::: ### `q_insert_head/q_insert_tail` > [commit bb886d6](https://github.com/Libright1558/lab0-c/commit/bb886d604bc813d180b386786498641bc2cbdc95) :::danger 本 commit 並未依循 [How to Write a Git Commit Message](https://cbea.ms/git-commit/) 的規範,使用 `git rebase -i` 更正,隨時準備跟世界各地的開發者協作! ::: q_insert_head/q_insert_tail 先檢查開頭節點、字串是否存在,如果存在則複製字串,並分配 element_t 大小的空間,將字串賦值到 element_t 的 value memeber 裡,最後將 element 加入到佇列裡。 ### `q_remove_head/q_remove_tail` > [commit bb886d6](https://github.com/Libright1558/lab0-c/commit/bb886d604bc813d180b386786498641bc2cbdc95) :::danger 本 commit 並未依循 [How to Write a Git Commit Message](https://cbea.ms/git-commit/) 的規範,使用 `git rebase -i` 更正,隨時準備跟世界各地的開發者協作! ::: q_remove_head/q_remove_tail 先檢查開頭節點、字串是否存在,如果存在,則取出指向 element_t 的指標,如果 element_t 物件內存在字串,且 sp 不為 NULL,則先將字串複製到 sp 內,之後在字串尾端加入中止符號,最後移除該節點。 ### `q_size` > [commit 239892a](https://github.com/Libright1558/lab0-c/commit/239892a3b7f6f419a4f6fd618e1a6a861baf71e7) 藉由<s>遍歷</s> 節點來計算節點數量。 :::danger 留意[資訊科技詞彙翻譯](https://hackmd.io/@sysprog/it-vocabulary) ::: ### `q_delete_mid` > [commit 1493d63](https://github.com/Libright1558/lab0-c/commit/1493d6355ac23006730c52311efd4fe0a9e00f62) :::danger 本 commit 並未依循 [How to Write a Git Commit Message](https://cbea.ms/git-commit/) 的規範,使用 `git rebase -i` 更正,隨時準備跟世界各地的開發者協作! ::: 如果開頭佇列存在且不為空佇列,則透過 q_size 計算總節點數量並除以二,得知中間節點的相對位置,找到節點後,先用 `list_del` 將其移出佇列,之後用 `q_release_element` 釋放空間。 ### `q_swap` > [commit f5f5a63](https://github.com/Libright1558/lab0-c/commit/f5f5a63ab41d45e4894873d031e03c352ccd021a) `q_swap` 在佇列不為空且節點不為單一的條件下,使用 `prev` 與 curr 兩個指標,分別指向 `head->next` 與 `prev->next` ,之後紀錄 `prev->prev` 與 `curr->next` 的指標,並將 `prev` 與 `curr` 指向的節點交換位置。 ### `q_reverse` >[commit f5f5a63](https://github.com/Libright1558/lab0-c/commit/f5f5a63ab41d45e4894873d031e03c352ccd021a) `q_reverse` 在佇列不為空且節點不為單一的條件下,將頭節點與尾節點兩兩對應並交換位置。 ### `q_reverseK` >[commit f78600f](https://github.com/Libright1558/lab0-c/commit/f78600fda797f5409356be00e203d923c629cbc4) `q_reverseK` 藉由 `q_reverse` 反轉特定數量節點的位置。 ### `q_ascend` >[commit f78600f](https://github.com/Libright1558/lab0-c/commit/f78600fda797f5409356be00e203d923c629cbc4) `q_ascend` 藉由從佇列尾部開始往前走訪,遇到比前一個節點字串值還要小的節點,就刪除它。 ### `q_descend` >[commit f78600f](https://github.com/Libright1558/lab0-c/commit/f78600fda797f5409356be00e203d923c629cbc4) `q_descend` 藉由從佇列尾部開始往前走訪,遇到比前一個節點字串值還要大的節點,就刪除它。 ### `q_sort` >[commit fdcbea4](https://github.com/Libright1558/lab0-c/commit/fdcbea4a281238655463dc3467ac42a4dcf799b4) 透過將 [2024q1 第 1 週測驗題](https://hackmd.io/@sysprog/linux2024-quiz1) 中所提到的 Timsort 引入作為 `q_sort` 實作。原先嘗試以 [Powersort](https://arxiv.org/pdf/1805.04154.pdf) 進行實作,然而在串列節點個數在 18000 以上時,遇到執行時間超時的問題,有待解決。 ### `q_merge` ## 效能評測 #### `timsort vs listsort` `listsort` 參考 linux 核心排序演算法的實作,`timsort` 參考 [2024q1 第 1 週測驗題](https://hackmd.io/@sysprog/linux2024-quiz1),測驗二對 `timsort` 的實作。 生成 49999 個 `element_t` 節點,節點內包含長度 5~10 個字元的隨機值,上述測試資料被用於測試 `timsort` 與 `listsort` 執行效能,分別各執行 49999 次,結果以 `gnuplot` 呈現如下(綠色為 timsort ,紫色為 listsort)︰ ![listsort_vs_timsort](https://hackmd.io/_uploads/B1BJZ1DkR.png) 參考 [Linux 核心專題: 改進 lib/list_sort.c](https://hackmd.io/@sysprog/Hy5hmaKBh) ,使用F檢定來決定 `timsort` 與 `listsort` 是否有相同的平均執行時間。 1. 首先建立虛無假說: 虛無假說: + $H_0$ : $\mu_1 = \mu_2 = ... \mu_k$ 對立假說: + $H_1$ : $Not\ all\ means\ are\ equal$ 其中 $\mu_i$ 為每種演算法的平均執行時間。 2. 之後計算 F 值: mean square due to treatment 公式如下: $MSTR = \frac{\sum\limits_{j=1}^k n_j(\mu_j-\mu)^2}{k-1}$ mean square error 公式如下: $MSE = \frac{\sum\limits_{j=1}^k (n_j-1)s_j^2}{n_T-k}$ , $n_T = \sum\limits_{j=1}^k n_j$ k 為所有排序演算法的種類數量,$\mu$ 為所有排序演算法的執行時間總平均,$n_j$ 為每種排序演算法的執行次數,$s_j$ 為每種排序演算法執行時間的變異數。 F 值公式: $F = \dfrac{MSTR}{MSE}$ 本次評測所得到的 F 值為 99996 。 3. 是否拒絕虛無假說: [參考材料](https://online.stat.psu.edu/stat200/book/export/html/213) (Numerator) Degrees of Freedom : $df_{between}=k-1$ (Denominator, Error) Degrees of Freedom : $df_{within}=n-k$ $n$ 為所有排序演算法執行次數合計,$k$ 為排序演算法種類數量 本評測 (Numerator) Degrees of Freedom 為 1,(Denominator, Error) Degrees of Freedom 為 99998 。 [查表得知](http://www.socr.ucla.edu/Applets.dir/F_Table.html),critical value 在 $a=0.05$ 的情況下約為 3.84 ,而本評測 F 值為 99996,遠大於 critical value ,因此 reject null hypnothesis ,表示 `timsort` 與 `listsort` 執行時間平均值在統計學上不相同。 `timsort` 執行時間為:38908 (cycles_per_sec) , `listsort` 執行時間為:40677 (cycles_per_sec),顯示 `listsort` 相較於 `timsort` 在隨機的 49999 筆資料下,執行 49999 次,花費較多執行時間。