# 2017q1 Homework4 (phonebook-concurrent)
###### tags: `embedded`
contributed by <`Cayonliow`>
## 作業
* 作業說明: [B07: phonebook-concurrent](https://hackmd.io/s/rkOExTOoe#)
* 參考資料: [Toward Concurrency](https://hackmd.io/s/Skh_AaVix#), [李昆憶學長的共筆](https://hackmd.io/s/HJFhaiAp#) , [The Free Lunch Is Over ](http://www.gotw.ca/publications/concurrency-ddj.htm), [Concurrency is not parallelism](https://blog.golang.org/concurrency-is-not-parallelism)
## 背景知識
![](https://i.imgur.com/zgFrAqJ.png)
這張圖我在計組上課的時候有看過, 跟老師說的一樣
* 以前 CPU 增進效能的手段
* Clock speed
* Execution Optimization
* Pipelining、Branch prediction
* Out-of-order execution 還要注意不能讓原本程式崩潰 (read/write reorder)
* Cache:盡量減少存取 Main memory 的機會
可是依上圖, Perf/Clock(ILP), Power 跟 Clock Speed 在 2000 年就出現飽和的現象, 所以增進效能的手段也跟着改變
* 現在 CPU 增進效能的手段
* Hyperthreading: 在 single CPU 上同時執行多個 thread,但是共用 ALU、FPU
* Multicore:多個 CPU
* Cache
[Free (performance) lunch](http://www.gotw.ca/publications/concurrency-ddj.htm) 指的是程式設計的效能可以透過 CPU 時脈的進步而得到改善。會說 over 是因為 CPU 的時脈無法得到更進一步的增加,由於耗電和散熱的問題,所以程式設計師必須要修改程式才能改善效能
### Concurrency
* 使用 Concurrency 的理由:
1.分離可獨立運作的工作;
2.為了效能,像是使用多核新的 CPU
* 可是並非所有的程式都合適平行化;所以程式要設計可以實行 Concurrency 是很困難的
* 如果程式的相依性太高或是太常取用共同資源則會拖慢 Concurrency 的效率
* 對軟體設計的影響
1. 想要完全使用到 CPU 所有的資源
2. 程式越來越有機會造成 CPU-bound。雖然主要還是 IO-bound 等,但如果 CPU 時脈無法增加,而其他存取方式速度變快,最後會發生 CPU-bound
3. 效能優化將會越來越重要
4. 程式語言強迫必須好好處理 concurrency
* A happends-before B : 在 B 開始執行之前, A 的執行效果必須可見。
* A is Sequenced-Before B : 在同一個 thread 下,A 比 B 先求值。
* A is synchronized-with B : A 在這一個 thread 對記憶體的操作,在另一個 thread 的 B 必須可見。
* Sequential consistency : 無論處理器實際怎麼執行,確保結果跟照程式順序執行一樣
* Read before Write : 在新值寫入前讀取到舊值
* Write after Write : 在正確值寫入後被舊值複寫 (有先後)
* Concurrency is not parallelism
* Concurrency 是指程式架構, 將程式拆開成多個可獨立運作的工作
* eg:drivers, 都可以獨立運作,但不需要平行化。
* 拆開多個的工作不一定要同時運行
* 多個工作在單核心 CPU 上運行
* 工作可拆分成「獨立執行」的部份, 這樣「可以」讓很多事情一起做, 但是「不一定」要真的同時做。
* 展示具有並行性,但不去同時執行
* 並行性是種「架構程式」的概念。寫下一段程式之前, 思考問題架構時就決定好的。
* ![](https://i.imgur.com/V62wZ5s.png)
* Parallelism 是指程式執行, 同時執行多個程式. Concurrency 可能會用到 parallelism,但不一定要用 parallelism 才能實現 concurrency。
* eg:Vector dot product
* 程式會同時執行 (例如:分支後,同時執行,再收集結果)
* 一個工作在多核心 CPU 上運行
* 把規劃好, 能夠並行的程式, 分配給不同執行緒, 並讓他們同時執行。
* ![](https://i.imgur.com/6X2tDXw.png)
## 開發記錄
一開始先 make plot 看看結果
![](https://i.imgur.com/0M5Dbbg.png)
發現 `append()` 的效能提升了很多, 所用的時間甚至比 `findName()` 來的少
然後參考 [csielee 的共筆](https://hackmd.io/s/ByiOeFYie#) 分析 concurrent 的實作
主要是在 append() 的時候將工作分給多個 thread 一起做
並使用下面的方法,讓多個 thread 可以順利 concurrent
* 對齊
* 利用 text_align 對齊 `DICT_FILE`(要讀取的名字檔案)
* [open()](http://c.biancheng.net/cpp/html/238.html)
* `O_RDONLY`: 以只讀方式打開文件
* `O_NONBLOCK`:以不可阻斷的方式打開文件,也就是無論有無數據讀取或等待,都會立即返回進程之中
```
text_align(DICT_FILE, ALIGN_FILE, MAX_LAST_NAME_SIZE);
int fd = open(ALIGN_FILE, O_RDONLY | O_NONBLOCK);
off_t file_size = fsize(ALIGN_FILE);
```
* 對映
* 把 `ALIGN_FILE` 的檔案內容利用 mmap() 記憶體對映到記憶體當中
* 同時利用對齊後的檔案大小去計算出需要幾個 entry,利用一次 malloc 把需要的記憶體空間一次分配好
* [mmap()](http://c.biancheng.net/cpp/html/138.html)
* `PROT_READ`:映射區域可被讀取
* `PROT_WRITE`:映射區域可被寫入
* `MAP_PRIVATE`:對應射區域的寫入操作會產生一個映射文件的複制,即私人的“寫入時復制”(copy on write)對此區域作的任何修改都不會寫回原來的文件內容 。
* 在調用mmap()時必須要指定MAP_SHARED或MAP_PRIVATE。
```
map = mmap(NULL, file_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
assert(map && "mmap error");
entry_pool = (entry *)malloc(sizeof(entry) *
file_size / MAX_LAST_NAME_SIZE);
assert(entry_pool && "entry_pool error");
```
* 分配
* 最後將這些東西分配進每個 thread,這樣互相執行起來, 就可以解決同時讀取同一個檔案的檔案指標 critical 問題,跟頻繁的 malloc
```
for (int i = 0; i < THREAD_NUM; i++)
// Created by malloc, remeber to free them.
thread_args[i] = createThead_arg(map + MAX_LAST_NAME_SIZE * i, map + file_size, i,
THREAD_NUM, entry_pool + i);
```
* merge linked list
* 最後等每個 thread 執行完,就把每個 thread 各自產生的 linked list 合併起來
```
for (int i = 0; i < THREAD_NUM; i++) {
if (i == 0) {
pHead = thread_args[i]->lEntry_head->pNext;
DEBUG_LOG("Connect %d head string %s %p\n", i,
pHead->lastName, thread_args[i]->data_begin);
} else {
e->pNext = thread_args[i]->lEntry_head->pNext;
DEBUG_LOG("Connect %d head string %s %p\n", i,
e->pNext->lastName, thread_args[i]->data_begin);
}
e = thread_args[i]->lEntry_tail;
DEBUG_LOG("Connect %d tail string %s %p\n", i,
e->lastName, thread_args[i]->data_begin);
```