contributed by < max890808
>
mesohandsome
敘述函式新增或修改什麼部分,可以貼出對應的關鍵程式碼,可善用 diff。
有些 commit 沒有連結到正確的 github,例如 q_insert_head
實作中的這兩個
研讀 lib_sort.c 並引入到 lab0-c 章節中,只有看到比較,沒有研讀?
q_new
commit ac06e12
Chris Beams 在 How to Write a Git Commit Message 一文 (繁體中文翻譯: 如何寫一個 Git Commit Message) 提出,好的 Git commit message 應符合七條規則:
你沒有依循上述規範,請詳讀作業說明,以 git rebase -i
改正。
使用 malloc()
配置 list_head 記憶體,並透過 INIT_LIST_HEAD
初始化 head 。
q_free
commit 92f4f21
Chris Beams 在 How to Write a Git Commit Message 一文 (繁體中文翻譯: 如何寫一個 Git Commit Message) 提出,好的 Git commit message 應符合七條規則:
你沒有依循上述規範,請詳讀作業說明,以 git rebase -i
改正。
使用 list_for_each_entry_safe
走訪每一個結構體,並透過 q_release_element
釋放結構體所在的記憶體位置。
q_insert_head
q_insert_tail
Chris Beams 在 How to Write a Git Commit Message 一文 (繁體中文翻譯: 如何寫一個 Git Commit Message) 提出,好的 Git commit message 應符合七條規則:
你沒有依循上述規範,請詳讀作業說明,以 git rebase -i
改正。
一開始在撰寫程式碼時使用 strcpy
處理複製字串的部份,在 git commit
時跳出 Dangerous function 通知,經查閱2024 年 Linux 核心設計/實作課程作業 —— lab0和 Common vulnerabilities guide for C programmers 後,將 strcpy
改用 strncpy
實作,但還是需要注意字串結尾字元 '\0'
的問題。
The strcpy
built-in function does not check buffer lengths and may very well overwrite memory zone contiguous to the intended destination. In fact, the whole family of functions is similarly vulnerable: strcpy
, strcat
and strcmp
.
q_remove_head
q_remove_tail
Chris Beams 在 How to Write a Git Commit Message 一文 (繁體中文翻譯: 如何寫一個 Git Commit Message) 提出,好的 Git commit message 應符合七條規則:
你沒有依循上述規範,請詳讀作業說明,以 git rebase -i
改正。
使用 list_first_entry
或 list_last_entry
找到第一個結構體或最後一個結構體,之後使用 list_del
移除目標結構體。
q_delete_mid
使用快慢指標的方法找出中間的結構體,之後使用 q_release_element
將結構體刪除。
使用 List API 中的 list_del
精簡程式碼。
TODO: 使用 List API,撰寫出更精簡的程式碼。
q_delete_dup
commit 7477ca9
使用兩層迴圈實作,程式碼不精簡,應使用一個變數紀錄目前是否為最後一個相同值的結構體,並搭配 List API 撰寫更精簡的程式碼,之後還需要作修改。
q_swap
commit d125b96
運用指標的指標和 list_move
實作
q_reverse
commit 05c88a6
逐一走訪每個節點,將節點的 prev
和 next
交換。
q_reverseK
commit 9bc9849
使用 list_cut_position
將要反轉的鏈結串列移動到新的空佇列上,接著使用 q_reverse
將鏈結串列作反轉,最後使用 list_splice_init
將反轉完的鏈結串列移動回原本的佇列。
q_sort
commit 16cf13f
使用 merge sort 實作,採用遞迴呼叫,搭配快慢指標將鏈結串列左右對半切分,直到分割成單一節點再使用 merge
合併成排序好的鏈結串列。
q_ascend
q_descend
commit 2e0d815
使用list_for_each_safe
走訪每個節點並判斷在此節點前是否存在比此節點較大或較小的節點,若存在則使用 list_del
和 q_release_element
刪除節點。
q_merge
commit ab45163
先將所有佇列合併到第一個佇列,再使用 q_sort
排序。
目前在 github 上分數為95分,下一步將閱讀 Dude, is my code constant time?
這篇論文提出了一種可以檢測程式執行時間是否為常數時間的工具 dudect,此技術是基於 leakage detection techniques,不需要硬體模型,並且可以使用在 black-box testing。
接著作者說明如何實作 TIMING LEAKAGE DETECTION
Step 1: Measure execution time
使用兩種資料,分別為 fix-vs-random, fixed value 是為了要觸發 corner-case
。
Step 2: Apply post-processing
對所收集的資料進行後處理,有2種方式
Step 3: Apply statistical test
反駁 null hypothesis “the two timing distributions are equal”
commit 3010009
在比較 dudect 原始程式碼和 lab0-c 後,發現 lab0-c
缺少 percentile
的部份,因此我在lab0-c
中加入 cmp
、 percentile
和 prepare_percentiles
函式計算 NUMBER_PERCENTILES(這裡為100)
個閾值(thresholds),接著透過 update_statistics
過濾執行時間超過閾值的資料,符合條件的資料會放入對應該閾值的 t[crop_index]
,max_test
函式會從 t[crop_index]
計算最大的 t 值回傳。report
函式比較 t 值和 t_threshold_bananas
t_threshold_moderate
判斷程式執行是否為常數時間。
在 dudect 的 update_statistics
函式中,有捨棄前十筆資料,在一開始改進 lab0-c 時我也只有將 update_statistics
函式中的 i 改成10,但在參考 nosba0957 同學的實作後發現這樣改是錯的,lab0-c 原先就有在 measure
函式中加入 DROP_SIZE
,但這樣在 differentiate
函式計算執行時間時會導致開頭和結尾的地方都是 0。因此應該要在 measure
時紀錄全部的時間,在update_statistics
再將頭尾值刪除。執行上述改進後 make test
就能得到滿分了。
輸入 make valgrind
檢查是否發生記憶體錯誤
由測試結果可以可以發現目前的實作 valgrind 沒有發出任何的警告
commit 37715dc
參考 Risheng1128 的作法,將 lib/list_sort.c 引入 lab0-c 專案
使用 Risheng1128 的方法,建立一個用來量測排序時間的測試檔 trace-time-measure.cmd
資料總數 | q_sort 第一次 (s) |
q_sort 第二次 (s) |
q_sort 第三次 (s) |
q_sort 平均 (s) |
list_sort 第一次 (s) |
list_sort 第二次 (s) |
list_sort 第三次 (s) |
list_sort 平均 (s) |
---|---|---|---|---|---|---|---|---|
100000 | 0.093 | 0.090 | 0.086 | 0.090 | 0.093 | 0.082 | 0.089 | 0.088 |
200000 | 0.202 | 0.204 | 0.203 | 0.203 | 0.199 | 0.199 | 0.204 | 0.200 |
300000 | 0.333 | 0.358 | 0.360 | 0.350 | 0.322 | 0.325 | 0.322 | 0.323 |
400000 | 0.496 | 0.492 | 0.485 | 0.491 | 0.448 | 0.454 | 0.388 | 0.430 |
500000 | 0.642 | 0.628 | 0.631 | 0.634 | 0.563 | 0.578 | 0.583 | 0.575 |
從平均的結果可以看到 list_sort
的執行時間比 q_sort
短,但幅度不大。接著使用 perf 工具進一步分析效能
輸入命令 perf stat --repeat 5 -e cache-misses,cache-references,instructions,cycles ./qtest -f traces/trace-time-measure.cmd
→ 程式會執行 5 次,並且自動平均,其中每次測試的資料數都設定為 500000 ,以下為 q_sort
及 list_sort
執行的結果
list_sort
q_sort
q_sort |
list_sort |
|
---|---|---|
cycles | 41,2860,8108 | 38,2314,6420 |
instructions | 23,8540,9426 | 23,1425,8918 |
cache-references | 9665,6148 | 7947,5959 |
cache-misses | 5715,4304 | 4981,3140 |
insn per cycle | 0.58 | 0.60 |
從 perf 的分析結果可以看到, list_sort
發生 cache miss 的次數比 q_sort
來的少,可以對應到 Linux 核心的鏈結串列排序 裡提到的「用 bottom up 實作 merge sort 對 cache 較友善,可避免大量的 cache trashing」