# 2016q3 Homework1 (phonebook) contributed by <`petermouse`> ### Reviewed by <`jkrvivian`> * 可以嘗試引入不同的hash function 進行效能比較 * 還有其他的方法可以實驗,如:字串壓縮法 * 還未引進符合現實狀況的 data set實驗 * 改良版本v2貼上的每段程式碼,加入說明會比較清楚 * 參考[How to Write a Git Commit Message](http://chris.beams.io/posts/git-commit/),Title以大寫開頭 * [8dc5a4f](https://github.com/petermouse/phonebook/commit/8dc5a4f76df6be71b1aa8d6e1f4522d829436246)從標題看不出在哪裡做了甚麼修改 ## 開發環境 * OS:Lubuntu 16.04 LTS * Memory: 8 GB * CPU: * Name: * Intel(R) Core(TM) i5-4200H CPU @ 2.80GHz * Cache: * L1d Cache: 32K * L1i Cache: 32K * L2 Cache: 256K * L3 Cache: 3072K * Frequency: * CPU max MHz: 3400 * CPU min MHz: 800 測試時governor皆設定為performance `$ cpupower frequency-set -g performance` (需root權限) Frequency: > 2.8 GHz ## Perf測試 根據[Linux 效能分析工具: Perf](http://wiki.csie.ncku.edu.tw/embedded/perf-tutorial) `perf_top_example.c`做測試 ``` Samples: 1K of event 'cycles:pp', Event count (approx.): 951215754 Overhead Shared O Symbol 99.40% test [.] compute_pi_baseline 0.41% [kernel] [k] _nv014159rm 0.06% [kernel] [k] _nv014096rm 0.06% [kernel] [k] pci_conf1_read 0.06% [kernel] [k] __internal_add_timer 0.01% [kernel] [k] entry_SYSCALL_64_fastpath 0.00% [kernel] [k] native_write_msr_safe ``` >「程式輸出的文字訊息」請勿使用圖片表示,請改由文字貼上[name=課程助教] >不好意思,已更正[name=petermouse] 大部分的執行時間都花在了`compute_pi_baseline`上 ## Makefile ing... --- ## Phone Book ### 原始版本 不修改code,執行`make cache-test`看cache miss rate 結果: ``` Performance counter stats for './phonebook_orig' (100 runs): 1,024,374 cache-misses # 85.793 % of all cache refs ( +- 0.26% ) 1,194,011 cache-references ( +- 0.30% ) 260,843,909 instructions # 1.38 insns per cycle ( +- 0.02% ) 188,829,353 cycles ( +- 0.51% ) 0.057309105 seconds time elapsed ( +- 0.92% ) ``` 100次平均時間 ``` append() 0.037041 findName() 0.005024 ``` ### 改良版本v1 在原始版本中,因為struct太大,導致cache中最多能夠存的資料量極少,cache miss機率高,影響到效能,因此最直接的作法是改良struct的大小。 將`phonebook_opt.h`資料結構改變。 ```clike= typedef struct __PHONE_BOOK_DATA{ char firstName[16]; char email[16]; char phone[10]; char cell[10]; char addr1[16]; char addr2[16]; char city[16]; char state[2]; char zip[5]; }data; typedef struct __PHONE_BOOK_ENTRY { char lastName[MAX_LAST_NAME_SIZE]; struct __PHONE_BOOK_DATA *pData; // a pointer to other data struct __PHONE_BOOK_ENTRY *pNext; } entry; ``` 而`phonebook_opt.c`與`phonebook_orig.c`大致相等。 結果 ``` Performance counter stats for './phonebook_opt' (100 runs): 139,238 cache-misses # 46.657 % of all cache refs ( +- 0.35% ) 298,427 cache-references ( +- 0.45% ) 244,614,107 instructions # 1.93 insns per cycle ( +- 0.02% ) 126,809,543 cycles ( +- 0.40% ) 0.038456267 seconds time elapsed ( +- 0.51% ) ``` 100次平均時間 ``` append() 0.030186 findName() 0.001915 ``` 長條圖 ![](https://i.imgur.com/TKbwS1o.png) Cache miss 大幅降低,但仍有45% 而append()與findName()時間都減少了 我並沒有分配空間給其他資料,所以還不能存取 ### 改良版本v2 因為資料多達35萬筆,且採用linked list結構,萬一資料在linked list尾端,每次重頭搜尋會花太多時間。 解決辦法:引入hash(使用djb2),pHead也改為陣列。 以下為幾個程式碼片段(main.c) ```clike= #define PRIME_NUM 42737 ``` ```clike= unsigned int Hash(char *str) //djb2 { unsigned long hash = 5381; int c; while ((c = *str++)) hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ return hash%PRIME_NUM; } ``` ```clike= pHead = (entry *) malloc(sizeof(entry)*PRIME_NUM); ``` ```clike= e=pHead+Hash(line); e=append(line, e); ``` 結果: ![runtime_comp3.png](https://i.imgur.com/hnUHyI1.png) 可以發現append會比較花時間,而findName幾乎瞬間完成,原因是append需要經過hash才能append,findName反而透過hash使得總體要找的linked list資料變少,幾乎瞬間完成。 因為測資只有一筆資料(zyxel),如果有多筆資料的查詢,findName會節省更多時間,將會比前兩組有更好的效能 ### 改良版本v3 待續 ## 參考資料 [課程video](https://www.youtube.com/watch?v=ZICRLKf_bVw) [Linux 效能分析工具: Perf](http://wiki.csie.ncku.edu.tw/embedded/perf-tutorial) [使用gnuplot科學作圖](http://www.phy.fju.edu.tw/~ph116j/gnuplot_tutorial.pdf)