# 2017q1 Homework4 (phonebook-concurrent)
contributed by < `laochanlam` >
###### tags: `laochanlam`
## 開發環境
- Ubuntu 16.10 ( 64 bit )
```
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 4
On-line CPU(s) list: 0-3
Thread(s) per core: 2
Core(s) per socket: 2
Socket(s): 1
NUMA node(s): 1
Vendor ID: GenuineIntel
CPU family: 6
Model: 61
Model name: Intel(R) Core(TM) i7-5500U CPU @ 2.40GHz
Stepping: 4
CPU MHz: 2400.878
CPU max MHz: 3000.0000
CPU min MHz: 500.0000
BogoMIPS: 4788.90
Virtualization: VT-x
L1d cache: 32K
L1i cache: 32K
L2 cache: 256K
L3 cache: 4096K
NUMA node0 CPU(s): 0-3
```
## 相關連結
- [2017 年春季作業說明](https://hackmd.io/s/Bk-1zqIte#)
- [2017q1 Homework4 (作業區)](https://hackmd.io/s/Hkxl2Zcpx)
- [B07: phonebook-concurrent 作業要求](https://hackmd.io/s/rkOExTOoe#)
- [課程進度與開放資源 Wiki](http://wiki.csie.ncku.edu.tw/sysprog/schedule)
## 開發紀錄
先研究一下 phonebook-concurrent 的整個架構。
先研究一些神奇的函式orz。
```C
int fd = open(ALIGN_FILE, O_RDONLY | O_NONBLOCK);
```
[這裡](http://c.biancheng.net/cpp/html/238.html)有關於 open() 各種參數的說明。
[這裡](http://c.biancheng.net/cpp/html/326.html)有關於 stat() 各種參數的說明。
```C
map = mmap(NULL, file_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
```
[這裡](http://c.biancheng.net/cpp/html/138.html)有關於 mmap() 函式的一些說明。
看了良久,終於有點頭緒,先來記錄一下原來優化版本的執行時間:
![Imgur](http://i.imgur.com/Sipct8q.png)
所進行的優化有:
1. 對齊
2. 使用 mmap 防止 I/O Bound
3. 使用 Memory
4. POSIX Programming
### Refactoring
參考完[東霖學長的共筆](https://hackmd.io/s/ByiOeFYie#)後,嘗試用類似的方式實現 OOP 的程式架構。
然後要來實現一下 Thread Pool 了!
### Thread pool
在~~偷~~看完 [mbrossard](https://github.com/mbrossard/threadpool) 的 Thread pool 以及 [xdennisx 的 Github](https://github.com/xdennisx/phonebook-concurrent) 後,開始實作:
![Imgur](http://i.imgur.com/1XtF0XT.png)
似乎得到了跟我預期不太同的結果...該來驗證一下程式碼...
### Remove
隨後實作了 link-listed remove, 並用 ```assert(removeName(input, e))``` 確定正確性,若有 Remove 失敗則回傳 NULL。
## 補充知識
- [x] Concurrency & Parallel
- [x] CPU bound & I/O bound
- [ ] Lock-Free
- [x] POSIX Thread
---
## Concurrency & Parallel
### Concurrency (並行)
![](https://i.imgur.com/rweOyiD.png)
Concurrency 是指一種"程式架構",把需要被執行的程式拆分成很多不同的小工作進行。
此處強調 **拆分** 。
實用場景:
- 單核心 CPU ( 多個任務運行在同一 CPU 上)
---
### Parallel (平行)
<img src="https://i.imgur.com/Oom3wM5.png" width="700" height="140" />
<br>
>話說我現在才知道 Markdown 可以寫 HTML lol
>[name=laochanlam][color=blue]
Parallel 是指可使程式分拆成幾個小工作同時執行,可用多條 Thread 等實作方法執行。
此處強調 **平行** 。
:::info
**Concurrency 可能會用到 parallelism,
但不一定要用 parallelism 才能實現 concurrency。**
:::
#### 實用場景:
- 多核心 CPU ( 多個任務運行在多個 CPU 上)
#### 參考及引用資料
[Toward Concurrency](https://hackmd.io/s/Skh_AaVix#)
---
## CPU bound & I/O bound
### CPU bound
CPU bound 是指大部分的狀況都是 CPU 在進行運算,相比下 CPU loading 較高, I/O 讀寫所佔的時間很短,所以說效能被 CPU "bound" 住了。
### I/O bound
I/O bound 指 CPU 的 loading 較低,大部份時間都在執行 I/O的讀寫,效能被 I/O "bound" 住。
:::info
資料壓縮/解壓縮主要是增加 CPU 的 Loading ,平衡兩者。
:::
#### 參考及引用資料
[CPU bound? I/O bound?](http://delphi.ktop.com.tw/board.php?cid=30&fid=66&tid=90309)
---
## Lock-Free
![](https://i.imgur.com/XAbt1fh.png)
簡單來說,就是每個 thread 的執行之間不會相影響 ( block )。
#### 參考及引用資料
[Toward Concurrency](https://hackmd.io/s/Skh_AaVix#)
---
## POSIX Thread
先來理解一下[Toward Concurrency](https://hackmd.io/s/Skh_AaVix#)中所包含的範例 Code。
```C
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define NUM_THREADS 3
#define TCOUNT 10
#define COUNT_LIMIT 12
int count = 0;
int thread_ids[3] = {0,1,2};
pthread_mutex_t count_mutex;
pthread_cond_t count_threshold_cv;
void *inc_count(void *t)
{
long my_id = (long)t;
for (int i = 0; i < TCOUNT; i++) {
pthread_mutex_lock(&count_mutex);
count++;
// Check the value of count and signal waiting thread when condition is
// reached. Note that this occurs while mutex is locked.
if (count == COUNT_LIMIT) {
pthread_cond_signal(&count_threshold_cv);
printf("inc_count(): thread %ld, count = %d Threshold reached.\n",
my_id, count);
}
printf("inc_count(): thread %ld, count = %d, unlocking mutex\n",
my_id, count);
pthread_mutex_unlock(&count_mutex);
/* Do some "work" so threads can alternate on mutex lock */
sleep(1);
}
pthread_exit(NULL);
}
void *watch_count(void *t)
{
long my_id = (long) t;
printf("Starting watch_count(): thread %ld\n", my_id);
/*
Lock mutex and wait for signal. Note that the pthread_cond_wait
routine will automatically and atomically unlock mutex while it waits.
Also, note that if COUNT_LIMIT is reached before this routine is run by
the waiting thread, the loop will be skipped to prevent pthread_cond_wait
from never returning.
*/
pthread_mutex_lock(&count_mutex);
while (count < COUNT_LIMIT) {
pthread_cond_wait(&count_threshold_cv, &count_mutex);
printf("watch_count(): thread %ld Condition signal received.\n", my_id);
count += 125;
printf("watch_count(): thread %ld count now = %d.\n", my_id, count);
}
pthread_mutex_unlock(&count_mutex);
pthread_exit(NULL);
}
int main (int argc, char *argv[])
{
int i, rc;
long t1 = 1, t2 = 2, t3 = 3;
pthread_t threads[3];
pthread_attr_t attr;
/* Initialize mutex and condition variable objects */
pthread_mutex_init(&count_mutex, NULL);
pthread_cond_init (&count_threshold_cv, NULL);
/* For portability, explicitly create threads in a joinable state */
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_create(&threads[0], &attr, watch_count, (void *)t1);
pthread_create(&threads[1], &attr, inc_count, (void *)t2);
pthread_create(&threads[2], &attr, inc_count, (void *)t3);
/* Wait for all threads to complete */
for (i = 0; i < NUM_THREADS; i++)
pthread_join(threads[i], NULL);
printf ("Main(): Waited on %d threads. Done.\n", NUM_THREADS);
/* Clean up and exit */
pthread_attr_destroy(&attr);
pthread_mutex_destroy(&count_mutex);
pthread_cond_destroy(&count_threshold_cv);
pthread_exit(NULL);
}
```
---
#### pthread_cond_wait(&count_threshold_cv, &count_mutex);
#### pthread_cond_signal(&count_threshold_cv);
wait 用於阻塞目前的 Thread , 等待另外一個 Thread 的 signal 的到來 (mutex 會在 wait 期間被 unlock , signal 到來後又 lock 回去 )。
例子:
A Thread:
```C
pthread_mutex_lock(&mut);
while (x <= y) {
pthread_cond_wait(&cond, &mut);
}
/* operate on x and y */
pthread_mutex_unlock(&mut);
```
B Thread:
```C
pthread_mutex_lock(&mut);
/* modify x and y */
if (x > y) pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mut);
```
---
#### pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
函式原型為
```C
pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)
```
```datachstate``` 的參數可選擇為
##### ```PTHREAD_CREATE_DETACHED```(分離執行緒)
會在執行緒完成任務後直接釋放系統資源,不需等待```pthread_join()```。
##### 及 ```PTHREAD _CREATE_JOINABLE```(非分離執行緒)
反之,需要等待```pthread_join()```返回才會釋放系統資源。
---
#### pthread_create(&threads[0], &attr, watch_count, (void *)t1);
函式原型為
```C
int pthread_create(pthread_t *tid , const pthread_attr_t *attr , void *(*function)(void *) , void *argument)
```
產生一個Thread及執行Function,附有4個參數。
:::info
參數1:
pthread_t *tid為pthread的指標,在使用Thread之前必須要先宣告一個pthread_t的變數。
參數2:
const pthread_attr_t *attr 為該 Thread 的屬性,預設是NULL。
參數3:
void *(*function)(void *) 為 Function pointer ,這邊要放入需要被執行的Function。
參數4:
void *argument 為 Function pointer 所要帶的參數。
回傳值:
如果執行成功則回傳0,如果執行失敗則回傳錯誤代碼。
:::
---
#### void pthread_exit (void *value_ptr)
這個 Function 的作用是用來關閉一個 Thread ,附帶有1個參數。
:::info
參數1:
void *value_ptr 用來設定執行成功時該 Thread 會回傳的值,這個值可由 pthread_join() 這個 Function 來取得。
回傳值:
不會回傳任何值。
:::
---
#### int pthread_cancel (pthread_t thread)
關閉一個指定的 Thread ,附帶有一個參數。
:::info
參數1:
pthread_t thread 為要關閉的 Thread 。
回傳值:
如果執行成功則回傳0,如果執行失敗則回傳錯誤代碼。
:::
---
#### int pthread_join (pthread_t thread, void **value_ptr)
等待目標 Thread 執行完畢後,目前執行 pthread_join 的 Thread (呼叫者) 才會繼續執行。
:::info
參數1:
pthread_t thread為要等待的目標Thread。
參數2:
void \**value_ptr 用來取得目標 Thread 的回傳值。
回傳值:
如果執行成功則回傳0,如果執行失敗則回傳錯誤代碼。
:::
---
#### 參考及引用資料
[Toward Concurrency](https://hackmd.io/s/Skh_AaVix#)
[线程的分离状态 pthread_attr_setdetachstate 函数使用 ](http://blog.csdn.net/sjin_1314/article/details/8023575)
[Pthread 程式撰寫](http://blog.xuite.net/nikoung/wretch/154071878-Pthread+%E7%A8%8B%E5%BC%8F%E6%92%B0%E5%AF%AB)
[pthread_cond_signal和pthread_cond_wait简介](http://www.eefocus.com/andrew_dj/blog/12-08/282740_85e58.html)
---