or
or
By clicking below, you agree to our terms of service.
New to HackMD? Sign up
Syntax | Example | Reference | |
---|---|---|---|
# Header | Header | 基本排版 | |
- Unordered List |
|
||
1. Ordered List |
|
||
- [ ] Todo List |
|
||
> Blockquote | Blockquote |
||
**Bold font** | Bold font | ||
*Italics font* | Italics font | ||
~~Strikethrough~~ | |||
19^th^ | 19th | ||
H~2~O | H2O | ||
++Inserted text++ | Inserted text | ||
==Marked text== | Marked text | ||
[link text](https:// "title") | Link | ||
 | Image | ||
`Code` | Code |
在筆記中貼入程式碼 | |
```javascript var i = 0; ``` |
|
||
:smile: | ![]() |
Emoji list | |
{%youtube youtube_id %} | Externals | ||
$L^aT_eX$ | LaTeX | ||
:::info This is a alert area. ::: |
This is a alert area. |
On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?
Please give us some advice and help us improve HackMD.
Do you want to remove this version name and description?
Syncing
xxxxxxxxxx
2023q1 Homework1 (lab0)
contributed by <
tseng0201
>詳閱作業規範!
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →開發環境
完成 queue.c
q_new
建立新的
鏈結串列佇列鏈結串列是用來實作佇列的機制,而
queue.h
正是封裝一系列佇列操作,因此你該用「建立佇列」來說明q_new
的作用,並指明是藉由 List API 達成。- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →q_delete_element
自定義函式用於刪除整個節點,先將節點移出鏈結串列接著釋放掉該節點其值 (node->value) 所指向的記憶體空間,最後釋放節點本身的記憶體空間
void q_delete_element(struct list_head *node)
q_free
將該鏈結串列所使用的記憶體空間詮釋放,使用 list_for_each_safe () 進行鏈結串列的走訪, safe 指標會額外保存 ptr 指標指向的下一個節點 (ptr->next) 因此可對 ptr 指表所指向的節點進行刪除,且不影響鏈結串列的走訪
q_insert_head
對鏈結串列的開頭插入一個節點,每次執行 malloc 函式時都要確定使否成功配置記憶體,建立新節點後使用 list_add() 進行插入
q_insert_tail()
對鏈結串列的尾部插入一個節點,方法同 q_insert_head 但將 list_add() 改為 list_add_tail()
q_remove_head
移除鏈結串列開頭的第一個節點,同時使用 stencpy 函式保存該節點的值,將 bufsize - 1 設為 '\0' 是避免當節點的值長度大於 bufsize 時 stencpy 函式不會自動補 '\0'
q_remove_tail
移除鏈結串列開頭的第一個節點,方法同 q_remove_head,但將 list_first_entry() 改為 element_t *node = list_last_entry(head, element_t, list);
q_size
取得鏈結串列的長度,使用 list_for_each 對鏈結串列進行走訪,每走訪一個節點將 size+1
q_delete_mid
刪除鏈結串列中間的節點,根據 leetcode 描述中間點定義為 \(⌊n / 2⌋^th\),n 為 index 從 0 開始計算,本方法透過快慢指標,快指標走兩步慢指標走一步,當快指標無法完整走兩步時慢指標正好指著中間節點
q_delete_dup
從已排序的鏈結串列中刪除值重複的節點,將 ptr 指標指向節點的值與下一個節點 (ptr->next) 的值比較,如果值相同就刪除下一個節點並將 kill_self 設為 true,當 ptr 指標指向節點的值與下一個節點的值不同時將 ptr 指標移至下一個節點,隨後檢查kill_self 是否為 true,如過 kill_self 為 true 刪除 ptr->prev
q_swap
將鏈結串列的節點兩兩進行順序交換,透過 ptr 指標將 ptr->next、ptr->next-next 兩個節點進行順序交換,最後將 ptr 指標移至 ptr->next-next 對下兩個節點進行位置交換,直到剩下 0 個或 1 個未被交換的節點便停止
q_reverse
將結串列中的節點順序反轉,透過走訪 list 並將每個節點的 next 與 prev 指向的位置交換
q_reversek
以 K 個節點為一組,進行順序反轉,如果剩下未處理的節點列少於 K 就不進行動作
merge_two_sorted_list
參照 你所不知道的 C 語言: linked list 和非連續記憶體 將兩條已排序的鏈結串列合併為一條已排序的鏈結串列
q_sort
對鏈結串列中節點的值進行排序
閱讀 lab0 作業說明Linux 核心的鏈結串列排序,仿造文中提到的方法進行 merge sort
q_descend
從頭開始走訪鏈結串列,當該節點的所有右側節點中有一個節點的值大於該節點便刪除該節點,透過 top-down 作法從尾巴開始往頭部檢查,當 ptr 指向的節點其值小於 上一個節點 (ptr->prev) 的值時時刪除上一個節點反之移動 ptr 至 ptr->prev
閱讀 論文〈Dude, is my code constant time?〉
該篇論文提出一個新的工具 dudect 透過統計方法,在不受硬體差異的限制下,偵測目標函式是否為常數時間的實作。
dudect 透過兩種不同的輸入:
固定輸入(Fixed Inputs), 論文建議可以挑選一些可能該函式能處理較快速的 Corner Case,
隨機輸入(Random Inputs), 每次的輸入都不相同,以隨機產生
當目標函式對固定輸入與隨機輸入所花費的時間分佈有顯著差異,便可推翻虛無假說(目標函式為常數時間的實作),反之無法拒絕該虛無假說
dudect 的執行步驟如下:
使用固定與隨機兩種輸入,重複數次進行時間的測量
由於時間的測量可能會因為中斷發生導致導致數據不準確,因此需要刪除部份測量結果,降低離群值的影響,論文中提到的方法如下
Cropping: 設定一個閥值,將大於閥值的測量結果捨棄,論文實做,透過 qsort 將測量結果由小到大排列,取得第 數據量 * p 個測量結果作為閥值, p 為 0 ~ 1 的值
Higher-order preprocessing:根據不同的數據使用不同的前處理
透過統計學方法,判斷兩種輸入花費的時間分佈是否一致,論文中提到的方法如下
t-test:判斷兩種輸入是否有相同的平均值,論文使用 online Welch’s t-test with Welford’s variance computation 方法進行實作當計算出的 t 值小於 4.5 時,無法拒絕虛無假設,及兩種輸入來自相同的分佈故,並可推測該函式為常數時間的實作
為 lab0 c 改進 dudect
TODO
目前的實作為將 random_string 改為 fixed_string 使隨機部份只在於佇列的長度,但仍然無法保證 trace-17-complexity 的測試,需要再理解 percentile 後處理對於數據的影響
改進 random_string 對測量時間的影響
參考 yanjiew1 同學的共筆後開始研究 lab0-c 中 measure 函式與 prepare_inputs 函式的實作,嘗試尋找改進空間
prepare_inputs 程式碼如下
prepare_inputs 函式有以下幾個階段
接下來看看 measure 函式(只顯示 case DUT(insert_head) 部份)
透過 case DUT(insert_head) 步驟可推知每次測量數據時會經過以下步驟
步驟 3 每次產生不同大小的佇列,來測量指定操作是否可在常數時間完成
而 [yanjiew1] 同學於共筆提到,因為 randombytes 函式有可能產生數值 0,導致 random_string 陣列中每個 string 的長度會不相同,導致無法正確判斷函式是否為常數時間的實作,同時觀察 DUT(remove_head) 與 DUT(remove_tail) 部份,執行remove 時的參數為 (l, NULL, 0),這代表在進行 remove 函式的測量時,並不會考量到記憶體的複製操作,不受隨機長度 string 的影響因此我認為應該將 random_string 改為定值,使其與 remove 函式有相同的測試基準。
透過上述的解析,可推得 lab0-c 對於 measure 的實現,其隨機的部份應該在於每次進行指定操作時的佇列長度不一進行操作,為此我認為應該將 random_string 改為定值
疑問
在看完 Dude, is my code constant time 後,經過 lab0c 的提示下,決定在 lab0c 中實現 percentile 後處理功能,在參考數位同學如 alanjian85、chiangkd 的筆記後,都有提到加入 percentile 功能後,程式便可以通過 trace-17-complexity,但我追蹤 ducet 的原始碼在 src/dudect.h 的 report() 後產生了疑問,不了解為什麼實現 percentile 後處理功能後便可以正確測量函式實作是否為常數實作
首先 src/dudect.h 的 report() 程式碼如下
由判斷式 if (max_t > t_threshold_bananas) 可得知 dudect 是用 max_t 與 t_threshold 判斷是否為常數時間,max_t 由 max_test 函式取得,而 max_test 函式程式碼如下:
由上述程式碼可得知 max_test 會將不同前後處理中擁有最大的 t 值的數據作為回傳值,為了了解有那些不同的後處理數據需要了解 update_statistics 函式,其程式碼如下
可以看到 ctx->ttest_ctxs[0] 是儲存不經過 percentile 後處理的數據,而ctx->ttest_ctxs[1 ~ DUDECT_NUMBER_PERCENTILES] 才會儲存經過 percentile 後處理的數據,因此可以得知 max_test 函式在選擇最大的 t 值時也會考慮沒有後處理的數據,那麼為什麼在原本 Lab0-c 的實現中未經過後處理的數據 t 值會大於閥值,而加上 percentile 後處理後,未經過後處理的數據 t 值就會小於閥值(假設未經過前處理的數據的 t 值會最大)
追蹤 alanjian85 同學的 github 可看到其動如下
其用 t_ctxs[0]算出來的 t 值作為 max_t 的值,而非使用 max_test() 的回傳值 t,代表 max_test 函式毫無作用,在追蹤 update_statistics 可以發現以下改動
其中 t_ctxs[0] 的確是未經過 percentiles 前處理的數據,最後比較 t_ctxs[0] 相關的差異如下
尚無法了解其改動後與原 lab0-c 實現的差異。
TO DO
滿足 make test 自動評分系統的所有項目
目前只差 trace-17-complexity 無法通過且每次結果都不一樣,翻閱其他同學的筆記後發現目前判斷的程式有問題,無法正確判斷實作是否為常數時間,需詳閱論文〈Dude, is my code constant time?〉 與其程式碼後,對判斷程式進行修正再對 trace-17-complexity 進行測試
比較自己實作的 merge sort 和 Linux 核心程式碼之間效能落差
尚無法理解如何利用二進位的 0、1 判斷 \(3 \times 2^k\) 是否發生,預計透過 perf 進行效能落差比較
改善 web 命令
尚未開始研究
提供 shuffle 命令並以統計的原理來分析實作,並探究洗牌的「亂度」
尚未開始研究
設計一組新的命令或選項,得以在 qtest 切換不同的 PRNG 的實作
尚未開始研究