contributed by <LanKuDot
>
沒有絕對的 thread model
sort
:將輸入資料排序
-R
flag:隨機排序uniq
:可以用來將重複的行刪除而只顯示一個
-c
flag:顯示重複字詞的出現次數wc
:列出檔案有多少行,多少單字,多少字元
-l/w/m
:僅列出行、單字、字元:=
:Simply expanded variables,當 reference 這類變數時,其值等於當下被定義的值。
-MMD
:-M
的縮減板,列出指定檔案所需要的其他 header file,但只列 user 定義的 header files-MF <file>
:配合 -M
等 flag 使用,將所需的 header file 輸出到 file
。參考-include <file>
:告訴 Makefile 先忽略找不到 <file>
的問題,除非無法完成最後要完成的 target-rdynamic
:指定 linker 將所有 label 寫入到 dynamic symbol table。參考
list.c
intptr_t
:幫助轉換來自 void pointer 所指向的值。// stdint.h
#if __WORDSIZE == 64
# ifndef __intptr_t_defined
typedef long int intptr_t;
# define __intptr_t_defined
# endif
typedef unsigned long int uintptr_t;
#else
# ifndef __intptr_t_defined
typedef int intptr_t;
# define __intptr_t_defined
# endif
typedef unsigned int uintptr_t;
#endif
list_add
的註解提到如果要新增的 value 已經存在的話就不會新增這個 node,但是實做沒有對應的檢查。
list_add
的回傳值永遠是 0
list_nth
實際上是回傳第 n+1 個元素,因此當 idx == list->size
時回傳是 NULL
idx
的元素 (非第 idx 個),並改名為 list_get
沒有提供 free list 的 function,雖然在程式結束時仍然有有效的 pointer 指向這些區塊,但為求保險還是 free 掉
void (*func)(void *)
:pointer to function void foo(void *arg)
(*func)
括號起來,否則是宣告一個會回傳 pointer to void 的函式 void *func(void *)
在 C99/C11 中,有個重要的概念叫做 function designator,詳見 Value categories jserv
我一開始設計時是希望能做到像pipe那樣,可以把前一個task的回傳值傳給下一個task,所以在設計的時候的確是要宣告一個回傳pointer to void的函式,只是後來忘記實作就是了…LuisHsu
func
不是要指定工作開始的函式嗎,然後上個工作回傳的內容透過arg
傳遞。_task->func(_task->arg);
像這樣的用法LanKuDot
pthread_join
來取得回傳值task_free()
及 tqueue_init()
等函式都只會回傳 0,除非有特別意義,否則可以不用回傳值
tqueue_init()
增加檢查傳入 pointer 是否有效,如果是 NULL
則回傳 -1task_free()
短短的但問題很多
task_t
是 NULL
不代表 task_t->arg
也是 NULL
→ 第一個 free()
會有問題task_t->arg
是不是在 task 結束後還會用到
queue 操作不對,pop 從後面,push 從前面
tqueue_init()
不會配置記憶體給 task queue,只會初始化其 member,應該要檢查傳入指標是否有效
用 single linked-llist singly-linked list 也可以實現 queue
grep
來檢查 double linked list 的 last
pointer 使用,發現都只有 assign。-inr
:ignore case; show line number; recursive$ grep -inr --exclude=*.txt 'last' .
./threadpool.h:21: struct _task *last; ///< Pointer to the previous task
./threadpool.h:39: task_t *tail; ///< Pointer to the last _task\_t_ in queue
二進位格式檔案 ./.threadpool.h.swp 符合
./threadpool.c:47: the_queue->head->last = NULL;
./threadpool.c:79: task->last = the_queue->tail;
二進位格式檔案 ./.threadpool.c.swp 符合
"singly-linked list" 才是正確寫法,請注意 jserv
了解LanKuDot
main.c
the_list
task_run()
task_run()
是一個無限迴圈會一直從 task queue 請求工作,中止條件為收到對應函式為 NULL
的工作。執行完成後會將 task_t
free 掉。(void) data;
是為了避免編譯器發出未使用參數的警告,表明在函式中不會使用這個參數。參考可透過 gcc 的
__attribute__ ((unused))
來抑制警告,請見 unused parameter warnings in C code jserv
cut_func()
切割 the_list
MIN(thread_count, data_count) - 1
,當 data 比 thread 多時,可以將資料平均分配給 thread。(4 個 thread 分 3 次)cut_func()
,但是當切割次數滿了之後就會直接執行 merge_sort
和 merge
merge_sort()
:純粹將取得的 list 一直對半分,直到長度小於 2。結束時呼叫 merge_list()
組合左右兩邊並回傳組合後的 list。merge_list()
:merge local list,將左右兩個 list 做排序合成一個 list,會回傳新的 llist_t
,傳入的兩個會被 free 掉。merge()
:merge lists between threads。地一個進來的 thread 會檢查 tmp_list
是否為空,有則暫存自己的 local list,如果沒有則取得暫存的 list 和自己的 local list,配到 merge_list()
作為下一個工作。max_cut
算式過於冗長,可以用 MIN
macro 取代cut_func
在分左右時,註解寫左邊但傳入的是右半的 list,右邊則傳入左半merge_list()
與 merge_n_sort()
移到 merge_sort.c/h
merge_list()
為 merge_n_sort()
,merge_sort()
為 split_n_merge()
merge_list()
中被 free 掉的 llist_t
,其指標應設為 NULL
,避免被使用tmp_list
(for merging lists between threads)
cut_count
研究 concurrent-II 如何計算 scalabilty
scalability.sh
執行 1~N 個核心的運行測試,並與 1 核心的執行成果作比較。
評估 vertical scalability
scalability1.sh
與scalability2.sh
差別只有一次執行一個或兩程式
core
,shift
會讓所有參數左移 (原本第二個參數會變第一個)config
及 lock_exec
scriptprog
,剩下後接的參數到 params
core
核心的測試,並輸出與 core 1 的執行結果比較unlock_exec
scriptlock_exec
新增一個 /tmp/lock_exec_$(whoami)
的空檔案,並設定如果接收到 SIGINT
則把這個檔案刪除。但如果在新增檔案的時候該檔案存在的話,就不允許新開的測試進行 (代表前一個測試程式還在運作)
trap command signal
:如果接收到 signal
就執行 command
2> /dev/null
:將 stderr(fd=2)
的訊息導入 /dev/null
,也就是不輸出unlock_exec
刪除 tmp/lock_exec_$(whoami)
不知道這兩個 script 的功用,主程式也沒有使用到,純粹創檔刪檔!?LanKuDot
我懂了,目的在於防止使用者同時進行同一個測試。LanKuDot
config
將從 run_II.sh
傳入的 cores
指定的項目轉為對應的數列
GENERIC_PRINTF
的 macro,以針對不同的資料型態作對應的輸出gettimeofday(2)
來測量 elapsed time,從第一個 task 被 push 到 queue 中開始。
getrusage(2)
來得到 kernel time 和 user time,用 USAGE_SELF
會得到所有 thread 的使用時間加總。用 USAGE_THREAD
則會得到 calling thread 的使用時間,但 main thread 在等待 join 的這段時間都會一直使用 CPU 嗎?task_pop()
中來計算被消耗的 task 數,新增 member num_of_consumed
到 tqueue_t
中,在呼叫 tqueue_free()
時會回傳這個值。#Total_tasks_consumed: 14
#Elapsed_time: 0.648 ms
#Throughput: 21604 (per sec)
$ ./sort 4 /tmp/run_input.txt
#Total_tasks_consumed: 14
#Elapsed_time: 43.012 ms
#Throughput: 325 (per sec)
$ ./sort 64 /tmp/run_input.txt
#Total_tasks_consumed: 254
#Elapsed_time: 447.635 ms <--- 時間大大增加
#Throughput: 567 (per sec)
為了減少 critical section 的使用,所以作一點變化,不讓個別 task 去切 list,而是建立一個專門切割 list 的 task。
只要切好 local list 就會 push 到 task queue。
我在 makefile 中新增一個 check
target 用以測試排序完的結果是否正確,流程如下:
gen-random-numebrs.sh
產生隨機變數並輸出到測試檔案中sort -g
將測試檔案作排序以作為 ground truthcompare.sh
比較輸出檔與 ground truth,並提示 verify 的結果node->data
的結構,所以只能是指標或是 long int
,想要用 hw2 的方法,將輸入資料 map 到 process 的虛擬記憶體,再讓 thread 使用讓程式去排序 10000 筆數字,4 thread
sysprog2016