owned this note
owned this note
Published
Linked with GitHub
# 2016q3 Homework 1 (phonebook)
contributed by <`jkrvivian`>
### Reviewed by `0140454`
* 在 commit 前應多多注意,避免無意義的 commit 產生。也可善用 `git revert` 來復原 commit。
例如 [commit "Add smaz-master"](https://github.com/jkrvivian/phonebook-1/commit/e29169987a9d39423b1f228356b4632deb0f7346) 和 [commit "delete"](https://github.com/jkrvivian/phonebook-1/commit/9c6a9c1f3aef1b93215c5c9cefa9849ec699243c)。
* 應該善用 .gitignore 來排除測試時由程式所產生的檔案。
如 [commit "add smaz string compression algorithm"](https://github.com/jkrvivian/phonebook-1/commit/5ace5a8806a4692fbb28363d15d77a0eca7f0717) 中的 `smaz.txt`。
* `malloc()` 是一個開銷很大的 function,未來可提出相對應的改善方案。
* 可嘗試使用多種 hash function 並比較其效能差異。
---
繼續改進效能與研究:
* 字串壓縮的演算法,降低資料表示的成本
* binary search tree
* 研究dataset問題
> 挑戰進階題>"<
[春季班連結](https://embedded2016.hackpad.com/2016q1-Homework1-X6DOgpgSkkC)
## 案例分析:phonebook
優化前:
![before](https://i.imgur.com/G8wScPf.png)
>> 避免用圖片表示文字訊息 [name=jserv]
用perf找尋熱點:
`./phonebook_orig & sudo perf top -p $!`
`findName()` 為熱點,佔了12.91%; append()只佔了1.34%,
但append()的執行時間卻遠遠超過findName()
![](https://i.imgur.com/ms1MEbl.png)
cache misses 居然到96.047%
![](https://i.imgur.com/2Ebj88V.png)
## 優化
### 方法:
* 減少structure大小 (findName()只看lastname)
* hash function
#### 優化1 :減少structure大小
把lastName以外的資訊刪掉
```C=
typedef struct __PHONE_BOOK_ENTRY {
char lastName[MAX_LAST_NAME_SIZE];
/*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]; */
struct __PHONE_BOOK_ENTRY *pNext;
} entry;
```
結果:
* structure size從136byte縮減為24byte
![](https://i.imgur.com/7SVllPu.png)
* cache misses
cache missis從原先的96.047%降至70.018%
![](https://i.imgur.com/CpBHY4h.png)
* plot
![](https://i.imgur.com/QcfqEOu.png)
* 之前的只有註解掉好遜喔! 所以把資料搬動到新結構中
```C==
typedef struct __PHONE_INFO {
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];
} info;
typedef struct __PHONE_BOOK_ENTRY {
char lastName[MAX_LAST_NAME_SIZE];
struct __PHONE_INFO *information;
struct __PHONE_BOOK_ENTRY *pNext;
} entry;
```
結果:
* 大小變為32byte
```
size of entry : 32 bytes
execution time of append() : 0.038601 sec
execution time of findName() : 0.002589 sec
```
* cache misses降至62.990%
```
Performance counter stats for './phonebook_opt' (100 runs):
407,872 cache-misses # 62.990 % of all cache refs ( +- 0.49% )
647,521 cache-references ( +- 0.93% )
246,533,337 instructions # 1.67 insns per cycle ( +- 0.02% )
147,266,812 cycles ( +- 1.02% )
0.050819024 seconds time elapsed ( +- 1.33% )
```
* plot
![](https://i.imgur.com/wPW0C7Y.png)
#### 優化2 :hash
**hash** : 一般是一個整數,通過某種算法,可以把一個字符串”壓縮” 成一個整數
>感覺這裡用把字符串"壓縮"成一個整數這個解釋好像有點奇怪,應該是透過算法把字符串變成一個index ,這個index在 hash table裡面就會指向這個字串的bucket(可以當作門牌)
**衝突**:對不同的關鍵字可能得到同一雜湊地址,即k1!=k2, f(k1)=f(k2)
**hash table**: hash table大小越是質數,mod取餘數就越可能均勻分布在表的各處
* 利用字母ASCII碼算出每個字串的hash值,若hash值相同則使用linked list串起來另外多添加了hash function的 .c .h檔
* 使用BKDR hash
```C==
unsigned int BKDRHash(char lastName[])
{
unsigned int seed=31;
unsigned int hash=0;
int i=0;
while (lastName[i] != '\0') {
hash = hash * seed + lastName[i];
++i;
}
return hash %= SIZE;
}
```
* 多看了orig版的程式碼幾次,才知道應該怎麼修正,回頭看看自己寫的原版hash,真是一團糟,根本是胡亂拼湊@@ 所以重新再大修改一翻,總算成功了
* 結果:Hash Table size為256
* 執行時間 :
* findName()時間驟減
* 可是append()的時間卻到了十二秒多,增加太多太多了!
```
size of entry : 24 bytes
execution time of append() : 12.750197 sec
execution time of findName() : 0.000022 sec
```
* cache-misses是從96%降至64.743%
```
Performance counter stats for './phonebook_hash1' (10 runs):
153,348,502 cache-misses # 64.743 % of all cache refs ( +- 0.89% )
236,855,570 cache-references ( +- 0.14% )
2,033,121,783 instructions # 0.06 insns per cycle ( +- 0.24% )
36,137,260,010 cycles ( +- 1.41% )
12.420656874 seconds time elapsed ( +- 0.90% )
```
推測應該是Hash Table size太小,造成碰撞率很高,所以改成==1051==
* 結果:append()的時間仍然超過太多 但已有顯著下降
```
size of entry : 24 bytes
execution time of append() : 3.235966 sec
execution time of findName() : 0.000005 sec
```
再從維基百科中找一個大一點的質數 試試3571
* 結果:append()下降至0.575651秒 但還是比原本超出太多了
```
size of entry : 24 bytes
execution time of append() : 0.575651 sec
execution time of findName() : 0.000000 sec
```
* 推測:
在append()裡原先是從每一個linked list的頭去找最後一個再新增資料,就是因為找尋最後一個節點的迴圈太花費時間
* 改善:
直接把pointer指向每個linked list的最後一個,append完後再統一還原指向每個linked list的頭,以便findeName()進行
* 結果:
append的時間果然下降很多!
```
size of entry : 24 bytes
execution time of append() : 0.083959 sec
execution time of findName() : 0.000001 sec
```
* cache-misses也從70.999%降至57.058%
```
Performance counter stats for './phonebook_hash1' (100 runs):
865,677 cache-misses # 57.058 % of all cache refs ( +- 0.49% )
1,517,185 cache-references ( +- 1.46% )
348,528,862 instructions # 1.16 insns per cycle ( +- 0.02% )
300,277,793 cycles ( +- 1.04% )
0.100966169 seconds time elapsed ( +- 0.99% )
```
* plot
![orig_opt_hash](https://i.imgur.com/Fswd5bR.png)
-----
## 新進度
* 本例選用的 dataset (也就是輸入電話簿的姓名) 是否存在問題?
* 有代表性嗎?跟真實英文姓氏差異大嗎?
* 瀏覽整份`words.txt`,裡頭的名字跟真實英文姓氏差異很大,很多沒有意思的辭彙(ex:aaaa)
* 資料難道「數大就是美」嗎?
* 如果我們要建立符合真實電話簿情境的輸入,該怎麼作?
* 網路搜尋
* >該怎麼切入這個問題呢?
### 使用字串壓縮法
在[字串壓縮法](http://stackoverflow.com/questions/1138345/an-efficient-compression-algorithm-for-short-text-strings)中,有很多朋友們介紹了不同的字串壓縮法,決定先以最高人氣的SMAZ試驗
* [SMAZ](https://github.com/antirez/smaz)
* 簡介:
短字串的壓縮非常顯著,英文小寫的字串效果最好,若字串中有穿插數字效果較差
* functions:
* 壓縮:`int smaz_compress(char *in, int inlen, char *out, int outlen);`
* 解壓縮:`int smaz_decompress(char *in, int inlen, char *out, int outlen);`
* 皆回傳壓縮和解壓縮後的字串長度
* 節錄README的測試結果:
```
'This is a small string' compressed by 50%
'foobar' compressed by 34%
'the end' compressed by 58%
'not-a-g00d-Exampl333' enlarged by 15%
'1000 numbers 2000 will 10 20 30 compress very little' compressed by 10%
```
* 改寫`findName()`和`append()`
```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)
{
e->pNext = (entry *) malloc(sizeof(entry));
e = e->pNext;
/* compress string */
smaz_compress(lastName,sizeof(lastName),e->lastName,sizeof(lastName));
e->pNext = NULL;
return e;
}
```
### 字串長度真的"全部"縮短了嗎?
* 並非如此,不少字串經壓縮後反而比原本還長
* 以下為試驗:
### original 加SMAZ
結果:
* append時間比前面的方法都大的多,因為每次append都要經過SMAZ演算法,此演算法甚至比BKDR hash更繁複
* findName的時間與opt差不多
```
size of entry : 32 bytes
execution time of append() : 0.269562 sec
execution time of findName() : 0.003497 sec
```
* cache-misses:
cache-misses比起原版cache-misses仍從70%降低至56%,推測:壓縮字串後,有更多空間可以儲存資料
```
Performance counter stats for './phonebook_smaz' (100 runs):
613,100 cache-misses # 56.363 % of all cache refs ( +- 0.57% )
1,087,780 cache-references ( +- 2.52% )
1,143,419,207 instructions # 1.44 insns per cycle ( +- 0.00% )
794,834,236 cycles ( +- 0.61% )
0.264604214 seconds time elapsed
```
* plot
![](https://i.imgur.com/1vzO4DU.png)
* 問題
*
## 參考資料
* wikipedia
* [林佳瑩學姊](https://embeddedhomework.hackpad.com/Homework-2-ZFMBm6jTeIr)
* [吳勃興學長](https://embedded2016.hackpad.com/2016q1-Homework-1-DdP67a0ke8Q#:h=%E9%96%8B%E7%99%BC%E7%92%B0%E5%A2%83)
* [makefile和前置處理](http://deanjai.blogspot.tw/2006/03/makefiledefineif.html)
* hash:
* 各hash比較: https://www.byvoid.com/blog/string-hash-compare
* hash介紹ppt: http://www.eecs.wsu.edu/~ananth/CptS223/Lectures/hashing.pdf
* BKDR hash : http://blog.csdn.net/wanglx_/article/details/40400693
###### tags: `system embedded` `HW1-1`