# 2017q1 Homework1 (phonebook) contributed by <`genehuang0011`> >進度要加快摟~[name=課程助教][color=#4029af] >好的![name=俊錚] ## 開發環境 ### 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: 61 Model name: Intel(R) Core(TM) i5-5257U CPU @ 2.70GHz Stepping: 4 CPU MHz: 2700.000 CPU max MHz: 3100.0000 CPU min MHz: 500.0000 BogoMIPS: 5399.89 Virtualization: VT-x L1d cache: 32K L1i cache: 32K L2 cache: 256K L3 cache: 3072K NUMA node0 CPU(s): 0-3 ``` ### Kernal: ```shell= $ name -a Linux geneh-MacBookPro 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 ``` ## 工具熟悉 ### Perf 首先依照 [perf原理與實務](https://hackmd.io/s/B11109rdg#) 步驟指示裝上 perf,在 root 輸入 `$ perf top` 測試其功能時,出現以下錯誤畫面: ![](https://i.imgur.com/zL3hVDn.png) 目前的權限值是 **3** ??? (上面權限值不是只有 -1 到 2 嘛奇怪) 因此必須更改權限值,輸入指令 `$ sudo sh -c "echo -1 > /proc/sys/kernel/perf_event_paranoid"` 接著再試一次會出現: ![](https://i.imgur.com/VJtfqle.png) 因此須輸入以下指令,以解決 kernel pointer 的禁用。 `$ sudo sh -c "echo 0 > /proc/sys/kernel/kptr_restrict"` 常用的功能有: - list :列出所以可觸發的 event - top:可看出各函式在某個 event 上的熱點,可看出是哪個函式拖垮效能 - stat:針對特定 event 做效能檢查 ### gnuplot 在 [gnuplot語法解說和示範](https://hackmd.io/s/Skwp-alOg#) 一文中,可以了解到這是一個很方便且功能強大的資料視覺化工具,有很多參數可以設定,感覺要用 gnuplot 畫出一個清楚易懂的好圖表也是一門學問阿! ### astyle 一個很強大的工具,可以幫忙檢查 coding style 是否一致。 當檢查出格式不一致時,可輸入下列指令將檔案格式化。 `$ astyle --style=kr --indent=spaces=4 --indent-switches --suffix=none *.[ch]` ## 效能分析 首先,使用剛裝好的工具來觀察未優化的 phonebook 程式。 ### 程式碼分析 **phonebook_orig.c** ```c= entry *findName(char lastName[], entry *pHead) { while (pHead != NULL) { if (strcasecmp(lastName, pHead->lastName) == 0) return pHead; pHead = pHead->pNext; } return NULL; } entry *append(char lastName[], entry *e) { /* allocate memory for the new entry and put lastName */ e->pNext = (entry *) malloc(sizeof(entry)); e = e->pNext; strcpy(e->lastName, lastName); e->pNext = NULL; return e; } ``` ### perf top 熱點分析 ```shell= Overhead Shared Object Symbol 52.84% libc-2.23.so [.] __strcasecmp_l_avx 9.39% phonebook_orig [.] findName 7.68% [kernel] [k] unmap_page_range 6.89% libc-2.23.so [.] __strcasecmp_avx 4.28% [kernel] [k] page_remove_rmap 3.97% [kernel] [k] __tlb_remove_page_size.part.60 3.85% [kernel] [k] __mod_node_page_state 3.74% [kernel] [k] __mod_zone_page_state 3.65% [kernel] [k] uncharge_list 3.50% [kernel] [k] free_pcppages_bulk 0.20% [kernel] [k] perf_pmu_enable.part.93 0.01% [kernel] [k] native_sched_clock 0.00% [kernel] [k] perf_event_nmi_handler ``` ### make cache-test `$ perf stat --repeat 100 -e cache-misses,cache-references,instructions,cycles ./phonebook_orig` ``` Performance counter stats for './phonebook_orig' (100 runs): 3,404,315 cache-misses # 92.708 % of all cache refs ( +- 0.04% ) 3,672,081 cache-references ( +- 0.14% ) 262,497,727 instructions # 1.43 insn per cycle ( +- 0.02% ) 183,422,609 cycles ( +- 0.22% ) 0.059987389 seconds time elapsed ( +- 0.22% ) ``` ### make run 接著,將 cache 清空,並使用 **watch** 指令 觀察執行時間。 `$ watch -d -t "./phonebook_orig && echo 1 | sudo tee /proc/sys/vm/drop_caches" ` 由於我們對此次作業 cache misses 很在意,故要排除系統 cache 的影響,在每次執行之前手動藉由`echo 1 > /proc/sys/vm/drop_caches`來釋放 cache。 :::info **Freeing the page cache:** echo 1 > /proc/sys/vm/drop_caches **Free dentries and inodes:** echo 2 > /proc/sys/vm/drop_caches **Free the page cache, dentries and the inodes:** echo 3 > /proc/sys/vm/drop_caches ::: ``` size of entry : 136 bytes execution time of append() : 0.066283 sec execution time of findName() : 0.004928 sec ``` 使用 gnuplot 練習做一張圖,並作為之後的對照組。 ![](https://i.imgur.com/MqckR8G.png) ### 小結 - 由熱點分析結果以及圖表,可以很清楚發現,在 `findName()` 效能的瓶頸主要發生在資料的字串比對上。 - 透過 `perf cache-test` 結果,可發現 cache-misses 高達 **92.708%** 。 ### 預計解決方向 - 資料結構選擇上,因為原始使用 link-list 建構,字串必須「逐一」比對導致效率不彰,可考慮樹狀結構或先將其分類,以改善字串比對上的效能瓶頸。 - 縮小 entry-size 使得符合 cache 大小,以降低 cache-misses ## 參考資料 [strcasecmp(3) - Linux man page](https://linux.die.net/man/3/strcasecmp)