# 2017q1 Homework4 (phonebook-concurrent)
contributed by <`baimao8437`>
## 認識 Concurrent (並行)
- 工作可拆分成「獨立執行」的部份(decomposition), 這樣「可以」讓很多事情一起做, 但是「不一定」要真的同時做。
- Concurrency 可能會用到 parallelism,但不一定要用 parallelism 才能實現 concurrency。
這兩句話很簡單的可以理解並行與平行的差別
### 背景知識[^first]
1. Evaluation
- value computations:對一串運算式計算的結果
- side effect:也就是對物件狀態的修改
2. Sequenced-Before
對同一個thread下,求值順序關係的描述
3. Happens-Before
程式碼「效果」的呈現順序 舉例:
````
A = B + 1; // (1)
B = 1; // (2)
```
Compiler 最佳化編譯後組語為:
```
movl B(%rip), %eax
movl $1, B(%rip) //(2) finish
addl $1, %eax
movl %eax, A(%rip) //(1) finish
```
雖然後者先結束 但看起來效果像先 1 再 2 即符合 Happens-Before
4. Synchronized-with
就是跨 thread 版本的happens-before
*ps.Sequenced-before 其實就是同一個 thread 內的 happens-before*
5. Sequential Consistency
- 對於每個獨立的處理單元,執行時都維持程式的順序(Program Order)
- 整個程式以某種順序在所有處理器上執行
6. Lock-Free
還是有lock 但不會影響到其他執行續的運行(non-blocking),不一定比非lock-free快
7. Coroutines
讓原本循序執行的陳述指令,得以做出交錯執行的結果,由 programmer 控制
[^first]: [Concurrency 系列文章](http://opass.logdown.com/posts/797957-concurrency-series-4-be-with-the-synchronizes-with-relation)
## Code Refactoring
「在不改變軟體的外在行為之下,改善既有軟體的內部設計」
重構的主要目的就是為了提升可讀性,以及為了日後有了需求的變化時,可以更容易因應修改或是擴充。
一些常見的 「Bad smell」[^second]
- Duplicated Code
在作業中,常因為要測試很多種方法,而使某些程式碼不斷的重複,可使用:
- Extract method
- Form Template Method
- Long Method
Programming people have realized that the longer a procedure is, the more difficult it is to understand.
- 有可另外提出的程式碼: Extract method
- 太多的 temporary variables: Replace Temp with Query
- Comments
需要comment表示這段程式碼充滿妨礙閱讀的 bad smell,先 refactoring 後,往往就不需要 comment 了
- 需要註解一段程式碼: Extract Method
- 需要註解的程式碼已是 method: Rename Method
- 需要註解執行程式的必要條件: Introduce assertion(老師常用!)
[^second]: [Refactoring](https://www.csie.ntu.edu.tw/~r95004/Refactoring_improving_the_design_of_existing_code.pdf)
## 實驗實作
### 開發環境
```
baimao@baimao-Aspire-V5-573G:~$ 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
型號: 69
Model name: Intel(R) Core(TM) i5-4200U CPU @ 1.60GHz
製程: 1
CPU MHz: 1711.242
CPU max MHz: 2600.0000
CPU min MHz: 800.0000
BogoMIPS: 4589.38
虛擬: VT-x
L1d 快取: 32K
L1i 快取: 32K
L2 快取: 256K
L3 快取: 3072K
NUMA node0 CPU(s): 0-3
```
### 原始效能分析
記得 `echo 3 | sudo tee /proc/sys/vm/drop_caches` 清空 cache
- `$ ./phonebook_orig `
```
size of entry : 136 bytes
execution time of append() : 0.111719 sec
execution time of findName() : 0.005537 sec
```
- perf orig
```
Performance counter stats for './phonebook_orig' (100 runs):
1,202,610 cache-misses # 78.679 % of all cache refs ( +- 0.49% )
1,528,504 cache-references ( +- 0.19% )
320,439,956 instructions # 1.48 insn per cycle ( +- 0.02% )
216,931,003 cycles ( +- 0.15% )
0.087285532 seconds time elapsed ( +- 0.27% )
```
- `./phonebook_opt ` 這裡預設的 thread_num = 4
```
orginal file size = 3206080
size of entry : 24 bytes
execution time of append() : 0.012572 sec
execution time of findName() : 0.003665 sec
```
- perf opt THREAD=4
```
Performance counter stats for './phonebook_opt' (100 runs):
387,281 cache-misses # 23.459 % of all cache refs ( +- 0.47% )
1,650,873 cache-references ( +- 0.71% )
247,715,431 instructions # 1.43 insn per cycle ( +- 0.09% )
173,636,294 cycles ( +- 0.64% )
0.091929529 seconds time elapsed ( +- 1.40% )
```
- gnuplot
![](https://i.imgur.com/OYz61ZP.png)
#### **分析**
opt 版本主要實作了以下去優化 append 效能:
- text align
- 將所有資料的長度對齊 輸出至 align.txt
- 不夠長補'\0' 太長截斷
- fsize 會回傳整的檔案的大小(bytes)
- entry_pool
- 依據 text align 的 fsize 去一次 malloc 全部 entry
- [mmap](http://welkinchen.pixnet.net/blog/post/41312211-%E8%A8%98%E6%86%B6%E9%AB%94%E6%98%A0%E5%B0%84%E5%87%BD%E6%95%B8-mmap-%E7%9A%84%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95)
- 將 align.txt 映射至虛擬記憶體 VMA(Virtual Memory Area)
- 好處:高速檔案存取、執行緒共享記憶體
- pthread
- 預設 thread num = 4
- 每個 thread 工作量一樣 (append data = 87475)
至於 findName() 時間也變快的原因 下面 show_entry 後就可以看出來
針對特定測資就會出現一些誤差 預設搜尋的`zyxel`
在 orig 版本 所在位置在 349891 行
而用 opt 版本 由於順序是會被打亂的 此次出現在 262423 行 所以搜尋的時間花費較少 但速度其實是一樣的
#### **正確性**
*append:*
利用 `phonebook_opt.c`定義的`show_entry`來檢視用 pthread 建立的 linked list 是否與來源(words.txt)一致
可以發現
- 順序是被打亂的
- words.txt 有 349900行 show_entry只有349896行 少四行
發現原本程式中有點小錯誤 再串接各 thread 建立的 list 時
```
pHead = thread_args[0]->lEntry_head->pNext;
...
for (int i = 1; i < THREAD_NUM; i++) {
e->pNext = thread_args[i]->lEntry_head->pNext;
...
```
應改為
```
pHead = thread_args[0]->lEntry_head;
...
for (int i = 1; i < THREAD_NUM; i++) {
e->pNext = thread_args[i]->lEntry_head;
...
```
行數就正確了
*findName:*
opt 版的 findName 有點小疑惑 建好 list 後 show_entry 為 349900 行沒錯
但不知道為何 findName 完後再執行 show_entry 就變成 349899 少了一行
回頭看完[學長影片](https://www.youtube.com/watch?v=sWtWslsr4Ac)才知道這是故意設計成這樣的
但我下面要實作 delete 所以設計回 findName 完後可以 show_entry 出完整 list 的版本
```c
while (pHead) {
if (strncasecmp(lastname, pHead->lastName, len) == 0
&& (pHead->lastName[len] == '\n' ||
pHead->lastName[len] == '\0')) {
// pHead->lastName[len] = '\0';
```
#### **再優化**
- **merge linked list improve[^third]**
- **use macro at thread set**
```c
for (int i = 0; i < THREAD_NUM; i++)
thread_args[i] = createThread_arg(map + MAX_LAST_NAME_SIZE * i, map + file_size, i,THREAD_NUM, entry_pool + i);
for (int i = 0; i < THREAD_NUM; i++)
pthread_create(&threads[i], NULL, (void *)&append, (void *)thread_args[i]);
```
改寫成
```
for (int i = 0; i < THREAD_NUM; i++)
THREAD_SET(i);
```
time(100 run):`append()0.004217 findName() 0.005007`
append 時間減少 一個設定好就先跑的概念
[^third]: [老師給東霖的意見](https://hackmd.io/s/ByiOeFYie#最後-merge-linked-list)
### 實作 Delete Name
- orig 時間:
```
execution time of append() : 0.052377 sec
execution time of delete() : 0.005598 sec
execution time of findName() : 0.005562 sec
```
- opt 時間:
```
execution time of append() : 0.004075 sec
execution time of delete() : 0.003510 sec
execution time of findName() : 0.003502 sec
```
在建完 list 後 可以透過指令`DELETE(name)`來刪除特定的資料
delete 的速度應該跟 findName 是差不多的
### Refactoring
- 改用之前修改的時間工具 Stopwatch 測量時間
- 參考[老師的API包裝法](https://github.com/sysprog21/matrix_oo/blob/master/stopwatch.c) 包裝了原本的 Stopwatch
- 將 orig & opt 版本包裝成 API , `phonebook_orig.h` & `phonebook_opt.h` 合併成 `phonebook.h` 實現多型(polymorphism)
重構之後 程式碼好閱讀多了 依照階段來完成任務 並會自動對應到不同版本的實作 成就感UPUP~
~~然後剩不到一天 還剩兩個作業...~~