# 2017q1 Homework1 (phonebook) contributed by <`rayleigh0407`> ### Reviewed by `yachiyang01` - 測試一中削減資料大小的方法,可以嘗試將一些不需要放在struct中的資料獨立出來。 - Commit Message內文應說明是什麼、為什麼而不是如何做。 ### Reviewed by `illusion030` * commit message 寫得不清楚 * 沒有當 malloc 失敗時的防呆 ### Reviewed by `SeanCCC` * 沒有解釋hash table大小怎麼決定的 ## 開發環境 ```shell **Desktop** Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian CPU(s): 8 On-line CPU(s) list: 0-7 Thread(s) per core: 2 Core(s) per socket: 4 Socket(s): 1 NUMA node(s): 1 Vendor ID: GenuineIntel CPU family: 6 Model: 94 Model name: Intel(R) Core(TM) i7-6700K CPU @ 4.00GHz Stepping: 3 CPU MHz: 800.000 CPU max MHz: 4200.0000 CPU min MHz: 800.0000 BogoMIPS: 8016.72 Virtualization: VT-x L1d cache: 32K L1i cache: 32K L2 cache: 256K L3 cache: 8192K NUMA node0 CPU(s): 0-7 **Laptop** Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian CPU(s): 4 On-line CPU(s) list: 0-3 Thread(s) per core: 2 Core(s) per socket: 2 Socket(s): 1 NUMA node(s): 1 Vendor ID: GenuineIntel CPU family: 6 Model: 69 Model name: Intel(R) Core(TM) i5-4210U CPU @ 1.70GHz Stepping: 1 CPU MHz: 1694.531 CPU max MHz: 2700.0000 CPU min MHz: 800.0000 BogoMIPS: 4788.93 Virtualization: VT-x L1d cache: 32K L1i cache: 32K L2 cache: 256K L3 cache: 3072K NUMA node0 CPU(s): 0-3 ``` ## 前置作業 ### Dual system 系統方面,我使用 ubuntu 16.04.1 LTS 因為半年前換電腦時就先灌好雙系統了 所以直接沿用 ```shell rayleigh@rayleigh-System-Product-Name:~$lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 16.04.1 LTS Release: 16.04 Codename: xenial ``` ### Vim 編輯器 大一的時候還覺得很難用 不過現在學了一些基本修改及安裝插件 直接變成 coding 神器阿XD 感謝老師及助教提供的資料 這裡有個小工具能夠改變語法顏色 ```shell Plugin 'octol/vim-cpp-enhanced-highlight' ``` 可以修改一些基本語法的顏色和字體 ```shell hi Character cterm=bold ctermfg=DarkRed ctermbg=NONE hi SpecialChar cterm=bold ctermfg=DarkGreen ctermbg=NONE hi Function cterm=bold ctermfg=DarkBlue ``` 參考資料: [syntax highlight plugin](https://github.com/octol/vim-cpp-enhanced-highlight) [vim syntax](http://vimdoc.sourceforge.net/htmldoc/syntax.html) ### Perf linux 相當強大的效能統計軟體 稍微看一下使用的說明 ```shell $ perf --help usage: perf [--version] [--help] [OPTIONS] COMMAND [ARGS] The most commonly used perf commands are: ... list List all symbolic event types ... stat Run a command and gather performance counter statistics ... top System profiling tool. ... ``` 這次作業主要是使用 stat ```shell $ perf stat --help NAME perf-stat - Run a command and gather performance counter statistics SYNOPSIS perf stat [-e <EVENT> | --event=EVENT] [-a] <command> perf stat [-e <EVENT> | --event=EVENT] [-a] — <command> [<options>] . . *-e, --event= Select the PMU event. -p, --pid=<pid> stat events on existing process id (comma separated list) -a, --all-cpus system-wide collection from all CPUs *-d, --detailed print more detailed statistics, can be specified up to 3 times -d: detailed events, L1 and LLC data cache -d -d: more detailed events, dTLB and iTLB events -d -d -d: very detailed events, adding prefetch events *-r, --repeat=<n> repeat command and print average + stddev (max: 100). 0 means forever. -o file, --output file Print the output into the designated file. --append Append to the output file designated with the -o option. Ignored if -o is not specified. . . ``` 藉由 perf list 可以得知有哪些 PMU event 參考資料 [perf 原理和實務](https://hackmd.io/s/B11109rdg#) [Perf -- Linux下的系统性能調優工具](https://www.ibm.com/developerworks/cn/linux/l-cn-perf1/) ### gnuplot >請在中英文字間加上空白區隔 >[name=課程助教][color=red] >好的, 謝謝助教 參考資料 [gnuplot 語法解說和示範](https://hackmd.io/s/Skwp-alOg#) ### cache 實作前,不得不先了解 cache 這東西 cache 算是一個 cpu 專屬的記憶體 通常取資料時會先從 cache 裡找 這是為了提昇整體運算的效能(畢竟 ram 現在還是慢 cpu 很多) 藉由下列指令,可以得知使用者目前電腦 cache 的資訊 ```shell $ lscpu $ sudo lshw -C memory $ x86info -c(須先安裝x86info) $ sudo dmidecode -t cache ``` 或是開啟 /proc/cpuinfo 檔案,也可以看到該電腦 cpu 的資訊 ```shell $ cat /proc/cpuinfo | grep cache cache size : 3072 KB cache_alignment : 64 cache size : 3072 KB cache_alignment : 64 cache size : 3072 KB cache_alignment : 64 cache size : 3072 KB cache_alignment : 64 (有幾個核心就會顯示幾次) ``` 參考資料 [cache原理和實驗](https://hackmd.io/s/S14L26-_l#) [關於CPU Cache -- 程序猿需要知道的那些事](http://cenalulu.github.io/linux/all-about-cpu-cache/) [Stackoverflow](http://stackoverflow.com/questions/5401464/finding-the-cache-block-size) and [stackexchange](http://unix.stackexchange.com/questions/167038/is-there-any-way-to-know-the-size-of-l1-l2-l3-cache-and-ram-in-linux) ## 開發過程 ### **1. 原始版** 先執行原始程式碼,來看看他的效能如何 ```shell $ perf stat --repeat 100 -e cache-misses,cache-references,instructions,cycles ./phonebook_orig Performance counter stats for './phonebook_orig' (100 runs): 126,1316 cache-misses # 90.600 % of all cache refs ( +- 0.23% ) 139,2178 cache-references ( +- 0.16% ) 2,6076,3251 instructions # 1.45 insns per cycle ( +- 0.02% ) 1,7966,5430 cycles ( +- 0.11% ) 0.068607172 seconds time elapsed ( +- 0.43% ) ``` cache miss 高達90% 目前猜測原因有兩個: 1. **資料量大使用量卻少** 還有一個,正常的phonebook可能本身就有給予多種資料 但在原始碼裡卻只用lastName找資料 其他資料浪費的空間 也可能是導致cache-miss過高的原因 2. **資料儲存方式** 看一下原始碼,資料是以linked list直接串接成一列 要從這麼大的資料鏈(我猜是word.txt : 約350000筆)裡找一筆相符的 可能就是導致cache-miss過高的原因 ### **2. 測試一: 削減資料大小** 先從1開始下手 把 entry 裡除了 lastName 以外的資料另外創一個type 名為 otherData 並在 entry 裡宣告一個指向 otherData的pointer 有需要在分配記憶體給它儲存資料 >就直接把兩個完整的 structure 貼上來吧! >[name=課程助教][color=red] ```c= ... typedef struct __PHONE_BOOK_OTHER_DATA { //put data expect lastName } otherData; typedef struct __PHONE_BOOK_ENTRY { ... otherData *data; ... } entry ... ``` 測試結果: ![test1_Pic](http://i.imgur.com/5Z8Y9xe.png) **Cache-miss:** ```shell Performance counter stats for './phonebook_opt' (100 runs): 12,4501 cache-misses # 28.264 % of all cache refs ( +- 0.59% ) 44,0500 cache-references ( +- 0.46% ) 2,4398,0290 instructions # 1.94 insns per cycle ( +- 0.02% ) 1,2562,6360 cycles ( +- 0.19% ) 0.048758786 seconds time elapsed ( +- 0.30% ) ``` 結果意外地理想,大幅降低 cache-miss ,搜尋及插入資料的時間也變少了。 但是這只算是**治標不治本** 假設每筆資料都要填入其他相關資訊(e.g. 姓氏) 問題就會回到原點 這邊我讓每筆資料新增一塊空的 otherData ```c= phonebook_opt.c: ... e->data = (otherData *) malloc(sizeof(otherData)); strcpy(e->lastName, lastName); ... ``` 測試結果: ```shell Performance counter stats for './phonebook_opt' (100 runs): 151,8995 cache-misses # 94.176 % of all cache refs ( +- 0.07% ) 161,2927 cache-references ( +- 0.09% ) 3,4121,7546 instructions # 1.46 insns per cycle ( +- 0.02% ) 2,3358,1072 cycles ( +- 0.13% ) 0.090065262 seconds time elapsed ( +- 0.20% ) ``` 可以看到 cache-miss 又增加了 還比原始版本還高 執行時間也比原本的長 可見此方法不怎麼可行 ![opt](http://i.imgur.com/LUGvxWT.png) ### 3. 測試二 : 嘗試使用Hash table 第二次,我使用 Hash table 來儲存所有資料 建立一個 entry type 的 Hash table, 大小大概是350000 利用每個 lastName 所對應的 hash value 對 table size 取餘數來當作索引值 ```c= ... hash_index = hash_function(entry->lastName); put entry into table[hash_index]; ... ``` 但不見得每筆資料的 hash value 都是獨立的 因此當遇到 hash collision (兩個變數的 hash value 相同)時 就以 linked list 的方式串接(如下圖所示) ![hash collision](http://imgur.com/Gv97KDc.jpg) 然而 Hash function 有很多種 這邊就來測試以下六種 Hash function 1. **DJB2** 測試結果: ![DJB2-result](http://i.imgur.com/6teX4rp.png) cache-misses: ```shell Performance counter stats for './phonebook_hash_opt' (100 runs): 254,7001 cache-misses # 58.420 % of all cache refs ( +- 0.22% ) 435,9795 cache-references ( +- 0.24% ) 2,2879,0814 instructions # 1.18 insns per cycle ( +- 0.04% ) 1,9360,8219 cycles ( +- 0.34% ) 0.053086737 seconds time elapsed ( +- 0.33% ) ``` 2. **BKDR** 測試結果: ![BKDR-result](http://i.imgur.com/Gggq1s2.png) cache-misses: ```shell Performance counter stats for './phonebook_hash_opt' (100 runs): 255,0878 cache-misses # 59.273 % of all cache refs ( +- 0.30% ) 430,3633 cache-references ( +- 0.26% ) 2,3726,8911 instructions # 1.20 insns per cycle ( +- 0.04% ) 1,9828,2803 cycles ( +- 0.36% ) 0.054561474 seconds time elapsed ( +- 0.35% ) ``` 3. **AP** 測試結果: ![APHash](http://i.imgur.com/4UjRdGX.png) cache-misses: ```shell Performance counter stats for './phonebook_hash_opt' (100 runs): 251,9352 cache-misses # 58.323 % of all cache refs ( +- 0.29% ) 431,9651 cache-references ( +- 0.37% ) 2,5446,4326 instructions # 1.26 insns per cycle ( +- 0.04% ) 2,0218,2336 cycles ( +- 0.43% ) 0.055191749 seconds time elapsed ( +- 0.39% ) ``` 4. **JS** 測試結果: ![JSHash](http://i.imgur.com/mzUHrWW.png) cache-misses: ```shell Performance counter stats for './phonebook_hash_opt' (100 runs): 247,0955 cache-misses # 56.622 % of all cache refs ( +- 0.23% ) 436,3923 cache-references ( +- 0.21% ) 2,3718,4584 instructions # 1.21 insns per cycle ( +- 0.04% ) 1,9667,2118 cycles ( +- 0.28% ) 0.054124701 seconds time elapsed ( +- 0.32% ) ``` 5. **RS** 測試結果: ![RSHash](http://i.imgur.com/eaJx3GL.png) cache-misses: ```shell Performance counter stats for './phonebook_hash_opt' (100 runs): 215,5988 cache-misses # 52.947 % of all cache refs ( +- 0.26% ) 407,1983 cache-references ( +- 0.75% ) 2,3803,1769 instructions # 1.27 insns per cycle ( +- 0.04% ) 1,8734,7593 cycles ( +- 0.28% ) 0.049808275 seconds time elapsed ( +- 0.37% ) ``` 6. **SDB** 測試結果: ![SDBHash](http://i.imgur.com/imkKdD0.png) cache-misses: ```shell Performance counter stats for './phonebook_hash_opt' (100 runs): 242,4593 cache-misses # 57.131 % of all cache refs ( +- 0.26% ) 424,3887 cache-references ( +- 0.25% ) 2,3704,8982 instructions # 1.22 insns per cycle ( +- 0.04% ) 1,9386,2498 cycles ( +- 0.27% ) 0.053005389 seconds time elapsed ( +- 0.28% ) ``` 比較圖 --- - time: ![](http://i.imgur.com/tphg1vI.png) - cache-miss: ![](http://i.imgur.com/c0hE0Qi.png) ## 問題討論 : 本例選用的 dataset (也就是輸入電話簿的姓名) 是否存在問題? - 有代表性嗎?跟真實英文姓氏差異大嗎? 就現實情況來講差異還蠻大的, 許多名字根本沒有母音(我不知道如何發音), 或是只含一種字母( aaaaaa ),例如下面這些名字是從 word.txt 裡面擷取出來的: ```shell bbbb bbbbb bbbbbb bbbbbbb bbbbbbbb bbbbe bbbbut bbbppsll bbcc ``` 利用join指令去比對 words.txt 及 all-names.txt 兩個檔案 發現共有的名字有 <font color="red">14536</font> 個, 重疊的比例大概一半( 7110 個名字已重複) 不過如果這是一個人的別名或一個電話號碼的標籤, 就還說的過去 再加上 words.txt 幾乎沒有重複使用的名字, 雖然測資只有 35 萬, 但重複的比例也太低, 這裡有個網站名叫 name statistics, 統計了美國最多人取的名字, 像 Smith 的比例足足有 1.006%, 意思是 3 億多的美國人中, 有百萬多個名為 Smith, 對比到 words.txt, 可見相似率相對的低 [name statistics](http://www.namestatistics.com/) - 資料難道「數大就是美」嗎? 就如同剛剛提到的, words.txt 雖然有35萬資料, 卻沒有涵蓋住資料量只有 16000 的 all-names.txt, 可見資料不僅要大, 還要有意義, 才能算是好的資料, 如何才是有意義的資料, 會在下個問題來探討 - 如果我們要建立符合真實電話簿情境的輸入,該怎麼作? 或許可以沿用 words.txt, 將其定位為"電話簿的別名", 並在同一列中加入其他屬性的資料, 像是地址, 信箱, 或是現代常用的 facebook, line ID 等等, 增加資料的可信度, 參考資料: - [hash function 觀念和實務](https://hackmd.io/s/HJln3jU_e#) - [哈希表之bkdrhash算法解析及擴展](http://blog.csdn.net/wanglx_/article/details/40400693) - [字符串哈希函数](http://blog.csdn.net/wanglx_/article/details/40300363) - [HackMD Command](https://hackmd.io/s/S1vAcMjFe#) >同學您好,助教這邊沒有您的個人資料,請正確填寫課程表單[name=課程助教][color=#c04932] >助教抱歉,已填寫[name=戴子祐]