2017q1 Homework1 (phonebook) === contributed by <`Sean1127`> ### Reviewed by `chenweiii` - 可以嘗試看看每個 hash table row 擁有自己的 memory pool,如此在搜尋該 row 的其他 entries 時可發揮到空間的 locality,在我的電腦實驗中,cache misses 可下降約 10%,效果還不錯。 - git commit message fc4529f: ```Clone "phonebook", attempt no.1 to lower cache miss rate``` - 建議直接將修改的主旨寫在標題,而不是藏在接下來的內容。 - ```Redesign a smaller phone book entry structure``` >記得列出硬體相關資訊,如:cache, memory >[color=red][name=課程助教] >>已補上[name=Sean1127][color=#2cddc9] ## Prologue 我是抱著當砲灰的心態來上這堂課 老實說我上大學之後一直很混,大一的程式都麻最後兩三天才寫,數學物理不是蹺課就是睡覺,線代還被當 大二的時候上了資安要寫Java,資料結構要寫C/C++,才忽然意識到我連最基本的東西都要回去查,我的程度根本就跟一個剛進資訊系的大一一樣 但我的努力也就是跟上課程的進度而已,大二到大三上的程式課並不多,相反的數學課倒是頗多(離散、機率、演算法) 雖然說是比之前好,但認真講,就從'爛'進步到'普通'而已 等到三下也就是現在,畢業倒數的時刻,又是一個檢視自己的機會,就說我太容易滿足,覺得之前那樣就夠了,但好像不是這麼一回事XD 反正我是知道現在才開始學已經太晚了,也知道我這學期一定會爆炸 可是俗話說: Better late than never. 第一次用不是 VM 裝 linux,第一次注意程式的效能(之前只要跑出正確的結果就感謝上蒼了),第一次用 HackMD,第一次認真學 Git... Well, here I am. 如果有其他像我一樣經歷的人,我想我這種廢咖的程度都能來這兒,你們這些不比我差的傢伙憑什麼不行? ## Schedule * 2/21: * install ubuntu 16.04 with [輕鬆學會 Windows / Ubuntu 雙系統安裝](https://www.youtube.com/watch?v=rjpBTT6AmeU) * install ibus-chewing with [如何在 Ubuntu 16.04 上使用預設的 ibus 中文輸入法](http://it.livekn.com/2016/05/ubuntu-1604-ibus.html),但不知道為什麼多了一個 bopomofo,而且還刪不掉 ![](https://i.imgur.com/ojWUK24.png) * set vim environment with [終端機,Vim 設定](https://hackmd.io/s/HJv9naEwl) * 2/22: * learn git with [Learn Git Branching](http://learngitbranching.js.org/) * read course material * 2/23: * learn gnuplot with [gnuplot 語法解說和示範](https://hackmd.io/s/Skwp-alOg#) * read course material * 2/24: * learn Perf with [Linux 效能分析工具: Perf](http://wiki.csie.ncku.edu.tw/embedded/perf-tutorial) * read homework document * 2/25: * test 1 & 2 ## Requirement 1. cache miss 降低 2. findName() 的時間必須原本的時間更短 3. 回答以下問題:本例選用的 dataset (也就是輸入電話簿的姓名) 是否存在問題? * 有代表性嗎?跟真實英文姓氏差異大嗎? * 資料難道「數大就是美」嗎? * 如果我們要建立符合真實電話簿情境的輸入,該怎麼作? ## Computer Info CPU: ```shell= $ lscpu 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: 1678.710 CPU max MHz: 2700.0000 CPU min MHz: 800.0000 BogoMIPS: 4789.04 Virtualization: VT-x L1d cache: 32K L1i cache: 32K L2 cache: 256K L3 cache: 3072K NUMA node0 CPU(s): 0-3 ``` Kernel: ```shell= $ uname -a Linux sean 4.8.0-39-generic #42~16.04.1-Ubuntu SMP Mon Feb 20 15:06:07 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux ``` ## Preperation ### Perf * 重點整理: * 多次使用 perf record 會產生 perf.data, perf.data.old 等等,注意清除舊的 * perf 監測 CPU 時需要開啟 kernel pointer 權限(預設為3,需要設為0),有時重開機之後會變回3,這時要再手動改回來 * 要用`$ perf record`須開啟 kernel address map`$ sudo sh -c " echo 0 > /proc/sys/kernel/kptr_restrict" ` * 要用`$ perf stat`須開啟`$ sudo sh -c " echo 0 > /proc/sys/kernel/perf_event_paranoid"` * record 之前記得清空 cache!!!(設定在 Makefile 內較方便) ```shell= $ echo 3 | sudo tee /proc/sys/vm/drop_caches ``` >這邊數字代表的意義 1, 2, 3 有所不同,之後會補上資料 >老師用的是 $ echo 1 | sudo tee /proc/sys/vm/drop_caches * 不知道為什麼輸入`$ ./phonebook_orig & sudo perf top -p $!`會跑出 ```shell= ┌─Error:─────────────────────────────────────────┐ │Couldn't create thread/CPU maps: No such process│ │ │ │ │ │Press any key... │ └────────────────────────────────────────────────┘ ``` 所以我一律用 ```shell= $ perf record -e cache-misses,cache-references,instructions,cycles ./phonebook_orig $ perf report ``` 或是 `$ make cache-test` ### gnuplot * 重點整理 * `$ eog [圖檔名]` * `$ make plot` * `calculate.c main.c` 需要修正 看了影片和 document 後,還是不太了解 `$0` 和 `3:xtic(1)` 的意義,目前先照範例依樣畫葫蘆,之後再研究 ### Makefile ## Test Area * 先重現其他人的實驗結果,此處參考 [dada](https://embedded2016.hackpad.com/ep/pad/static/YkqjhwgnQcA), [rnic](https://embedded2016.hackpad.com/ep/pad/static/9eSkToGwJvZ), [Yuron](https://embedded2016.hackpad.com/ep/pad/static/NcHhQCQ4ijr), [ktvexe](https://hackmd.io/s/rk5ayZDKx) and [Programming Small](https://hackmd.io/s/HkO2ZpIYe) * 幾個較明顯的改善方法 1. 縮小資料結構:entry size 變小之後,相對的 cache 內可放入的 entry 增加,cache hit 的機率也增加 2. 新增 hash function:目的在於加速 findName() 的速度,append() 速度可能會稍微受損,但與 1. 結合後的速度仍比 orig 來的快 3. 壓縮字串:間接縮小了 entry size,與 1. 同理 4. 建立 memory pool,減少 append() malloc 時間 5. 建立 search tree,但有因有 [0140454](https://hackmd.io/s/Bkx3cYX6) 的前車之鑑,時間不減反增,這邊可能要再多想一下 ### Original Performance counter stats for './phonebook_orig' (100 runs): 1,216,932 cache-misses # 91.071 % of all cache refs ( +- 0.26% ) 1,336,239 cache-references ( +- 0.21% ) 262,822,206 instructions # 1.45 insn per cycle ( +- 0.02% ) 181,112,861 cycles ( +- 0.10% ) 0.070162363 seconds time elapsed ( +- 0.17% ) ### Test 1: smaller entry size * 在 findName() 與 append() 中都只用到了 lastname 這個member,但又要符合原本的功能,所以不考慮直接註解其他member * 參考 ++dada++ 另外設計一個 structure 只存 lastName,並加一個 pointer 指到原本的完整資料 * 另外 pFull 沒有 malloc 空間,等以後需要其他功能時再調整 ```clike= typedef struct __PHONE_BOOK_LASTNAME { char lastName[MAX_LAST_NAME_SIZE]; struct __PHONE_BOOK_ENTRY *pFull; struct __PHONE_BOOK_LASTNAME *pNext; } entry; ``` * 結果: Performance counter stats for './phonebook_opt' (100 runs): 106,448 cache-misses # 25.202 % of all cache refs ( +- 0.43% ) 422,371 cache-references ( +- 0.62% ) 244,884,604 instructions # 1.94 insn per cycle ( +- 0.02% ) 126,549,548 cycles ( +- 0.27% ) 0.049344870 seconds time elapsed ( +- 0.31% ) * 比較 ![1.0](https://i.imgur.com/oYu5Hfr.png) * time: 0.070162363 -> 0.049344870 * cache-misses: 91.071 % -> 25.202 % * 在兩個 function 裡面都用了新的 struct entry,若只改 findName() 不改 append(),這個設計就變得沒有意義,因為無法避免掉在 malloc 大量耗時與 cache 頻繁更新 >如同參考文件寫的,append的時候還有malloc detailEntry的話,cache miss不降反生,從93.309%升到94.412 %,但是這裡可以看出來findName的cache miss比原先版本的下降,從10.3%到3.03%[name=cheng hung lin] ### Test 1.1 我就不信邪,把 append() 跟 main 的 entry 寫回去看看會發生什麼事 ```clike= entry *append(char lastName[], entry *e) { e->pNext = (entry *) malloc(sizeof(entry)); e = e->pNext; strcpy(e->lastName, lastName); e->pNext = NULL; e->pFull = (full *) malloc(sizeof(full)); strcpy(e->pFull->lastName, lastName); return e; } ``` Performance counter stats for './phonebook_opt' (100 runs): 1,265,668 cache-misses # 91.709 % of all cache refs ( +- 0.34% ) 1,380,095 cache-references ( +- 0.88% ) 355,297,877 instructions # 1.26 insn per cycle ( +- 0.03% ) 281,243,138 cycles ( +- 1.17% ) 2,742,130 L1-dcache-load-misses ( +- 0.56% ) 0.114042676 seconds time elapsed ( +- 2.23% ) ![1.1](https://i.imgur.com/aCPqBIK.png) * 時間變快了沒問題,但是 cache-misses 高達 91.709 %(orig: 91.071 %,還多了一丁點兒) * 看來前人的錯還是不要再犯比較好... * 另外補充 `cache-misses` 跟 `L1-dcache-load-misses`的差別 >根據PERF_EVENT_OPEN :Cache misses. Usually this indicates Last Level Cache misses,在perf中cache miss指的是在任何階層的cache都找不到資料,所以要從memory載入資料至cache這種情況發生的event >L1-dcache-load-miss基本上就是字面的意思[name=Yuron] 果然還是學長眼尖啊,但在這種定義下,我們要看的應該是`L1-dcache-load-miss` ### Test 2: hash function * 參考 ++案例分析:phonebook++, ++Yuron++ 與 [nekoneko](https://hackmd.io/s/rJCs_ZVa) 的研究(主要是 ++nekoneko++,等於複習了一整堂資結) * hash 三大重點(引用 ++Yuron++) * table size * function * overflow handle * 白話來說,hash 希望建立一個**一一對應**的函數關係,但那是只最完美的狀態。一般來說還是會有有兩個值 map 到同一個 set 的情況,這時就用 link list 把它們連接 * 設 hash function distribution = uniform * slot = 8 * object = list length = 64 * new list length = 64 / 8 = 8 * 也就代表 new list 上面的 operation 會加速 8 倍 * hash 效果如此強大,重點也就變成如何產生 **uniform hash** [連結](http://www.cse.yorku.ca/~oz/hash.html)裡面簡單介紹了 3 個方法,我就先借 djb2 用用 * hash 為何要乘上一個大的質數在 ++Programming Small++ 最後有提到 * 實做部份參考 [ktvexe/phonebook-1](https://github.com/ktvexe/phonebook-1) 寫法,main 用最笨的方法寫 ```clike= int main() { #if defined(HASH) ... #else ... #endif } ``` 等於是複製一份 main 來改,但我就是笨,之後有空再回來改吧(這種寫法比較好 debug,照 ++ktvexe++ 的方法我大概會直接昏倒) * 結果 Performance counter stats for './phonebook_hash' (100 runs): 100,791 cache-misses # 23.689 % of all cache refs ( +- 2.22% ) 425,469 cache-references ( +- 0.97% ) 239,077,278 instructions # 1.70 insn per cycle ( +- 0.02% ) 140,451,230 cycles ( +- 0.67% ) 1,114,672 L1-dcache-load-misses ( +- 0.35% ) 0.057939853 seconds time elapsed ( +- 1.61% ) * `cache-misses` 比先前的 25.202 % 又進步了 * 但時間上變得很慘,有圖有真相![](https://i.imgur.com/SE7NDaF.png) * 問題來了,我修改了 `main` 之後,好像把 original 的部份弄壞啦... * 但就算如此,hash 的表現無論是跟 original's original: 0.089145 或是 new original: 0.054776 比,都來的快 >目前先這樣,這部份要念的話念不完啊[name=Sean1127] ### Test 3: fix test 1 & 2 * orig 的數據跑掉之後,我用 git 回朔到先前的版本重新測試,發現 orig 還是維持 0.5 ~ 0.6,比一開始測試的 0.8 ~ 1.0 還快的多 * 進到 Makefile 看就發現 ```clike=28 run: $(EXEC) echo 3 | sudo tee /proc/sys/vm/drop_caches watch -d -t "./phonebook_orig && echo 3 | sudo tee /proc/sys/vm/drop_caches" cache-test: $(EXEC) perf stat --repeat 100 \ -e cache-misses,cache-references,instructions,cycles \ ./phonebook_orig perf stat --repeat 100 \ -e cache-misses,cache-references,instructions,cycles \ ./phonebook_opt output.txt: cache-test calculate ./calculate plot: output.txt gnuplot scripts/runtime.gp ``` * 若用`$ make run`則會每次清除 cache,出現的數據也較接近原始數據 * 用`$ make cache-test`之前必須手動清除 cache,且在第一次之後的執行都是沿用之前的 cache * 再打開`orig.txt`觀察就很容易發現規律 ```coffeescript= append() findName() 0.062958 0.005667 append() findName() 0.048825 0.005518 append() findName() 0.049174 0.005507 append() findName() 0.047186 0.005497 append() findName() 0.047982 0.005529 append() findName() 0.048004 0.005543 append() findName() 0.047304 0.005514 ... ``` * 第一比資料花的時間特別久,之後 orig 都跟 opt 版本差不多快了 * 同理可證,opt 的資料也同樣有測不準的情況 *如何解決?* * 簡單看兩個例子 ```shell= $ echo 3 | sudo tee /proc/sys/vm/drop_caches 3 $ perf stat -e cache-misses,cache-references,instructions,cycles ./phonebook_orig size of entry : 136 bytes execution time of append() : 0.181801 sec execution time of findName() : 0.006251 sec Performance counter stats for './phonebook_orig': 62,4197 cache-misses # 85.606 % of all cache refs 72,9147 cache-references 2,6558,1572 instructions # 1.52 insn per cycle 1,7416,1931 cycles 0.264308628 seconds time elapsed $ $ $ $ perf stat -e cache-misses,cache-references,instructions,cycles ./phonebook_orig size of entry : 136 bytes execution time of append() : 0.050006 sec execution time of findName() : 0.005566 sec Performance counter stats for './phonebook_orig': 124,7285 cache-misses # 90.716 % of all cache refs 137,4933 cache-references 2,6357,6114 instructions # 1.48 insn per cycle 1,7750,6742 cycles 0.071150082 seconds time elapsed ``` * 第二次執行的時間誤差非常大(0.264 -> 0.071,將近 1/4 倍),但 cache 的表現差不多,再測試`phonebook_opt`也是一樣的規律 * 比對熱點 ```shell= Samples: 307 of event 'cycles', Event count (approx.): 182165922 Overhead Command Shared Object Symbol 17.62% phonebook_orig phonebook_orig [.] main 14.81% phonebook_orig libc-2.23.so [.] __strcasecmp_l_avx 11.53% phonebook_orig phonebook_orig [.] findName 9.10% phonebook_orig libc-2.23.so [.] _int_malloc 6.96% phonebook_orig libc-2.23.so [.] _IO_fgets 5.41% phonebook_orig libc-2.23.so [.] __memcpy_sse2 3.47% phonebook_orig phonebook_orig [.] append ``` ```shell= Samples: 327 of event 'cycles', Event count (approx.): 186019183 Overhead Command Shared Object Symbol 13.79% phonebook_orig libc-2.23.so [.] __strcasecmp_l_avx 12.01% phonebook_orig phonebook_orig [.] findName 11.57% phonebook_orig libc-2.23.so [.] _int_malloc 10.02% phonebook_orig phonebook_orig [.] main 7.16% phonebook_orig libc-2.23.so [.] _IO_fgets 4.95% phonebook_orig libc-2.23.so [.] __memcpy_sse2 4.94% phonebook_orig libc-2.23.so [.] _IO_getline_info 3.57% phonebook_orig [kernel.kallsyms] [k] clear_page_c_e 3.29% phonebook_orig [kernel.kallsyms] [k] page_fault 3.26% phonebook_orig libc-2.23.so [.] __strcpy_sse2_unaligne 3.12% phonebook_orig phonebook_orig [.] append ``` | function | cycles (cache cleared) | cycles (not cleared) | | -------- | --- | --- | | main | 32097635 | 18639122 | | findName | 21003730 | 22361366 | | append | 6321157 | 5803798 | [Linux 的記憶體快取(Cache Memory)功能:Linux 系統把記憶體用光了?](https://blog.gtwang.org/linux/linux-cache-memory-linux/) >要釋放 Linux 的記憶體快取,可以透過更改 /proc/sys/vm/drop_caches 這個檔案的內容來達到,當這個檔案內容被設為 1 時,是表示要求 Linux 釋放沒在使用的一般性快取(pagecache),而設為 2 時,則代表要求釋放 dentry 與 inode 所使用到的快取,若設為 3 則是釋放所有的快取(也就是包含 1 與 2 的狀況)。 * 目前還沒查到如何在 `$ perf stat --repeat`觀測時每一次都預先清除 cache * 兩種方法出現的時間誤差是來自 main,因為 main 當然也是會被放在 cache 裡面的,就算不是在 L1-dcache,也可能是在 L2, L3,比起重頭到 memory 拿一定還是來的快 * `findName`, `append`沒有太大影響,所以無論有無清除 cache,都不會改變不同演算法的排序 | speed | cache cleared | not cleared | | ------ | --- | --- | | method | orig < hash < opt | orig < hash < opt | ### Test 4: memory pool * 好...其實上個實驗只是因為我眼殘搞錯 對那麼多的資料來說,有時候跑出來的結果根本不是我要的,就好像方程式等號兩邊的單位不一樣,也就沒有意義 雖然可參考 ++louielu++ 對於 perf event 的整理,但我真的只看懂 30 % 左右而已... 總而言之,`output.txt`裡面的數據是根據同一個基準點去測的,所以畫出來的圖應該是沒問題 * 下一個目標是用 mem pool 因為`append`的呼叫次數很高,裡面 malloc 的成本也就被放很大,如果可以在`main`就預備一塊夠大的空間,希望可以: 1. `append`的成本移動到`main`之中,成本還是在但是轉移了 2. 呼叫 100 一次,跟呼叫 1 一百次,後者的呼叫成本比較高,因為函式會有 push, pop 等額外作業 3. 善用 cache 空間集中性,因為 link listed 的各個節點在記憶體上是非連續的,用 memory pool 應可以把節點跟節點間的地址拉近 * L1 cache 可放入的 entry number = 32KB / 32 B = 1024 個 ```clike= /* build the entry */ entry pHead[MAX_HASH_TABLE_SIZE], *e[MAX_HASH_TABLE_SIZE]; printf("size of entry : %lu bytes\n", sizeof(entry)); for (i = 0; i < MAX_HASH_TABLE_SIZE; ++i) { e[i] = &pHead[i]; e[i]->pNext = NULL; } ``` * 我的`MAX_HASH_TABLE_SIZE`剛好也設1024,代表 cache 裡面剛好可以完全放進我的 table,所以查表非常快 * 而`pHead`是用矩陣宣告,所以也保持了 locality,這算是參考學長誤打誤撞的一次小成功... * 程式碼參考 [Implement own memory pool](http://stackoverflow.com/questions/11749386/implement-own-memory-pool) * 結果 ```shell= Performance counter stats for './phonebook_hash' (100 runs): 90,706 cache-misses # 40.360 % of all cache refs ( +- 3.77% ) 224,744 cache-references ( +- 3.85% ) 167,708,391 instructions # 1.49 insn per cycle ( +- 0.05% ) 112,453,444 cycles ( +- 1.44% ) 934,537 L1-dcache-load-misses ( +- 0.64% ) ``` * `cache-misses`比率變高( 23 % -> 40 %),但總數是變低的!(misses: 100,791 -> 90,706)(references: : 425,469 -> 224,744) ![](https://i.imgur.com/oK3aoNy.png) (太好了沒有比較久) 因為更加集中了記憶體位置,連帶`findName()`也變快了一點點 ### Test 5: 用字典詞彙 根據 [stack overflow](http://stackoverflow.com/questions/4456446/dictionary-text-file) 上的回答,可以用 [SCOWL (And Friends)](http://wordlist.aspell.net/) 列出字典裡約 675k 個字(有分大小寫),這些字包含地名、人名、與其他有意義的詞彙 * 注意!需要修正`main.c 的總記憶體量(超過350k)、搜尋字等等` * 結果 ```shell= Performance counter stats for './phonebook_pool' (100 runs): 196,072 cache-misses # 38.689 % of all cache refs ( +- 4.77% ) 506,785 cache-references ( +- 5.17% ) 344,977,883 instructions # 1.43 insn per cycle ( +- 0.03% ) 240,840,199 cycles ( +- 1.81% ) 0.108246834 seconds time elapsed ( +- 2.80% ) ``` ![](https://i.imgur.com/o2zrdyj.png) * 總時間比較長,`$ make plot`需要耐心等候 * 以比較有代表性的輸入檔,hash 反而表現更差了, ```clike= clock_gettime(CLOCK_REALTIME, &start); while (fgets(line, sizeof(line), fp)) { while (line[i] != '\0') i++; line[i - 1] = '\0'; #if defined(HASH) key = djb2(line) % MAX_HASH_TABLE_SIZE; e[key] = append(line, e[key]); #elif defined(POOL) key = djb2(line) % MAX_HASH_TABLE_SIZE; e[key] = append(line, e[key], p); #else e = append(line, e); #endif i = 0; } clock_gettime(CLOCK_REALTIME, &end); ``` * 猜測原因出在`hash`比原本的多了`djb2`轉換,且還有`malloc` * `memory pool`雖然也有`djb2`,但省了`malloc`時間 * perf report: ```shell= Samples: 453 of event 'cycles', Event count (approx.): 285436920 Overhead Command Shared Object Symbol 28.23% phonebook_hash phonebook_hash [.] main 16.84% phonebook_hash phonebook_hash [.] djb2 9.17% phonebook_hash libc-2.23.so [.] _int_malloc 8.12% phonebook_hash libc-2.23.so [.] _IO_fgets 6.79% phonebook_hash libc-2.23.so [.] _IO_getline_info 6.73% phonebook_hash libc-2.23.so [.] __strcpy_sse2_unaligned 4.54% phonebook_hash libc-2.23.so [.] __memcpy_sse2 3.38% phonebook_hash libc-2.23.so [.] malloc 2.93% phonebook_hash phonebook_hash [.] append ``` * **更正** * 就算是用原本的`word.txt`,`hash`的表現還是比`struct`差,上面 Test 4 的圖其實是奇蹟 ![正確版本](https://i.imgur.com/oDOtW9q.png) * 照理說畫圖是執行 100 次之後的平均,應該不會出現極端值,看來我很幸運呢,但這也告訴我們實驗多做幾次,有時後得到的結果不一定是正確的!!! * 比較 `Test 4`, `Test 5`發現其實名次是一樣的,只是時間都照比例放大了一點,所以就可以知道:字彙有無意義或是有沒有真實存在不會影響演算法 * 但順序還不一定,因為新的字典也是以 A-Z 排列好的 ### Conclusion * Test 1 修改 struct: 成功 * Test 2 struct + djb2: 沒有比 Test 1 還好,失敗 * Test 3 了解問題: 成功 * Test 4 struct + djb2 + memory pool: 成功 其實不太知道這次作業要完成到什麼程度才算 ok,因為永遠都有改進的空間 但可以知道的是我的能力還很不足,連重複別人的實驗結果都要花上很久時間 如果沒有學長姐的筆記的話,下場一定很慘 往後的方向可以是: 1. hash 調整 1. 簡化`djb2()`或是採用其他 hash function 2. 修改 hash 使用的位移常數 3. 修改 hash table 大小 2. 字串壓縮 3. 搜尋樹(BST, 紅黑, balaned BST...) 4. 打亂字典 input 順序(unsorted) ### Answer the folloing questions 1. cache miss 降低 >miss: $1,216,932 \rightarrow 90706$ >percentage: $91 \rightarrow 40 \space\%$ 2. findName() 的時間必須原本的時間更短 >$0.005661 \rightarrow 0.000003$ 3. 回答以下問題:本例選用的 dataset (也就是輸入電話簿的姓名) 是否存在問題? * 有代表性嗎?跟真實英文姓氏差異大嗎? >`word.txt`裡面只有普通單字,沒有姓名,並且有一些甚至是無意義的字 >但從實驗五的數據來看,目前的演算法並不會因為輸入的字是否是有意義、是否是姓名而改變時間 * 資料難道「數大就是美」嗎? >「數大就是美」對這次作業的意義與平常我們理解的不同,這裡要理解成:資料越多越好,而不是資料本身的值越大越好 >而「好」的意思可能是有代表性,或是提供的資訊較多 >要破除全稱命題只需要舉一個反例 >例一:把`word.txt`第一個字複製 500,000 次,比 350,000 還多,卻沒有代表性,也沒有提供更多資訊 * 如果我們要建立符合真實電話簿情境的輸入,該怎麼作? >可能需要使用爬蟲程式去網路抓資料,例如建立「台南美食店家」的電話簿,此外還需要做字串前處理,再放到 entry 中的各個欄位 >進度請加速[name=亮谷][color=#9d3] ###### tags: `sysprog`