# phonebook-concurrent contributed by <`HuangWenChen`> , <`aweimeow`> ### 預期目標 * 以足夠大量的資料來測試結果,且資料需要以真實姓名測試,而非亂數產生之無意義文字 * 撰寫過濾信賴區間之程式,把頭尾等極端的資料去除掉 >>1.請補上「預期目標」 >>2.中英文關鍵字中間要使用空白隔開喔! >>[color=red][name=課程助教] # Refactoring ## 何謂重構(refactoring) * 參考閱讀 [ Refactoring - Improving the Design of Existing Code](https://www.csie.ntu.edu.tw/~r95004/Refactoring_improving_the_design_of_existing_code.pdf) Refactoring 是以一種方式改變軟件系統的過程,「在不改變軟體外部行為的前提下,改變其內部結構」,使其更容易理解且易於修改。 就是可以改進軟體設計、減少錯誤、增加可讀性或者簡化結構而不影響輸出結果。 ## 什麼時候該重構 1. 三次法則 (The Rule of Three) 簡單來說三次做相似的事情時,就可以將它們使用重構。 事不過三,三則重構。 2. 添加功能時重構 (Refactor When You Add Function) 3. 修補錯誤時重構 (Refactor When You Need to Fix a Bug) 4. 程式碼審查時重構 (Refactor As You Do a Code Review) 壞味道是指遇到什麼情況該使用重構。而在書中提及到共有22種壞味及各種改善方法。 22種壞味道如下: duplicated code、long method、large class、long parameter list、divergent change、shotgun surgery、feature envy、data clumps、primitive obsession、switch statements、parallel inheritance hierarchies、lazy class、speculative generality、temporary field、message chains、middle man、inappropriate intimacy、alternative classes with different interfaces、incomplete library class、data class、refused bequest、comments 改善方法就大概略過。 # Concurrency * 參考資料 * [Concurrency系列](http://enginechang.logdown.com/posts/784600-concurrency-series-1-the-road-to-understand-concurrency) ### 名詞定義 * Sequenced-Before - 先完成之後才開始進行 * Happens-Before - 同一個 thread 前一個操作的效果在後一個操作執行之前必須要可見 * Synchronized-with - 不同的 thread 前一個操作的效果在後一個操作執行之前必須要可見 # SuperMalloc: A super Fast Multithreaded Malloc for 64-bit Machines ## Abstract * SuperMalloc 是一原先設計於 x86 HTM(Hardware Transcantional Memory) 的 malloc 實作 * 在 1 個 thread 時,SuperMalloc 比 DLmalloc, JEmalloc, Hoard, TBBmalloc 還要快 2.1 倍 * 在 8 個 threads 時,有 HTM 的情況之下,SuperMalloc 比上述所說的還要快 2.75 倍 * 在 32 個 threads 時,無 HTM 的情況之下,SuperMalloc 比上述所說的還要快 3.4 倍 * 一般而言,SuperMalloc 比其他 Memory Allocator 還要:更快、可擴充性更高、速度更快且程式碼更少。 * <s>SuperMalloc 很快就是了(Abstract 第一段的說明)</s> >> 不要過度簡化,這樣跟腦補有什麼差別?注意到論文提到的要素 [name=jserv] * 利用的特性是:在 x64 實體記憶體雖然很稀有(precious),但虛擬記憶體就相對容易取得 ## Intro malloc & free 影響著程式的執行效能(allocation 操作在多執行緒的環境會慢) 簡單來說可以把影響效能切分為三個部份:alloc 速度、footprint、複雜度 記憶體在 alloc 的時候,通常被分為 Virtual Allocation 和 Physical Allocation 兩種。 * System Call(mmap)會配置出一段連續的虛擬記憶體空間,此時並未真正分配實體記憶體給程式 * 但是當程式真正要存取記憶體的時候會產生 Page Fault,此時才會真正去 alloc 實體記憶體 * 如果這一塊虛擬記憶體有對應到實體記憶體,我們稱之為 `committed page`,反之則為 `uncommitted page` <s>DLmalloc 是 Linux 的預設 allocator</s>,他相對簡單且穩定,每一塊被配置的記憶體物件前面都會有 8 byte 的前綴,這表示:這一個物件的 Size、前一塊記憶體位置是正在使用的或是閒置(Free)的。 >> glibc 以 [ptmalloc3](http://www.malloc.de/en/) 為基礎 [name=jserv] 當 DLmalloc 把一個物件釋放掉的時候,也能夠立即將接續的下一個物件合併起來。 # Phonebook-concurrent ## 閱讀程式碼 ### file.c - file_align function * 主要是將檔案讀進來的字串都 padding (補白)成同一個長度大小。 >為什麼要這麼做是因為再利用 mmap 將檔案都讀進記憶體空間時,每個字串長度大小不同這樣還要額外去紀錄每個字串偏移量,如果都是同大小不需多這一步驟。 ```c= int main(int argc, char *argv[]) { assert(argc == 4 && "./main orgfName modfName Padded"); char *org = argv[1]; char *mod = argv[2]; int pad = atoi(argv[3]); printf("orginal file size = %ld\n", fsize(org)); FILE *fd0 = fopen(org, "r"); FILE *fd1 = fopen(mod, "w+"); char rbuf[MAX_BUFF_SIZE]; int suffix; // 這邊是 wbuf 的 new, 但是還沒 init, init 於 #38 char *wbuf = (char *) malloc(sizeof(char) * pad); // get data from fd0 to rbuf while (fgets(rbuf, sizeof(rbuf), fd0)) { // 把 wbuf 的 pad 個字元 都設定成 NULL memset(wbuf, '\0', pad); // pad - strlen(rbuf) == 0 時 != 不成立,所以不會做 strncpy if ((suffix = (pad - strlen(rbuf))) != 0) strncpy(wbuf, rbuf, strlen(rbuf)); // fwrite(*ptr, size, nmemb, *FILE_STREAM) // 看不懂 nmemb 是要幹嘛的參數 fwrite(wbuf, pad, 1, fd1); } fclose(fd0); fclose(fd1); return 0; } ``` ### file.c - fsize function * 利用 [stat()](http://blog.csdn.net/tigerjibo/article/details/11695763) 讀取檔案文件訊息,回傳一個檔案大小。 `int stat(const char *restrict pathname, struct stat *restrict buf)` mmap() 參考[記憶體映射函數 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) ,讓 thread 可以同時存取。 ``` void *mmap(void *start,size_t length, int prot, int flags, int fd, off_t offsize) ``` * `start` :指向欲映射的核心起始位址,通常設為 NULL,代表讓系統自動選定位址,核心會自己在進程位址空間中選擇合適的位址建立映射。 * `length`:代表映射的大小。 * `prot`:映射區域的保護方式。 * `flags`:影響映射區域的各種特性。 * `fd`:由 open 返回的文件描述符,代表要映射到核心中的文件。 * `offset`:從文件映射開始處的偏移量。 pthread_setconcurrency():設定系統明確的並行數量,預設為0。 [pthread_create()](http://blog.xuite.net/nikoung/wretch/154071878-Pthread+%E7%A8%8B%E5%BC%8F%E6%92%B0%E5%AF%AB) 使用方法 ``` int pthread_create(pthread_t *tid , const pthread_attr_t *attr , void *(*function)(void *) , void *argument) ``` ## 改善 * 參考資料 * [Free First-name and Last-name Databases ](http://www.quietaffiliate.com/free-first-name-and-last-name-databases-csv-and-sql/) 1. 資料要夠(要真實資料) 2. 以統計的方式率掉信賴區間以外的資料 3. 資料型態(分成資料結構大的與小的) 使用美國常見的姓氏 Last-name Databases 去做 append() 與 findName() #### flle_align ```c= while (fgets(rbuf, sizeof(rbuf), fd0)) { memset(wbuf, '\0', pad); // 直接把 if statement 拿掉了 strncpy(wbuf, rbuf, strlen(rbuf)); fwrite(wbuf, pad, 1, fd1); } ``` 因為真實姓名字典只有 16751 行,因此為了比較,將隨機(不真實)的字典數量減少至 16751 行 >> 調整下方圖表的 Y 軸,否則視覺化效果很差 [name=jserv] >> 已調整 ~![name=aweimeow] #### 原本的隨機(不真實)資料 ![](http://imgur.com/Xe47P56.png) #### 以真實人名測試之資料 ![](http://imgur.com/Zt2e8cX.png) 88797 筆的對照組 #### 隨機資料 ![](http://imgur.com/iZlv322.png) #### 真實資料 ![](http://imgur.com/oJGrk6z.png) ### 信賴區間 * 參考閱讀 * [估計與信賴區間](http://web.ydu.edu.tw/~alan9956/docu3/0991stat/Statistics_09.pdf) 參考[oiz5201618同學共筆](https://hackmd.io/s/BJdYsOPa#信賴區間)以直接運算母體算出 lower/upper endpoint 公式: * Lower Endpoint = avg(X) − 1.96 * σ * Upper Endpoint = avg(X) + 1.96 * σ 標準差公式(Standard deviation) ![](https://i.imgur.com/Cm2QVc0.png) 過濾95%以外的值。 所以在 calculate.c 檔案修改加上信賴區間算法 使用 `make plot` 產生圖表。 僅保留信賴區間之資料,結果對照如下: * 無過濾信賴區間 ![](http://imgur.com/gfImz2m.png) * 有過濾信賴區間 ![](http://imgur.com/dc6nBvy.png) >>記得更新github上程式碼 >>[color=red][name=課程助教]