owned this note
owned this note
Published
Linked with GitHub
# 2016q3 Homework2 (phonebook-concurrent)
contributed by <`jkrvivian`>
## 預期目標
* 學習第二週提及的 [concurrency](/s/H10MXXoT) 程式設計
* 學習 POSIX Thread
* 學習效能分析工具
* code refactoring 練習
* 探索 clz 的應用
## 資料閱讀
### Concurrency 系列
* multithread底下有問題,常是因執行順序的不確定性
* **Sequenced-before**: 對 **==同一個==** thread下求值的描述
* evaluation(求值)
* value computation (如:left-to-right associativity)
* side effect (修改記憶體的值 呼叫Library)
A is not sequenced-before B and B is not sequenced-before A ==>執行順序不一定,求值時可能會重疊,優化後的執行順序會交錯
* 雖然訂定了許多evaluation order,但仍有一些未定義的行為
* i = i++
### [concurrency系列(二)](http://enginechang.logdown.com/posts/788206-concurrency-series-2-starting-from-sequenced-before)
value computation V.S. side effect
> 有點混亂了@@
> i++這類的後置運算子,value computation會先於side effect
>>對於assignment operator而言(=, +=, -=之類的),會先進行運算元的value computation,之後才是assigment的side effect, 最後是整個assigment expression的value computation[color=red]
* **happens-before** : 前一個操作的效果在後一個操作執行之前必須要 **可見**
* 執行順序不一定就如程式所撰寫的順序一樣
* A happens-before B != A happening before B
* 執行順序不一定是A在前
* 只要A在B執行前可見就好
* 同一個thread內sequenced-before就是happen-before
* C++定義能夠建立跨thread間的happens-before
* 1) A synchronizes-with B (A和B有適當的同步)
* 2) A is dependency-ordered before B (A和B有相依的順序關係)
* 3) A synchronizes-with some evaluation X, and X is sequenced-before B
* 4) A is sequenced-before some evaluation X, and X inter-thread happens-before B
* 5) A inter-thread happens-before some evaluation X, and X inter-thread happens-before B
* **synchornized-with** :兩個不同thread間的同步行為,當A synchronized-with B的時,代表A對記憶體操作的效果,對於B是可見的。而A和B是兩個不同的thread的某個操作
* ***跨thread版本的happens-before***
* `synchronized` in java
* mutual exclusive
* 只允許一個thread使用
* 建立happens before關係
* 確保每次對物件的修改都會對接下來的thread可見
* `volatile` in java
* 不同thread對變數的讀寫是atomic
* 都會讀到最新的值
* **memory consistency models**: 系統要想辦法在保證 **正確** 的情況下,盡可能的最佳化,讓程式跑的又快又好
* 只是一種 **幻象約定** 只要結果跟約定好的定義相同就好,改變執行順序也沒關係
* sequential consistency:
* 對於每個獨立的處理單元,執行時都維持程式的順序(Program Order)
* 整個程式以某種順序在所有處理器上執行
* cache architecture 與 sequential consistency
* cache coherence:確保不同處理器上的cache在系統中保持一致
* detecting the completion of write operations
* maintaining the illusion of atomicity for writes
### 冼鏡光的並行計算投影片
* 平行(parallel):兩件事齊頭並行
* 並行(concurrent):兩件事中交錯執行、但每一瞬間每件事都有點進展 ==> 一般電腦就是一個並行系統
### Concurrency (並行) vs. Parallelism (平行)
* 影片
* **concurrent** : a composition of independently process(executing things) ==> typically functions
* ***dealing*** a lot of things at once
* goal: good structure
* 不一定要平行
* **parallel** : a simultaniously execution of multiple things
* ***doing*** a lot of things at once
parallelism can come from better expression of concurrent problem
### Process vs. Thread vs. Coroutines
* **Process**: 包括了一個執行中的程式,和一些他所需的系統資源,諸如檔案描述表和位址空間等
* **Threads**: 一個程式計數器、一個堆疊再加上一組暫存器,掌握了所有的執行活動,『共享』系統資源
* **coroutines**: 多工實做技術,合作式多工
過往接觸過的:
A每次呼叫B或C都是從兩人的最開始執行到最後再回到A
![](https://i.imgur.com/CvRbSYw.png)
coroutines:
每次都是回到ABC三者的中斷點再接續執行,而不是從頭開始
![](https://i.imgur.com/XyzGTbW.png)
:::warning
若有使用到local變數,那執行結果可能就不如預期了
>解決辦法:allocate **separate** stack space for each function that is running as a thread[color=red]
:::
* 讀[Jserv老師的部落格](http://blog.linux.org.tw/~jserv/archives/001848.html)還有文中提到的經典文章[coroutines in C](http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html),完全不知道switch可以這樣用(跪),還沒完全看懂,研讀中~
* 用switch其實就是goto的效果!
>a case statement is still legal within a sub-block of its matching switch statement
### POSIX Threads
#### [Getting Started With POSIX Threads 宋振華](http://www.csie.ntu.edu.tw/~r92094/c++/pthread.txt)
* thread同步問題
* POSIX 提供
* **mutex**: 控制共享資源存取的一組函數
* **condition variable**: 能夠做到讓某一個 thread 能暫停,並等待另一個 thread 的信號(signal),當信號來了,thread 就醒過來,然後將相關的mutex lock 起來,為mutex功能的延伸
* **semaphore**: 透過整數運算,所有semaphore的初始值為1
* wait(),decrement直到為0時block
* post(),increment
:::warning
pthread_create 之前定義或在其所呼叫的函數中定義的變數來完成其工作,並將他的成果經由整體變數合併。對這些大家都可以存取的變數,我們必須加以控制。
>回想之前實作raytracing使用omp進行優化時,就是因此才需要使用`private()`保護變數[color=orange]
:::
* 檢查thread函式是否正確執行是必要的,若失敗大多數都回傳-1
* [condition varaible](https://www.cs.mtu.edu/~shene/NSF-3/e-Book/MONITOR/CV.html)
* **critical section**:
* **reentrant**:
* **mutual exclusion**:
## 驗證程式碼正確性及效能
### opt效能
* **Thread_num = 4**
```
size of entry : 24 bytes
execution time of append() : 0.009720 sec
execution time of findName() : 0.004989 sec
```
* cache misses約49.5%
```
Performance counter stats for './phonebook_opt' (100 runs):
774,269 cache-misses # 49.546 % of all cache refs ( +- 1.73% )
1,562,737 cache-references ( +- 1.64% )
225,877,891 instructions # 1.25 insns per cycle ( +- 0.04% )
181,307,063 cycles ( +- 1.23% )
0.129546375 seconds time elapsed ( +- 6.50% )
```
* plot
* 圖中可以明顯看見findName()的時間與orig版本差異不大,append()下降許多
* 應想辦法降低findName()時間 ==> 雖然使用hash function可以大大降低,但這樣append()的時間又會拉高@@
![](https://i.imgur.com/2bpNQd8.png)
### 正確性
* `phonebook_opt.c`中已有個`show_entry()`的function可以列出所有entry中的last name,因此可以與原有的`words.txt`比對
* 打開opt版本的字典檔發現與原本的`words.txt`不同
:::warning
直接看是不對的啊!因為輸出檔沒有排序,直接看下來當然會不同@@
:::
* 執行`diff`指令發現多處不同,其一原因是`phonebook_opt`的output並沒有sort,因此先執行`sort`指令排序
* 執行結果:
發現少了前4組
```
0a1,4
> aaaa
> aaaaa
> aaaaaa
> aaaaaaa
```
* 修正
```C==
for (int i = 0; i < THREAD_NUM; i++) {
if (i == 0) {
pHead = app[i]->pHead->pNext;
dprintf("Connect %d head string %s %p\n", i, app[i]->pHead->pNext->lastName, app[i]->ptr);
} else {
etmp->pNext = app[i]->pHead->pNext;
dprintf("Connect %d head string %s %p\n", i, app[i]->pHead->pNext->lastName, app[i]->ptr);
}
etmp = app[i]->pLast;
dprintf("Connect %d tail string %s %p\n", i, app[i]->pLast->lastName, app[i]->ptr);
dprintf("round %d\n", i);
}
```
把`pHead = app[i]->pHead->pNext`和`etmp->pNext = app[i]->pHead->pNext`裡的`pNext`拿掉就對了
## mmap
* creates a new mapping in the virtual address space of the calling process.
```C==
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);
```
## Code Refactoring
refactoring(重構)的定義:「在不改變軟體的外在行為之下,改善既有軟體的內部設計」
[甚麼是refactoring](http://teddy-chen-tw.blogspot.tw/2014/04/refactoring.html)有提到22種壞味道
* `main.c`58行開始及77行include和define搬到最前面,個人習慣,看起來比較整齊
```C==
#ifndef OPT
#include "file.c"
#include "debug.h"
#include <fcntl.h>
#ifndef THREAD_NUM
#define THREAD_NUM 4
#define ALIGN_FILE "align.txt"
#endif
```
* Line48 與 Line95 `struct timespec mid`雖然有`gettime()`,但之後完全沒有用到,所以直接刪除
* `struct timespec mid`刪除後就可以合併前後的`#ifndef OPT`內的程式碼,並把`struct timespec start, end`搬到外面
```C==
#ifndef OPT
FILE *fp;
int i = 0;
char line[MAX_LAST_NAME_SIZE];
struct timespec start, end;
double cpu_time1, cpu_time2;
/* check file opening */
fp = fopen(DICT_FILE, "r");
if (!fp) {
printf("cannot open the file\n");
return -1;
}
#else
file_align(DICT_FILE, ALIGN_FILE, MAX_LAST_NAME_SIZE);
int fd = open(ALIGN_FILE, O_RDONLY | O_NONBLOCK);
off_t fs = fsize(ALIGN_FILE);
#endif
```
* Line92的兩個for回圈可以合併
```C==
for (int i = 0; i < THREAD_NUM; i++) {
app[i] = new_append_a(map + MAX_LAST_NAME_SIZE * i, map + fs, i, THREAD_NUM, entry_pool + i);
pthread_create( &tid[i], NULL, (void *) &append, (void *) app[i]);
}
```
## 參考資料
[課程指定材料](https://hackmd.io/s/H10MXXoT#process-vs-thread-vs-coroutines)
[吳彥寬學長共筆](https://hackmd.io/s/BkN1JNQp#2016q3-homework1-phonebook)
[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)
###### tags: `system embedded` `HW 2`