contributed by <rwe0214
>
rwe0214
首先依照作業要求找到註解內容 FIXME
的地方,並更改以下程式碼:
為
註:
REF
和CPY
變數於程式開頭便以enum
定義,分別為0
和1
執行結果:
發現執行出來的結果錯誤的離譜,而且打印出來的字串也一樣,這其中一定有什麼誤會。於是我去觀察了 tst.h
和 tst.c
中關於此函式的定義
而我更動的參數影響的程式碼其實不多,如下幾行:
可以發現因為我傳入 REF
值為 0
( 即cpy
為 0
),所以程式並不會進入 9~13 行的 if
而是第 14~16 行的 else
執行。
這裡的差別透過註解可以得知 if
區塊會分配一個新的記憶體空間給新的 tst_node 並把值複製進去,而 else
部份則是直接指向傳入的字串 s
位址。
回到我們的程式碼 test_ref.c
,可以發現從讀檔建立 tst_tree,到最後程式使用 polling 的方式等待使用者輸入字串比對資料皆是使用同一個變數 word
,所以如果我們使用 ref 方式建立這個 tst_tree 就會發生每個節點都指向同一塊記憶體位置,所以當這個位址的資料一改變整棵 tst_tree 的數值也就不正確了,這也是為什麼我們的執行結果會跟我們最後輸入的字串 Taipe
都相同的原因。
為了證實這個想法,我將 test_ref.c 做了修改,觀察是否都是參照到相同的記憶體 ( 8b4ddca
)。
執行結果:
可以看到所有的 key 指向的位址皆一樣,也證實了我的想法。
既然已經確定問題是出在全數節點都指向相同的記憶體位置,那麼我在建立 tst_tree 時,只要把每個讀入的字串先行配置記憶體給它就好了阿!這樣每個節點便都會指向不同記憶體也不會造成程序出錯了。
但是仔細想了想,這樣的步驟和使用 cpy
的方法 assign 一塊記憶體給節點有什麼不同呢?
回想第一次的作業
phonebook
,好像有做過類似的修改,把一個 struct 的值挪去另一個記憶體空間,再用一個 pointer 指向這塊記憶體。如此一來不但縮小整個 struct 的大小進而減少了 cache miss rate。
沒錯,這個部份和正是 cpy
和 ref
的不同,一個是在 node 內新增一塊記憶體儲存資料,一個則是新增一個 pointer 去指向儲存的資料,根據作業一的想法,在 cpu 使用率上應該會因為減少 cache miss rate 而大幅提昇。
於是我觀察原本的程式碼在 cpy
的情形下是怎麼分配記憶體的
使用的是 strdup()
以字面上來看好像只是 string duplicate 的意思怎麼會有記憶體分配呢?
查詢 Linux Programmer's Manual 後發現:
原來這個函式背後已經偷偷幫我們把記憶體空間給 malloc
出來了,既然如此,我決定如法炮製這個記憶體配置方式並觀察其執行結果如何 (32d6bd9
):
可以看到記憶體位置已無重複且程式已經可以正確執行。
接著就是比較 cache miss 的部份 (62b771e
):
WHAT THE …???
沒想到竟然和預期的結果有點落差,cpy 和 ref 的 cache miss 竟然相差不遠或者說幾乎一樣,這部份可以能需要再思考幾天了QAQ
反覆思考一會兒,發現我好像對 hw1 的結果有點斷章取義了, hw1 之所以改成 pointer 會降低 cache miss 的原因是因為 pointer 指向的資料是並不是搜尋時會使用到的資料 ( key ),而這次的作業 pointer 指向的資料則是搜尋時會用到的 key ,所以 cpu 會多一個從 pointer 的位址再載入資料的步驟。這一來一往也讓 cache miss 相差不遠。