contributed by < komark06 >
大約十年前的舊電腦,實體記憶體只有 2GB 再加上 SWAP 2GB,總共為 4GB。作業系統為 Lubuntu。
q_new
透過 malloc
配置記憶體,並用 INIT_LIST_HEAD
讓佇列指向自己。
q_free
透過 list_for_each_entry_safe
走訪佇列,避免釋放節點後,無法走訪下一個節點。單一節點可以透過 q_release_element
來釋放,但是由於實作了 e_new
來配置新節點,將 q_release_element
包裝在 e_release
。
q_insert_head
透過 e_new
來配置新節點,並用 list_add
插入在佇列開頭。
研讀 Linux man-pages,撰寫出更精簡的程式碼。
commit 7b453b9 已修正
透過 strdup
來複製字串,可讓程式碼更精簡。
q_insert_tail
q_insert_head
和 q_insert_tail
只有插入位置的不同,透過將尾端節點當作是佇列本身即能重用 q_insert_head
。
q_remove_head
透過 q_empty
檢查佇列是否為空或是 NULL
,再移除佇列開頭的節點。需要注意的是,由於 bufsize
小於欲刪除的字串長度的情況下,字串結尾的 '\0'
並不會複製到,必須加上去。
q_remove_tail
類似於 q_remove_head
,把佇列的倒數第二個節點作為佇列本身,便會移除佇列結尾的節點。
q_size
走訪佇列來計算佇列大小。
q_delete_mid
透過快慢指標來找尋佇列的中間節點並釋放。
q_delete_dup
透過 list_for_each_entry_safe
走訪佇列,若發現當前節點和下一個節點的字串相同,則移除當前節點並紀錄下一個節點到 ready
,等到兩字串不相同或是佇列走到最後一個節點時,再釋放 ready
。
q_reverseK
dummy
: 用來反轉節點用的另一格佇列。start
: 紀錄切分佇列和連結佇列的位置下圖忽略最後節點和 head 的連結
假設有六個節點,要以三個節點為單位反轉 (k=3),當 count == 3
時, cur
會指向 3
。
執行完 list_cut_position(&dummy, start, cur)
,會形成兩個佇列。
透過 list_move
反轉。
最後,透過 list_splice_init(&dummy, start)
將兩個佇列連結成一個佇列,並更新 start
。
q_swap
q_swap
是兩個節點互換,等同於 q_reverseK(head,2)
。
q_reverse
q_reverse
反轉整個佇列,等同於 q_reverseK(head, q_size(head))
,但這意味著要走訪兩次佇列,應不依賴 q_reverseK
來實作 q_reverse
。
透過 list_for_each_safe
走訪佇列,並用 list_move
,將後方的節點逐個移動到佇列開頭。
commit ef3cfa7 修正。
專業術語避免引用 Wikipedia 中文條目,除非沒有對應的英語條目,前者通常落後於英語條目。
已更新成英語條目
使用 merge sort 來實作 q_sort
。拆分佇列時,除了從中間分割以外,也將雙向鏈結改為單向鏈結,直到合併後才回復雙向鏈結。
升序或降序則透過傳入函式指標來控制, e_cmp_func
做為 helper function 判斷是升序或降序,並回傳相對應的函式指標。
q_ascend
和 q_descend
由於q_ascend
和 q_descend
只差別在升序和降序,透過實作 q_remove_by_order
,讓程式碼得以重用。
q_remove_by_order
從尾部走訪佇列並用 descend
來決定升降序,決定節點是否要刪除。
q_remove_order
可能會讓人誤會是「移除某種順序設定」,但其實你要表達 "by order"
commit c04aced 已修正
將所有佇列合併成一個佇列後,再用 q_sort
排序。
這篇論文介紹了 dudect,dudect 透過對目標平台上執行時間測量的統計分析, 從而檢測程式碼的時間複雜度是否為常數時間。
減少項目縮排,控制在三層,以便於協作。
已修正縮排
首先,我們通過 fix-vs-random 測試來反覆測量函式的執行時間,以捕捉潛在的洩漏。在這種測試中,我們會使用兩種不同的輸入類別:一種是固定值,另一種是每次測量前隨機生成的值。為了避免可能的並行執行導致的干擾,交替使用兩個類別並隨機排列它們的順序。
測量方式:現代 CPU 提供的 cycle counter
一般來說,時間分布會呈現正偏態(分布的主體集中在左側),是因為主程序可能被作業系統中斷或是外部活動影響。在此情況下,截斷大於固定的、與類別無關的閾值的測量數據可能會更方便。
應用統計檢驗來嘗試反駁虛無假說「兩個時間分佈相等」。
使用 Welch’s t-test 來檢定兩個母體的平均是否相等,如果測試失敗就能驗證函式的時間複雜度不是常數時間。
commit 16516c3 已加入 percentile
測試函式為 test_const
,透過 X macro 擴展 test_const
,來測試 q_insert_head
、 q_insert_tail
、 q_remove_head
、 q_remove_tail
。
使用 prepare_inputs
來產生隨機字串、每次插入或移除節點的次數、隨機排序兩個資料輸入的順序。
使用 prepare_inputs
產生的資料,傳入到 measure
後,開始測量。
此步驟為 lab0-c 缺乏的部分。
透過 prepare_percentiles
來裁剪數據,並用 update_statistics
算出多個 t 值並找出最大的 t 值,若 t 值大於 t_threshold_moderate
,則拒絕虛無假說,說明被測式的函式的時間複雜度並非常數時間。
dudect 的文件中並未詳細說明 prepare_percentiles
函式的理論基礎,僅簡要提及其目標是使數據大致符合測量分布。
最初的想法是將 linenoise 的 Asyncrhronous API 加入 lab0-c 後,搭配 select ,便能在處理網頁請求的同時,還能使用 linenoise 的功能。
清楚標示 GitHub 帳號名稱。
已標示清楚
不過 vax-r 先行在 commit b13335b 提交相關功能。故目前以改善此實作為目標。
由於 lab0-c
的 linenoise
設計,是阻塞直到使用者輸入 Enter
、 Ctrl+C
或 CTRL+D
(無任何輸入的情況下), vax-r 的作法是透過在 line_edit
納入 callback , 也就是 web_eventmux
,阻塞直到有網路請求或是命令列有輸入。
現有實作的問題:
200 OK
的狀態碼,而不知道佇列的狀態。Ctrl+D
並不會結束程式。與未啟用網頁伺服器的情況不同。一開始的想法,將 web_eventmux
移至 console.c
並呼叫 interpret_cmd
執行命令後,再關閉連結,網路客戶端就可以看到命令執行後的結果。但本地命令列出現格式錯誤,如下面所示:
這是因為呼叫 interpret_cmd
時, 終端仍處在 raw mode ,導致 linenoise
仍以為使用者還未輸入命令,此外, raw mode 會關閉 OPOST,使 \n
並未轉換成 \r\n
,可見第八行。
思考如何提供對應的測試程式。
使用 Popen 來執行 qtest
並啟用網頁伺服器,再用 urlopen 來驗證網頁伺服器是否有正常運作,最後,試圖將程式碼融入到 driver.py
目前碰到的問題:
web
後,會進入無限迴圈,也是因為上述原因。對於如何測試,目前沒有太多想法
test_calloc
test_calloc
目前尚未將block_element_t
嵌入到配置的記憶體中,故無法追蹤記憶體的使用情況,例如:妥當地釋放記憶體、記憶體是否有越界存取等情況。
test_malloc
和 test_free
會用 FILLCHAR
填滿 payload
做初始化,而 calloc
則是用 0 。等你的 pull request!
console.c
cmd_select
不再需要多工處理網路請求和使用者輸入。故移除相關程式碼並縮減參數。block_timing
只受到 block_flag
的影響,所以也可以移除相關的條件判斷和變數本身。