github contributed by <Diana Ho
>
d0651
sys
phonebook_opt.c
和相關的檔案alloc()
的時間成本
名詞解釋
Concurrency is not Parallelism
比較
concurrency 問題:
lock 與 lock-free programming
核心目標:使程序間互相溝通順利
未來的效能效能提昇方向:
…
Thread 同步問題:mutex 和 condition variable
<pthread.h>
-lpthread
pthread_t a_thread;
pthread_attr_t a_thread_attribute;
void thread_function(void *argument);
char *some_argument;
int pthread_setconcurrency(int new_level);
int pthread_getconcurrency(void);
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);
int pthread_join(pthread_t thread, void **retval);
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, pthread_mutexattr_default);//定義mutex
pthread_mutex_lock( &mutex );//鎖定共享資源
pthread_mutex_unlock( &mutex );//解所共享資源
pthread_mutex_destroy();//結束mutex
Thread 缺陷
Thread Pool 功能
typedef struct {
void (*function)(void *);
void *argument;
} threadpool_task_t;
//Thread Pool 需要 job/task 讓 Thread 知道他們要做什麼事情
threadpool_t *threadpool_create(int thread_count, int queue_size, int flags);
//傳入 thread 的數量,queue 的大小,回傳 threadpool 型態是 threadpool_t
int threadpool_add(threadpool_t *pool, void (*routine)(void *), void *arg, int flags);
//傳入 threadpool,要執行的 function 位置,要傳入的參數
int threadpool_destroy(threadpool_t *pool, int flags);
//傳入 threadpool,會把 threadpool 的空間 free 掉
void *tpool_init(int num_threads)
//初始化時決定要用哪種方式進行 put work,選用 `ring-buffer` 的方式將 worker 加入queue
static int wait_for_thread_registration(int num_expected)
//確保所有執行緒都在準備中
int tpool_add_work(void *pool, void(*routine)(void *), void *arg)
//配合 `dispatch` 加入 task 到 work queue 中
static void *tpool_thread(void *arg)
//給 worker task,沒有用到 mutex_lock
static tpool_work_t *get_work_concurrently(thread_t *thread)
//利用 CAS 讓每個執行緒拿到自己的 task,確保從 work queue 出來時,遞減 `out` 的變數同步
void tpool_destroy(void *pool, int finish);
//判斷所有的 queue 是不是空的,確保所有 worker 完成工作
CAS
以具有 atomic 特性的計算操作來達成。
bool CAS(T* addr, T exp, T val)
{
if (*addr == exp) {
*addr = val;
return true;
}
return false;
}
$ git clone https://github.com/xhjcehust/LFTPool
$ cd LFTPool/
$ make
$ ./testtpool
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
mmap() 將檔案映射到一個 virtual memory address 上面,藉由直接對 memory address 修改的方式不用經過buffer,減少資料load到緩衝區的overhead,快速的記憶體存取可以提高對檔案 IO 的效率
Blocking VS Non-blocking I/O
addr
: 要 map 到的記憶體位置
length
: 要 mapping 的長度,它會從 fd + offset 的位置開始 mapping 檔案。prot
: 要保護的記憶體保護 mappingflags
: 選擇 map file 能不能被其他程式看到
fd
: 用系統函式 open 打開的檔案位置,open mode 可以選擇檔案的讀寫屬性,不能跟 prot 有衝突。offset
: 從檔案的哪裡開始 mapping,offset 必須是 page size 的倍數,可以用 sysconf(_SC_PAGE_SIZE) 取得。munmap() 用來取消參數 start 所指的映射記憶體起始地址,參數 length 則是欲取消的記憶體大小。當行程結束或利用 exec 相關函數來執行其他程式時,映射記憶體會自動解除,但關閉對應的文件描述詞時不會解除映射。
定義
「在不改變軟體外部行為的前提下,改變其內部結構,使其更容易理解且易於修改」。也就是說,在對外的介面上沒有做改變、介面背後的對應行為也沒有改變的情況下,基於可讀性,以及日後更便於修改的目的之下,來改寫內部的程式碼實作。
該執行重構的時機
Refactoring - Improving the Design of Existing Code
什麼是Refactoring
對於重構的兩則常見誤解
參考
參考 refactor 部分
dprintf
,與 printf
的用法一樣,只是會在前面增加 DEBUG:
的字樣,透過定義 DEBUG
macro 啟用。dprintf
功能不一樣,c library 的為將輸出寫入指定的 file descriptormmap
:將指定檔案的資料映射到 process 的 virtual memory space 上,可以防止 blocking I/O 的發生。munmap
:刪除配給的區塊,在程式結束時,這個區塊會自動被 delete 掉。
malloc
造成 waiting。
Performance counter stats for './phonebook_opt' (100 runs):
826,235 cache-misses # 42.230 % of all cache refs
1,986,333 cache-references
231,999,113 instructions # 1.00 insns per cycle
224,334,273 cycles
0.096829776 seconds time elapsed ( +- 1.72% )
phonebook_opt.h
中的 static double diff_in_second
加入 #ifdef DEBUG
& #endif
確認是否開啟 DEBUG 模式。$ make DEBUG=1
warning: variable ‘cpu_time’ set but not used [-Wunused-but-set-variable]
double cpu_time;
#ifdef DEBUG
static double diff_in_second(struct timespec t1, struct timespec t2);
#endif
main.c
中 opt 版本需引入的標頭檔集中在 main.c
的前面方便閱讀main.c
中的 for looppHead->pNext
,但應連接 pHead
才對
pHead = pHead->pNext;
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[0]->pHead;
dprintf("Connect %d head string %s %p\n", 0, app[0]->pHead->pNext->lastName, app[0]->ptr);
etmp = app[0]->pLast;
for (int i = 1; i < THREAD_NUM; i++) {
etmp->pNext = app[i]->pHead;
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);
}
phonebook_opt.c
中變數名稱
Performance counter stats for './phonebook_opt' (100 runs):
789,020 cache-misses # 37.669 % of all cache refs
2,168,562 cache-references
230,179,204 instructions # 1.07 insns per cycle
231,563,707 cycles
0.096281171 seconds time elapsed ( +- 1.38% )
threadpool_t *threadpool_create(int thread_count,
int queue_size,
int flags);
int threadpool_add(threadpool_t *pool,
void (*routine)(void *),
void *arg, int flags);
int threadpool_destroy(threadpool_t *pool, int flags);
Performance counter stats for './phonebook_opt' (100 runs):
753,916 cache-misses # 35.587 % of all cache refs
2,129,353 cache-references
238,219,831 instructions # 1.03 insns per cycle
223,924,023 cycles
0.095207583 seconds time elapsed ( +- 1.48% )