# 2017q1 Homework1 (phonebook)
contributed by<`zhanyangch`>
###### tags:`zhanyangch` `sysprog2017` `week1`
>中英文字間請以空白隔開,數字與文字間也是
>[color=red][name=課程助教]
### Reviewed by `you74674`
* 執行檔也在commit裡面,好像不太對?
* 最新的commit標題太長了,裡面也沒有提到append function的變更。
* 開發紀錄中的code可能忘了把entryArrayHead的宣告也放進去。
* append看起來不像有處理一次以後的碰撞?紀錄所提的「再用原本findName找到資料」,但append裡面沒有呼叫,main裡面也沒有相關的處理。
## 執行環境
lscpu 的資訊
```
$ lscpu
Architecture: x86_64
CPU 作業模式: 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 4
On-line CPU(s) list: 0-3
每核心執行緒數:2
每通訊端核心數:2
Socket(s): 1
NUMA 節點: 1
供應商識別號: GenuineIntel
CPU 家族: 6
型號: 42
Model name: Intel(R) Core(TM) i7-2640M CPU @ 2.80GHz
製程: 7
CPU MHz: 855.421
CPU max MHz: 3500.0000
CPU min MHz: 800.0000
BogoMIPS: 5587.06
虛擬: VT-x
L1d 快取: 32K
L1i 快取: 32K
L2 快取: 256K
L3 快取: 4096K
NUMA node0 CPU(s): 0-3
```
## <s>運行</s>執行原始版本
>> execute 在台灣/繁體中文的慣用翻譯是「執行」 [name="jserv"]
```
$ make
$ make run
size of entry : 136 bytes
execution time of append() : 0.105809 sec
execution time of findName() : 0.007168 sec
```
* Makefile 中
`echo 3 | sudo tee /proc/sys/vm/drop_caches`
* tee 為雙向重導向可以將資料同時導向檔案和螢幕
* /proc/sys/vm/drop_caches 設定為 3 時釋放大多數的Cachem emory
* 使用 gnuplot 顯示,修改 runtime.gp 刪除optimized(尚未實做的欄位)
`$ make plot`
![](https://i.imgur.com/2qbQ3p3.png)
* 用perf分析cache miss
`$perf stat --repeat 100 -e cache-misses,cache-references,instructions,cycles ./phonebook_orig
`
```
Performance counter stats for './phonebook_orig' (100 runs):
2,064,274 cache-misses # 91.100 % of all cache refs ( +- 0.12% )
2,265,937 cache-references ( +- 0.11% )
261,177,434 instructions # 1.28 insns per cycle ( +- 0.02% )
204,356,391 cycles ( +- 0.39% )
0.071805217 seconds time elapsed ( +- 2.00% )
```
可以看出 cache miss 高達 91.1%
* 用 perf 分析熱點函式
```
$ perf record ./phonebook_orig
$ perf report
```
```
Samples: 484 of event 'cycles:pp', Event count (approx.): 212235201
Overhead Command Shared Object Symbol
13.49% phonebook_orig libc-2.23.so [.] __strcasecmp_l_avx
12.59% phonebook_orig phonebook_orig [.] main
10.77% phonebook_orig libc-2.23.so [.] _int_malloc
8.47% phonebook_orig phonebook_orig [.] findName
6.54% phonebook_orig libc-2.23.so [.] __memcpy_sse2
6.24% phonebook_orig libc-2.23.so [.] _IO_fgets
5.74% phonebook_orig libc-2.23.so [.] _IO_getline_info
5.20% phonebook_orig libc-2.23.so [.] __strcpy_sse2_unaligned
4.05% phonebook_orig [kernel.kallsyms] [k] clear_page
3.37% phonebook_orig phonebook_orig [.] append
2.72% phonebook_orig libc-2.23.so [.] memchr
2.47% phonebook_orig [kernel.kallsyms] [k] get_page_from_freelist
2.34% phonebook_orig libc-2.23.so [.] malloc
2.23% phonebook_orig [kernel.kallsyms] [k] native_irq_return_iret
1.75% phonebook_orig [kernel.kallsyms] [k] handle_mm_fault
0.93% phonebook_orig phonebook_orig [.] fgets@plt
0.91% phonebook_orig [kernel.kallsyms] [k] __mod_zone_page_state
0.86% phonebook_orig [kernel.kallsyms] [k] unmap_page_range
0.86% phonebook_orig [kernel.kallsyms] [k] __pagevec_lru_add_fn
0.84% phonebook_orig libc-2.23.so [.] __strcasecmp_avx
0.57% phonebook_orig [kernel.kallsyms] [k] free_hot_cold_page
```
Overhead 最高的前幾名為 findName 與 __strcasecmp_l_avx 字串比較的部份,
若能夠改善 findName 的效能,對程式整體的速度影響較大。
## 優化一:縮小 struct
* 先看一下 Cache 的詳細資訊
```
Cache Information
Socket Designation: L1-Cache
Configuration: Enabled, Not Socketed, Level 1
Operational Mode: Write Back
Location: Internal
Installed Size: 32 kB
Maximum Size: 32 kB
Supported SRAM Types:
Other
Installed SRAM Type: Other
Speed: Unknown
Error Correction Type: None
System Type: Unified
Associativity: 8-way Set-associative
```
* findName 只查詢 lastName,entry 的其他資料會被搬移至cache,原本的entry佔136byte若將其他資料以令一個 strut details 儲存,可將 entry 縮小到 32byte,在 Cache 內可以存放更多的 entry,達到降低 Cache miss 的效果。
```C
typedef struct __DETAILS {
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];
} details;
typedef struct __PHONE_BOOK_ENTRY {
char lastName[MAX_LAST_NAME_SIZE];
details *data;
struct __PHONE_BOOK_ENTRY *pNext;
} entry;
```
* cache-misses 從 91.1% 降低為 58.18%
```
Performance counter stats for './phonebook_opt' (100 runs):
382,089 cache-misses # 58.184 % of all cache refs ( +- 0.17% )
656,691 cache-references ( +- 0.36% )
244,028,058 instructions # 1.63 insns per cycle ( +- 0.02% )
149,303,793 cycles ( +- 0.45% )
0.046472370 seconds time elapsed ( +- 1.08% )
```
* 時間比較圖
![](https://i.imgur.com/hYX3bzW.png)
```
Samples: 347 of event 'cycles:pp', Event count (approx.): 156687981
Overhead Command Shared Object Symbol ▒
19.20% phonebook_opt phonebook_opt [.] main ◆
13.37% phonebook_opt libc-2.23.so [.] _int_malloc ▒
12.93% phonebook_opt libc-2.23.so [.] __strcasecmp_l_avx ▒
7.09% phonebook_opt libc-2.23.so [.] _IO_getline_info ▒
6.31% phonebook_opt libc-2.23.so [.] __memcpy_sse2 ▒
6.23% phonebook_opt libc-2.23.so [.] _IO_fgets ▒
5.57% phonebook_opt libc-2.23.so [.] __strcpy_sse2_unaligned ▒
4.48% phonebook_opt phonebook_opt [.] append ▒
4.30% phonebook_opt libc-2.23.so [.] malloc ▒
3.91% phonebook_opt phonebook_opt [.] findName ▒
3.82% phonebook_opt libc-2.23.so [.] memchr ▒
2.02% phonebook_opt [kernel.kallsyms] [k] clear_page ▒
1.31% phonebook_opt [kernel.kallsyms] [k] release_pages ▒
0.84% phonebook_opt [kernel.kallsyms] [k] native_irq_return_iret ▒
0.73% phonebook_opt phonebook_opt [.] strcasecmp@plt ▒
0.69% phonebook_opt [kernel.kallsyms] [k] unmap_page_range ▒
0.58% phonebook_opt [kernel.kallsyms] [k] get_page_from_freelist ▒
0.56% phonebook_opt phonebook_opt [.] strcpy@plt ▒
0.56% phonebook_opt [kernel.kallsyms] [k] handle_mm_fault ▒
0.55% phonebook_opt libc-2.23.so [.] _IO_getline ▒
0.53% phonebook_opt [kernel.kallsyms] [k] __rmqueue.isra.79
```
## 優化二:使用hash function
* hash 原理:雜湊函式 把訊息或資料 (key) 壓縮成摘要,使得資料量變小,將資料的格式固定下來。該函式將資料打亂混合,重新建立一個叫做雜湊值的指紋。
* 好的 hash function 可以減少碰撞 (collison),碰撞指的是 不同的 key 卻得到相同的雜湊值。
* 解決碰撞的方法有兩種
1. 封閉式雜湊法(closed hashing, open addressing)
collision發生時,無法儲存的資料會被儲存到雜湊表中的其它 位置。線性偵測法與平方偵測法都屬於此法。
2. 開放式雜湊法(open hashing, separate chaining)
collision發生時,無法儲存的資料會被儲存到雜湊表之外的空間
* 在這次作業當發生碰撞時,利用原本 entry 的 pNext 建立一個 list,可以把它看作將 phonebook 分冊, hash function 找到第幾冊,再用原本 findName 找到資料。
* Table size 使用質數,可降低碰撞機率,且數值越大碰撞機率越低,但會使用較多記憶體空間。
* 程式碼如下,思考在不修改main之下是否有更簡潔的寫法?
```clike=
#define TABLESIZE 5393
entry **entryArray=NULL;
unsigned int BKDRHash(char *str)
{
unsigned int seed = 131; // 31 131 1313 13131 131313 etc..
unsigned int hash = 0;
while (*str){
hash = hash * seed + (*str++);
}
return (hash % TABLESIZE);
}
entry *findName(char lastName[], entry *pHead)
{
unsigned int hashvalue = BKDRHash(lastName);
pHead = *(entryArrayHead+hashvalue);
while (pHead != NULL) {
if (strcasecmp(lastName, pHead->lastName) == 0)
return pHead;
pHead = pHead->pNext;
}
return NULL;
}
entry *append(char lastName[], entry *e)
{
if(entryArray == NULL) {
entryArray = (entry **)malloc((TABLESIZE+1)*sizeof(entry *));
entryArrayHead = (entry **)malloc((TABLESIZE+1)*sizeof(entry *));
}
unsigned int hashvalue = BKDRHash(lastName);
e = *(entryArray+hashvalue);
if(*(entryArrayHead+hashvalue) == NULL) {
e = (entry *) malloc(sizeof(entry));
*(entryArrayHead+hashvalue) = e;
}
e->pNext = (entry *) malloc(sizeof(entry));
e = e->pNext;
strcpy(e->lastName, lastName);
e->pNext = NULL;
*(entryArray+hashvalue) = e;
return e;
}
```
* 修改 calculate.c、runtime.gp來紀錄資料與繪圖,可以發現由於 hash 版本的 append 時間太短,須另外找方式紀錄,而 append 的時間增加,試著找方法優化 append。
>runtime.gp 如何取消自動超連結?
>[color=blue][name=zhanyangch]
* 修改 main 和 calculate 即可 double 精度可以到小數點 15 位,diff_in_second 提供 nanosecond (10^-9^) 精度, 將 findName 的時間顯示 %lf 改為 %.9lf
![](https://i.imgur.com/5RQBVVX.png)
* cache miss 降低至 35%
```
Performance counter stats for './phonebook_opt_hash' (100 runs):
306,952 cache-misses # 35.750 % of all cache refs ( +- 0.08% )
858,599 cache-references ( +- 0.26% )
242,237,830 instructions # 1.49 insns per cycle ( +- 0.02% )
162,039,957 cycles ( +- 0.28% )
0.064788502 seconds time elapsed ( +- 3.89% )
```
* 想法:利用平行化處理 append, 但要注意每個 hash value 對應的 list 只有一個 thread 可以修改,使用的函式是否為 thread-safe,在 POSIX 標準下 malloc 為 thread-safe,可以使用。
參考資料
==
[gnuplot 語法解說和示範](https://hackmd.io/s/Skwp-alOg#)
[觀察 Linux 的虛擬記憶體](http://blog.linux.org.tw/~jserv/archives/002039.html)
[淺談memory cache](http://opass.logdown.com/posts/249025-discussion-on-memory-cache)
[關於 CPU Cache:程序猿需要知道的那些事](https://read01.com/0A6Dxg.html)
[hash function 觀念和實務](https://hackmd.io/s/HJln3jU_e#)
[Hashing原理介紹](http://note-whu.rhcloud.com/2015/09/27/hashing%E5%8E%9F%E7%90%86%E4%BB%8B%E7%B4%B9/)
[Is malloc thread-safe?](http://stackoverflow.com/questions/855763/is-malloc-thread-safe)
[ 在 Linux 中以特定的 CPU 核心執行程式](https://blog.gtwang.org/linux/run-program-process-specific-cpu-cores-linux/)