contributed by < quantabase13
>
說明
實作 queue,此 queue 能從 head 處插入 element。
q_new
- 新增空的 queueq_free
- 清除整個 queueq_insert_head
- 在 queue 的 head 前插入元素q_insert_tail
- 在 queue 的 tail 後插入元素q_remove_head
- 取出並移除 queue 的第一個元素q_size
- 給出 queue 中的元素個數q_reverse
- 反轉整個 queue ,並且只使用原本 queue 中的 elementq_sort
- 對 queue 進行遞增排序(時間複雜度:O(nlog(n))queue_t
,新增 size
和 tail
這兩個 member
q_size
和 q_insert_tail
的時間複雜度限制為 O(1)。透過這種方式可以讓 q_size
直接返回 size
的值 ; 讓 q_insert_tail
不需計算 tail 的位置。q_sort
部份)
malloc
是否有成功分配記憶體free
q_sort
實作相關)
q_size
q_size
確認傳入的 pointer 不為 NULL 後才對其取值。同時透過為 queue_t 加入 size 後可讓所需時間為 O(1)。
一樣根據程式碼中的註解,在實作中加入對記憶體分配失敗時的處理。
利用 next
和 indirect
兩個 pointer 遍歷整個 queue 來釋放記憶體。釋放記憶體的順序為:
*char
pointer 指向的記憶體根據傳進來 string 的長度,動態配置記憶體這裡必須要注意 malloc
的結果是否成功。為了突顯記憶體分配的問題,作業中的 malloc
其實已經被包裝過,在 harness.h
檔案中可以看到如下程式碼:
可以得知 malloc
在作業裡被替換成 test_malloc
。追蹤到 harness.c
檔後,可以看到 test_malloc
的具體實現:
可以看到,在呼叫真正 malloc
之前,多了一個 fail_allocation
函式。
在 harness.c
檔裡同樣可看到 fail_allocation
的實現:
我們從程式碼中可以得知, fail_allocation
代表分配記憶體失敗的機率。在 harness.c
檔裡可以看到, fail_probability
的值預設為0 。
在作業程式碼中搜尋 fail_probaility
時在 q_test.c
有找到以下程式碼:
再搭配用來測試的腳本,以 trace-10-malloc.cmd
的內容為例:
從這些程式碼,我們得知程式執行期間我們可以輸入的命令及參數。以 option malloc 50
為例,透過這個命令,能將 fail_probability
從原本的 0
修改為 50
。
q_insert_tail
的實作與 q_insert_head
基本相同。
這裡要注意的是 bufsize
代表可儲存的最大長度,而非一定要放滿 bufsize
的長度。因此在 for
迴圈中需要加入來源 pointer
指向的 char
是否為 '\0'
的檢查。
q_reverse
的實現在第一周上課的小考就有出現,畫出各個 pointer
指向的變化就能一目了然。
q_sort
我實作了兩次,第一次是用全遞迴的方式:
全遞迴的版本無法通過 trace-15 ,會出現 Segmentation fault (core dumped)
的錯誤訊息。
確認過程式碼不會 dereference NULL pointer
後,推測是遞迴用到的 stack 空間過多導致。
於是第二次參考ZhuMon,將 merge
的部份以迭代實現:
程式碼利用 l1
, l2
指向等待被 merge 的 linked list 的第一個元素 , 利用 cur
這個 pointer to pointer 訪問並修改原本 linked list 元素的 next 欄位
採取部份迭代的方法能順利通過 trace-15 ,但若要從根本上解決問題,程式碼必須全部改成迭代。
is_insert_tail_const
函式負責分析 q_insert_tail
的時間複雜度是不是常數:
比較重要的是 init_once
和 doit
兩個函式。
init_once
負責待測 queue
的初始化,為了避免干擾到真正要進行 q_insert_tail
的 queue
,會另外生成一個 queue
:
doit
則是包含實際負責測量的函式:
prepare_inputs
負責初始化 input_data
、 classes
、 random_string
,其中:
input_data
有150個數,每個數佔一個 chunk_size
,每個數表示對應 string
的插入個數。如果 classes[i]
對應的值為0(代表 fixed case),則對應 input_data
中第 i 個 chunk 的值(插入個數)也會設為0。classes
為長度 150 的 array , array 的第 i 格的值代表第 i 格的 class 。因為只有兩個 class,故只會有 0 、 1 兩種值。random_string
存有 150 個 string ,每個 string 長度為7。measure
負責取得 insert tail 之前和之後的 CPU Cycle 。
另外,在執行 dut_insert_tail
前,有先 insert head。從 random_string
取出 array ,從 input_data
指向的第 i 個 chunk 取出要 insert 的數量 。
differentiate
將其相減後得出 exe_times
,送入 update_statics
, update_statics
會根據第 i 個 measurement 對應的 class (也就是 class [ i ]的值) 進行 t-test。
t_push
負責計算、、
report
中的 t_compute
則會計算出 t
其中:
另外,仔細查看 t_push
的實作部份會發現,每次計算 (也就是)時,所用的都不同,但理論上必須使用全體的才符合統計學上的意義。
於是對以下檔案進行修改:
後來發現wikipedia 上其實有對 Welford's online algorithm 的仔細敘述,並且還有詳細舉出傳統我認知計算 variance 的方法可能會導致 overflow 的問題。
基於上述,以下的修改實屬多餘。
ttest.h
:為 t_ctx
新增 sum[2]
欄位,記錄對應 class 的 total sum 。ttest.c
: 修改 t_push
,只保留計算 的部份,將計算mean的工作交給新增的 t_mean
3.fixture.c
: 把 t_mean
加到程式碼的開頭,一開始就把 mean 給計算出來。
qtest
實作的記憶體錯誤