owned this note
owned this note
Published
Linked with GitHub
# 2017q1 Homework4 (phonebook-concurrent)
contributed by <`xdennisx`>
## 硬體規格
```shell
dennis@dennis-X550CC:~$ 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
型號: 58
Model name: Intel(R) Core(TM) i5-3337U CPU @ 1.80GHz
製程: 9
CPU MHz: 1270.898
CPU max MHz: 2700.0000
CPU min MHz: 800.0000
BogoMIPS: 3592.04
虛擬: VT-x
L1d 快取: 32K
L1i 快取: 32K
L2 快取: 256K
L3 快取: 3072K
NUMA node0 CPU(s): 0-3
```
## 分析實作
- text_align
```c=
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);
```
- `text_align` 將原本 `DICT_FILE` 的每一行名字都對齊 `MAX_LAST_NAME_SIZE` 的長度,長度太短就在後面補滿 `\0`,最後輸出成 `ALIGN_FILE`,這樣之後讀檔每一行的大小就都是一樣的,目的要讓記憶體對齊可加速讀取。
- `open` 為開啟檔案函式,第二個參數為開啟檔案的方式,若開啟成功則會回傳 0。
- `fsize` 會利用 `stat()` 回傳以 **byte** 為單位的檔案大小。
- [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_FILE` 直接對映到記憶體上,mmap 會回傳資料起始位址
```c=
map = mmap(NULL, file_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
```
- 順便利用計算過的 `file_size`,**一次**就把所有的 entry 給 malloc 好
```c=
entry_pool = (entry *)malloc(sizeof(entry) *
file_size / MAX_LAST_NAME_SIZE);
```
- pthread
- `pthread_setconcurrency(THREAD_NUM + 1)`
用來提示系統,表明希望的 thread 數量,這邊 `+1` 應該是希望能排除 main-thread。
[參考資料](http://www.cnblogs.com/nufangrensheng/p/3522583.html)說到,如果 CPU 同時一被分配給一個以上的 thread 的話,可能就會改善性能。
但是[這裡說到](http://man7.org/linux/man-pages/man3/pthread_getconcurrency.3.html)`LinuxThreads` 跟 `NPTL` 都是 1:1,用`pthread_setconcurrency` 好像沒啥用處@@
:::danger
應該使用繁體中文/台灣地區的慣用科技術語,請修改。不要輕易說「好像」,你做過實驗了嗎? --jserv
:::
> 好的老師,我會改進,發揮實驗精神 --xdennisx
根據我的硬體規格是 2 threads per core, 2 cores per socket.
嘗試用不同的 THREAD_NUM 去看結果
`n=3`
```shell
execution time of append() : 0.004465 sec
execution time of findName() : 0.001146 sec
```
`n=4`
```shell
execution time of append() : 0.008299 sec
execution time of findName() : 0.003174 sec
```
`n=5`
```shell
execution time of append() : 0.004265 sec
execution time of findName() : 0.001105 sec
```
`n=6`
```shell
execution time of append() : 0.006449 sec
execution time of findName() : 0.001518 sec
```
`n=7`
```shell
execution time of append() : 0.006740 sec
execution time of findName() : 0.004059 sec
```
`n=8`
```shell
execution time of append() : 0.007461 sec
execution time of findName() : 0.004209 sec
```
原本想用 `pthread_getconcurrency()` 看看是不是設定超過最大數量的 thread,`pthread_getconcurrency()` 會回傳一樣的值,不過結果我設多少他就回傳多少,而且 `pthread_setconcurrency()` 設多少效能上也沒有什麼太大的改變,從此判定 `pthread_setcomcurrency()` 在 Linux 上是沒什麼太大用處
- 我的 kernel model:
```shell=
dennis@dennis-X550CC:~/phonebook-concurrent$ getconf GNU_LIBPTHREAD_VERSION
NPTL 2.23
```
- 分配每個 thread 下去工作
```c=
for (int i = 0; i < THREAD_NUM; i++)
// Created by malloc, remeber to free them.
thread_args[i] = createThread_arg(map + MAX_LAST_NAME_SIZE * i, map + file_size, i,
THREAD_NUM, entry_pool + i);
```
- 最後再把他串起來
```c=
for (int i = 0; i < THREAD_NUM; i++) {
if (i == 0) {
pHead = thread_args[i]->lEntry_head->pNext;
} else {
e->pNext = thread_args[i]->lEntry_head->pNext;
}
e = thread_args[i]->lEntry_tail;
}
```
- 原始輸出圖
![](https://i.imgur.com/Wmf1zV4.png)
## 改善方法
### Thread pool
- ThreadPool 的基本原理是將要執行的工作放進一個 Queue 裡,然後啟動多條 Thread,每條 Thread 會去檢查 Queue 裡還有沒有工作要做,若有就取出來。
- 由於要管控每件工作只交給其中一個 Thread 去處理,所以得動用 **lock機制**;也就是同一時間只能開放一條 Thread 去檢查 Queue 並取出工作,可以想像成全部的 Thread 排成一列輪流存取。
- 在程式裡透過 `pool->count` 掌握工作何時全部做完,在遞減 `pool->count` 也要等同要啟動 lock 機制,任何時間下只允許一個 Thread 去修改它的值。
- 概念圖
![](https://i.imgur.com/hgIl84Y.png)
- `threadpool_add` function 就是把工作塞進去 Queue 裡面,`*threadpool_thread` function 就是把 Queue 裡面的工作分配給 Thread
- Queue 裡面每個元素
![](https://i.imgur.com/eR0nYqB.png)
- 參考 [0140454](https://github.com/0140454/phonebook-concurrent) 跟 [ierosodin](https://github.com/ierosodin/phonebook-concurrent) 實作 threadpool 結果...
![](https://i.imgur.com/xSYnD4J.png)
> Hmmm....哪邊出問題R 0.0
不過 cache-miss 有降
- phonebook_opt
```shell
Performance counter stats for './phonebook_opt' (100 runs):
731,497 cache-misses # 40.420 % of all cache refs ( +- 0.93% )
1,809,747 cache-references ( +- 1.81% )
246,807,242 instructions # 1.20 insns per cycle ( +- 0.12% )
205,503,773 cycles ( +- 1.06% )
0.083420525 seconds time elapsed ( +- 1.80% )
```
- phonebook_tp
```shell
Performance counter stats for './phonebook_tp' (100 runs):
789,868 cache-misses # 36.357 % of all cache refs ( +- 0.60% )
2,172,511 cache-references ( +- 1.40% )
259,023,470 instructions # 1.16 insns per cycle ( +- 0.05% )
223,875,263 cycles ( +- 0.71% )
0.079537321 seconds time elapsed ( +- 0.79% )
```
## Reference
[ierosodin](https://hackmd.io/s/rkCrQGbR)
[C 的 Thread Pool 筆記](http://swind.code-life.info/posts/c-thread-pool.html)
[Threadpool 比較](http://blog.darkthread.net/post-2010-01-02-multicore-1.aspx)