owned this note
owned this note
Published
Linked with GitHub
# [phonebook](https://hackmd.io/s/S1RVdgza)
[github](https://github.com/diana0651/phonebook) contributed by <`diana0651`>
###### tags: `d0651` `sys`
> Reviewed by <`LitSnow`>
`*dNext`已經搬到新的 `__PHONE_BOOK_ENTRY` 結構中 , 舊的` __PHONE_BOOK_DETAIL_ENTRY` 結構中的 `struct __PHONE_BOOK_DETAIL_ENTRY *dNext;`就可以不用留著
>>已經修正
## 案例分析
### 作業要求
適度修改 phonebook_opt.c 和 phonebook_opt.h 兩個檔案,使得這兩者執行時期的 cache miss 降低。
### 預期目標
- [x] 學習使用 Git 與 GitHub
- [x] 學習效能分析工具
- [ ] 可能的效能改進方向:
- [x] 改寫 `struct__PHONE_BOOK_ENTRY` 的成員,搬動到新的結構中
- [x] 使用 hash function 來加速查詢
- [x] 既然 first name, last name, address 都是合法的英文 (可假設成立),使用字串壓縮的演算法,降低資料表示的成本
- [ ] 使用 binary search tree 改寫演算法
### 開發環境
- `$ cat /etc/issue`
Ubuntu 14.04.5 LTS \n \l
- `$ cat /proc/version`
Linux version 4.2.0-42-generic (buildd@lgw01-55) (gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04.3) )
- `$ lscpu`
L1d 快取: 32K
L1i 快取: 32K
L2 快取: 256K
L3 快取: 8192K
---
## Makefile
![](http://4.bp.blogspot.com/-NvWSeUB4vRk/VW6X4zVWrJI/AAAAAAAAua0/R3uKnp4efdI/s1600/Makefile_diagram.jpg)
### makefile規則的基本結構
- 目標(Target) : 一個目標檔,可以是Object檔,也可以是執行檔,還可以是一個標籤(Label)。
- 依賴(Dependency,Prerequisites) : 要產生目標檔(target)所依賴哪些檔。
- 命令(Command) : 建立專案時需要執行的shell命令 (命令部分的每行的縮進必須要使用Tab鍵而不能使用多個空格)。
- 注意:
- make只在意依賴性
- 顯式make命令
- 在Makefile中的命令,必須要以[Tab]鍵開始。
### 變數宣告
- MACRO = value : 可利用`$(MACRO)`或`${MARCO}`來存取已定義的變數。
- := : 變數的值決定於它在 Makefile 中的位置,而不是整個 Makefile 展開後最終的值。
- ?= : 若變數未定義,則替它指定新的值。否則採用原有的值。
### 內部變數
- $?: 代表已被更新的 dependencies 的值。也就是 dependencies 中,比 targets 還新的值。
- $@ : 代表 targets 的值。
- $< : 代表第一個 dependencies 的值。
- $* : 代表 targets 所指定的檔案,但不包含副檔名。
### 參考資料
http://maxubuntu.blogspot.tw/2010/02/makefile.html
http://mropengate.blogspot.tw/2015/06/makefile-makefile.html
http://mropengate.blogspot.tw/2015/06/makefile-makefile-2.html
http://tetralet.luna.com.tw/?op=ViewArticle&articleId=185
---
## Gnuplot
[gnuplot](http://www.gnuplot.info/) 是一個用於生成趨勢圖和其他圖形的工具。它通常用於收集基於時間的數據,同時也可以使用靜態數據。
>[gnuplot introduction(中文版)](https://dl.dropboxusercontent.com/u/1091156/yong/text/gnuplot%20introduction.pdf)
>[gnuplot manual](http://www.gnuplot.info/docs_5.0/gnuplot.pdf)
### 簡易設定
- 設定標題:set title "title_description"
- 設定 x 軸 Label:set xlabel "x_description (unit)"
- 設定 x 軸值範圍:set xrange [ m : n ]
- 呈現圖式:plot
- 輸出圖檔:set output 'filename.png'
### 資料呈現
- 點與線:plot "filename" with linespoints
線條寬度(linewidth)、點大小(pointsize)。兩者都可以設置为整數或小數。
- 點:plot "filename" with points
點型(pointtype),主要用於設置點得形狀
![image alt](http://img.fanli7.net/2016-04-14/20160414161637833)
- 線:plot "filename" with line
線型(linetype ),主要用於設置線條的顏色
![image alt](http://img.fanli7.net/2016-04-14/20160414161619724)
### 參考資料
http://applezu.netdpi.net/2014/02/gnuplot.html
http://fanli7.net/a/bianchengyuyan/C__/20160414/558945.html
---
## GitHub
* `git init` 建立目錄 `.git`
* `git status` 確認現在所有檔案的狀況
* `git add 檔名` 追蹤此檔案
* `git commit` 確認檔案並snapshot(有點像暫時存檔)
* `git remote add <nickname> <repository-URL>` 連結遠端的repository並給他取代稱
* `git push <nickname>` 把檔案上傳到指定的repository
## Linux 效能分析工具: Perf
[Perf (Performance Event)](http://wiki.csie.ncku.edu.tw/embedded/perf-tutorial) 提供一個簡便的效能分析方案,另有即時的效能分析等應用,是用來進行軟體性能分析的工具。
- `$ uname` : 顯示作業系統
- `sudo perf top` : 查看整體系統負載
- `perf list` : 列出所有能夠觸發 perf 採樣點的事件
- Perf 能觸發的事件分為三類
- hardware : 由 PMU 產生的事件,比如 cache-misses、cpu-cycles、instructions、branch-misses …等等,通常是當需要瞭解程序對硬體特性的使用情況時會使用。
- software : 是核心程式產生的事件,比如 context-switches、page-faults、cpu-clock、cpu-migrations …等等。
- tracepoint : 是核心中的靜態 tracepoint 所觸發的事件,這些 tracepoint 用來判斷在程式執行時期,核心的行為細節,比如 slab 記憶體配置器的配置次數等。
- `perf stat` : 概括提供程序運行的整體情況
### 參考資料
http://mropengate.blogspot.tw/2016/04/perf.html
---
## 未優化版本 phonebook_orig
### 測試程式效能
- `make run` 執行時間
``` clike=
size of entry : 136 bytes
execution time of append() : 0.102741 sec
execution time of findName() : 0.007556 sec
3
```
- `make cache-test` ==**cache-misses 90%**==
``` clike=
Performance counter stats for './phonebook_orig' (100 runs):
2,029,918 cache-misses # 89.885 % of all cache refs
2,275,583 cache-references
263,219,831 instructions # 1.22 insns per cycle
221,815,824 cycles
0.076515784 seconds time elapsed ( +- 1.48% )
![](https://i.imgur.com/NH22hCN.png)
```
:::info
解釋:
cache-misses: 沒有在cache中找到需要的資料, 反過來就是cache hit
cache-references: 讀取cache的次數
instructions: 機器指令的數目
cycles: 程式中的指令所需要的總cycle次數
:::
- `sudo perf top` 尋找熱點
![](https://i.imgur.com/BZtcfts.png)
### 小結
>L1 Cache : 32KB = 32\*1024,PhoneBook size = 136 bytes,321024 / (136\*8) = 30.12 (只能存 30 筆左右)
若把整個 `__PHONE_BOOK_ENTRY` 的 struct 都丟進 `findName()` 尋找, L1 cache 最多也只能放 30 個 entry,一共有 35 萬個單字,必定會發生頻繁的 cache miss。
- 效能:findName() 較 append() 好
- 執行時間:append() 較 findName() 久
- 目標:==**縮短 findName() 的執行時間**==
### 改善方向
- Reduce search times
- Hash table
- Binary Search Tree
- Reduce struct size
- Data structure
- string compression
---
## 優化版本 phonebook_opt: 1。減少資料結構大小
搜尋時只比對 lastName, 所以針對 cache-misses,嘗試將 `struct __PHONE_BOOK_ENTRY` 縮小,把其它資訊包裝起來放到 `struct __DETAIL_ENTRY`,再用指標指向他
```clike=
typedef struct __PHONE_BOOK_ENTRY {
char lastName[MAX_LAST_NAME_SIZE];\
struct __DETAIL_ENTRY *pDetail;
struct __PHONE_BOOK_ENTRY *pNext;
} entry;
```
### 測試程式效能
- `$ make cache-test`
size of entry : 136 -> 32
cache-miss : 90% -> 53%
```clike=
size of entry : 32 bytes
execution time of append() : 0.046319 sec
execution time of findName() : 0.003430 sec
Performance counter stats for './phonebook_opt' (100 runs):
406,381 cache-misses # 53.221 % of all cache refs
685,441 cache-references
246,015,882 instructions # 1.60 insns per cycle
147,728,355 cycles
0.048182165 seconds time elapsed ( +- 0.81% )
```
- `$ eog runtime.png `
![](https://i.imgur.com/6TgUi5M.png)
## 優化版本 phonebook_opt: 2。 hash function 加速查詢
[Hash](https://www.csie.ntu.edu.tw/~hsinmu/courses/_media/dsa_12spring/hashing.pdf) 將資料透過特別的函數轉換成表格索引進行存取,所以實做的重點在於**建立表格**及**建立轉換的函數**。
> >[參考概念](https://embedded2016.hackpad.com/2016q1-Homework-1--NcHhQCQ4ijr)
>
> >[參考實作](https://hackmd.io/KYZgDGCcAcYIwFoBMAWAZgYwSkkDsC0IOCeIAJsHsGkgEbDlJA==)
- hash function:
先將字串資料轉換成數值,使用最容易實做的 division 計算 index。
- hash table size:
為了降低 collision 發生,我們希望 hash table 愈大愈好,然而較大的 table 容易造成空間的浪費。
由於 hash function 採用 division,在實做上table size= D,且為了降低 collision 的發生,所以選擇 D 為質數。
- overflow handling:
當 hash value 對應到 hash table 中的欄位已經有資料了,使用 Chaining,可以有效利用原本結構體的 pNext 將 overflow 的資料加進該欄位的 list 中。
### 測試程式效能
``` clike=
$ ./phonebook_hash
size of entry : 32 bytes
execution time of append() : 0.077230 sec
execution time of findName() : 0.000000 sec
Performance counter stats for './phonebook_hash' (100 runs):
446,120 cache-misses # 75.538 % of all cache refs
589,839 cache-references
303,567,973 instructions # 1.67 insns per cycle
181,623,645 cycles
0.062899824 seconds time elapsed ( +- 1.29% )
```
![](https://i.imgur.com/E01t14f.png)
#### 改善方向
- 測試 hash table size
### 其他的 HASH function
>[各種字符串Hash函數比較](https://www.byvoid.com/zht/blog/string-hash-compare)
#### 可以實作的方向
[Programming Small](https://hackmd.io/s/S1rbwmZ6)
提到 ==djb2 hash function== & ==BKDR hash function==
> >[參考實作](https://hackmd.io/BwVgJgzBxgjAtAU2LY8AstEAZ4EMAjANnXm2WwCYBjCMYYa2IA==)
- djb2 hash function
初始 hash 值為 5381,key 為 hash\*33 加上字串的內容。
```clike=
unsigned int DJB2_hash(char lastName[], int hash_table_size)
{
unsigned int hash = 5381;
int c;
int i = 0;
while (c = lastName[i++])
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
return hash %= hash_table_size;
```
- BKDR hash function
計算過程中有係數(seed)參與,seed為31 131 1313
```clike=
unsigned int BKDR_hash(char lastName[], int hash_table_size)
{
unsigned int seed = 131;
unsigned int hash = 0;
int i = 0;
while(lastName[i] != '\0')
{
hash = hash * seed + lastName[i];
i++;
}
return hash %= hash_table_size;
}
```
## 優化版本 phonebook_opt: 3。字串壓縮的演算法,降低資料表示的成本
:::info
[Smaz](https://github.com/antirez/smaz) is a simple compression library suitable for compressing very short strings.
:::
> >[參考實作](https://hackmd.io/MwUwrAnAZlDGAMBaWAOAhixAWLB2AbImsFoSAEbz4BMWKUwuaAjEA===)
### 測試程式效能
```clike=
$ ./phonebook_smaz
size of entry : 32 bytes
execution time of append() : 0.181449 sec
execution time of findName() : 0.008812 sec
Performance counter stats for './phonebook_smaz' (100 runs):
681,816 cache-misses # 58.851 % of all cache refs
1,191,106 cache-references
806,187,790 instructions # 1.46 insns per cycle
554,662,350 cycles
0.163610323 seconds time elapsed ( +- 0.51% )
```
![](https://i.imgur.com/GdAAIXj.png)
### 其他的資料壓縮方法
> >[參考](https://mimihalo.hackpad.com/HW2--D5sU0CfNmJH)
- 資料壓縮
- 失真壓縮
- 影像處理
- 無失真壓縮
- 字串壓縮
- 常見之壓縮方法
- 霍夫曼編碼
將資料先搜尋一遍來建編碼樹
- smaz 方法
即時的壓縮方法
適合短字串, 英文字串效果好
- [shoco 方法](http://ed-von-schleck.github.io/shoco/#quick-start)
即時運算的壓縮方法
特性與 smaz 相反
## 優化版本 phonebook_opt: 4。 binary search tree 改寫演算法
...
## Code Review
- coding style
`$ astyle --style=kr --indent=spaces=4 --indent-switches --suffix=none *.[ch]`
- github
- commit 的敘述明確
- branch 的版本控制
---
<style>
h2.part {color: red;}
h3.part {color: green;}
h4.part {color: blue;}
h5.part {color: black;}
</style>