contributed by < tseng0201
>
詳閱作業規範!
jserv
建立新的鏈結串列 佇列
鏈結串列是用來實作佇列的機制,而 queue.h
正是封裝一系列佇列操作,因此你該用「建立佇列」來說明 q_new
的作用,並指明是藉由 List API 達成。
jserv
自定義函式用於刪除整個節點,先將節點移出鏈結串列接著釋放掉該節點其值 (node->value) 所指向的記憶體空間,最後釋放節點本身的記憶體空間
void q_delete_element(struct list_head *node)
將該鏈結串列所使用的記憶體空間詮釋放,使用 list_for_each_safe () 進行鏈結串列的走訪, safe 指標會額外保存 ptr 指標指向的下一個節點 (ptr->next) 因此可對 ptr 指表所指向的節點進行刪除,且不影響鏈結串列的走訪
對鏈結串列的開頭插入一個節點,每次執行 malloc 函式時都要確定使否成功配置記憶體,建立新節點後使用 list_add() 進行插入
對鏈結串列的尾部插入一個節點,方法同 q_insert_head 但將 list_add() 改為 list_add_tail()
移除鏈結串列開頭的第一個節點,同時使用 stencpy 函式保存該節點的值,將 bufsize - 1 設為 '\0' 是避免當節點的值長度大於 bufsize 時 stencpy 函式不會自動補 '\0'
移除鏈結串列開頭的第一個節點,方法同 q_remove_head,但將 list_first_entry() 改為 element_t *node = list_last_entry(head, element_t, list);
取得鏈結串列的長度,使用 list_for_each 對鏈結串列進行走訪,每走訪一個節點將 size+1
刪除鏈結串列中間的節點,根據 leetcode 描述中間點定義為 ,n 為 index 從 0 開始計算,本方法透過快慢指標,快指標走兩步慢指標走一步,當快指標無法完整走兩步時慢指標正好指著中間節點
index | 0、1 | 2、3 |
---|---|---|
對應的 middle | 0 | 1 |
從已排序的鏈結串列中刪除值重複的節點,將 ptr 指標指向節點的值與下一個節點 (ptr->next) 的值比較,如果值相同就刪除下一個節點並將 kill_self 設為 true,當 ptr 指標指向節點的值與下一個節點的值不同時將 ptr 指標移至下一個節點,隨後檢查kill_self 是否為 true,如過 kill_self 為 true 刪除 ptr->prev
將鏈結串列的節點兩兩進行順序交換,透過 ptr 指標將 ptr->next、ptr->next-next 兩個節點進行順序交換,最後將 ptr 指標移至 ptr->next-next 對下兩個節點進行位置交換,直到剩下 0 個或 1 個未被交換的節點便停止
將結串列中的節點順序反轉,透過走訪 list 並將每個節點的 next 與 prev 指向的位置交換
以 K 個節點為一組,進行順序反轉,如果剩下未處理的節點列少於 K 就不進行動作
參照 你所不知道的 C 語言: linked list 和非連續記憶體 將兩條已排序的鏈結串列合併為一條已排序的鏈結串列
對鏈結串列中節點的值進行排序
閱讀 lab0 作業說明Linux 核心的鏈結串列排序,仿造文中提到的方法進行 merge sort
從頭開始走訪鏈結串列,當該節點的所有右側節點中有一個節點的值大於該節點便刪除該節點,透過 top-down 作法從尾巴開始往頭部檢查,當 ptr 指向的節點其值小於 上一個節點 (ptr->prev) 的值時時刪除上一個節點反之移動 ptr 至 ptr->prev
該篇論文提出一個新的工具 dudect 透過統計方法,在不受硬體差異的限制下,偵測目標函式是否為常數時間的實作。
dudect 透過兩種不同的輸入:
固定輸入(Fixed Inputs), 論文建議可以挑選一些可能該函式能處理較快速的 Corner Case,
隨機輸入(Random Inputs), 每次的輸入都不相同,以隨機產生
當目標函式對固定輸入與隨機輸入所花費的時間分佈有顯著差異,便可推翻虛無假說(目標函式為常數時間的實作),反之無法拒絕該虛無假說
dudect 的執行步驟如下:
目前的實作為將 random_string 改為 fixed_string 使隨機部份只在於佇列的長度,但仍然無法保證 trace-17-complexity 的測試,需要再理解 percentile 後處理對於數據的影響
參考 yanjiew1 同學的共筆後開始研究 lab0-c 中 measure 函式與 prepare_inputs 函式的實作,嘗試尋找改進空間
prepare_inputs 程式碼如下
prepare_inputs 函式有以下幾個階段
透過 case DUT(insert_head) 步驟可推知每次測量數據時會經過以下步驟
而 [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 實現的差異。
目前只差 trace-17-complexity 無法通過且每次結果都不一樣,翻閱其他同學的筆記後發現目前判斷的程式有問題,無法正確判斷實作是否為常數時間,需詳閱論文〈Dude, is my code constant time?〉 與其程式碼後,對判斷程式進行修正再對 trace-17-complexity 進行測試
尚無法理解如何利用二進位的 0、1 判斷 是否發生,預計透過 perf 進行效能落差比較
尚未開始研究
尚未開始研究
尚未開始研究