2017q3 Homework1 (phonebook)
contributed by <twngbm>
系統環境
原始碼閱讀-opt.txt輸出問題
-
一開始用make plot的時候,畫出來的兩張圖長的一樣
-
資料夾裏面只產生一個orig.txt檔,預期中的opt.txt並沒有產生
-
進入main裏面看到
-
但是上面有沒有看到 OPT的define,而且phonebook.h也沒有被include進來
-
看到main 裏面的IMPL
- 根據網路上的資料,-DIMPL會把 main.c裏面的 IMPL替換成後面給定的值,因此上面的指令會變成
- 最後我們進入phonebook_opt.h裏面,我們可以發現
- 將其取消註解即可正常輸出opt.txt和正常plot
Orig輸出
可以看到高達93.015%的cache misses
優化1-減少struct大小
-
這裡定義了entry是一個linked list 的結構,每一個節點裡面都存有上面列出的所有資料(last name,first name,email…)。每一個節點的大小是136bytes。
-
我們一個list的結構大小是136bytes,我電腦的L1 cache 是32K,因此我頂多只能放32x1024/(136X8)=30.12,30個entry在cache裏面,但是我們的基數有35萬個,因此cache-miss一定會很常發生。(引用自programming small)
- 嘗試將不需要處理的資訊移出原本的資料結構,並用一指標儲存
- 因為我們的搜尋是用lastname來去搜尋的,因此我把lastname獨立出來做一個結構,再用指標指向詳細資料的結構
- 執行後我們發現entry的大小變成32bytes,因此可預期cache的內可存放的entry會變多。
可以發現cashe-miss的情況減少了27%
從圖片中也可以發現append和find執行時間有所減少
Image Not Showing
Possible Reasons
- 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 →
優化2-hash table
- 希望能利用hash的方法來解決linked list的循序搜循的時間複雜度為O(n)的情況,利用hash表來解決這個問題
- 本例利用BKDR hash,seed 選131,bucket大小選擇是4093
- 首先先引入hash function
- 這邊我們定義SIZE的大小為4093,SIZE的意義為Hash table的大小,同時也是hash bucket的數量
- 修改main.c
- 在這邊我建構 an array of pointer to struct entry
- entry是利用優化1中,減小的data struct 來實作
- 建構完array之後,我就可以利用hash functin return 回來的key值,直接存取在array中的記憶體位址中的變數
- 此處中array所存放的變數為pointer to struct entry,因此我只要利用 hash_table_index[key]->lastName即可取出lastName資料
- 建構append()和findName()
- 為了解決collision的情況,因此我使用linked list來儲存同一個key的所有資料
- struet entry 中保留 struct __PHONE_BOOK_ENTRY *pNext
- 原來的作法是,進入linked list之後,一一比較每個*pNext的值直到list的末端(pNext==NULL)
- 耗時過長因此更改作法
- 後來更改作法為,直接在linked list 的頭,插入新的資料節點
- 將hash_table_indes[key]的pointer指向新的節點,將新的節點的pNext指向後方原來存在的linked list
- 35行判斷此新增的資料結構是否為新的節點或是在bucket中已有其他資料結構,來判斷是否指向NULL或是原有的linked list
- 透過hash function算出key值後,透過array的存取來進入linked list中的第一個節點,並判斷字串是否相同,不相同則進入第二個節點
- 如果有找到則回傳1
- 結果
- 首先先比較執行時間(原版,結構優化,Hash)

關於append time
- 優化後的資料結構,在進行append的時候,因為資料結構較小,因此fetch進cache時,可以減少cache miss的機會,因此執行時間較快
- hash雖然是使用優化後的資料結構,但是因為必須進行hash運算,所以整體的append時間約莫跟原版的一樣,但是還是比原版略快一點(某些情況下可以差到5%左右的時間)
關於findName time
- 優化後的資料結構,因為size較小,所以cache miss較少,固執行速度比原版快
- 經過hash之後,search 的average time變成Θ(1),雖然有linked list的Θ(n)存在,但是整體的搜尋時間已經變成線性的了,根據實測,每個bucket中的數量大概在80~100之間
- 建構hash_table的缺點是,需要額外得記憶體空間來儲存hash table,本例使用hash table 內的儲存內容為pointer 因此額外的記憶體空間為8*SIZE bytes。
hash中整體的cache miss影響在於bucket 數量的影響,實測過當bucket=32時,cache misses rate比bucket=4096時高了5%左右
QA
本例選用的 dataset (也就是輸入電話簿的姓名)有代表性嗎?跟真實英文姓氏差異大嗎?
本例子選用的資料,與真實英文姓名相差頗大。
但是作為phonebook的dataset,必須考慮有人可能在新增資料時,就不注重名字的正確性,有些人可能只是想要儲存一個自己能夠看懂的資料進去。
如果資料庫的大小越大,那麼我們就需要更符合資料庫結構的演算法來幫助我們儲存資料,甚至必須考慮,針對不同的search key去建立不同的資料結構,來符合快速的搜尋目的。
如果我們要建立符合真實電話簿情境的輸入,該怎麼作?
- 如果是像本案例,由一排序過的檔案進行輸入,則可以使用hash來進行append
- 如果是隨機輸入,則可以使用TST來進行append,並利用system idle time來進行balance。
Reference