contributed by <HMKRL
>
jcyztw
test_cpy
和 test_ref
的效能分析時,用了 RDTSC
指令來幫你達成目的。但是根據以前修課同學的共筆及其相關影片,RDTSC
指令在多核心 CPU 的環境上作量測會有問題。所以你才會使用 chrt
和 taskset
兩個 command 來解決 RDTSC
不能在多核心 CPU 量測的限制嗎?amikai
建議先提及理論, 在對應到自己的 code, 報告會更有完整性
我找到一個函式 sched_setaffinity(), 可以試試看
tst_traverse_fn 這邊建議可以在介紹的詳細一點,因為這是很有用的函式, 並且可以舉個
實例
以上是小弟淺見, 如有冒犯請見諒
jserv
REF
模式根據程式碼註解:
if (cpy) { /* allocate storage for 's' */
const char *eqdata = strdup(*s);
if (!eqdata)
return NULL;
curr->eqkid = (tst_node *) eqdata;
return (void *) eqdata;
} else { /* save pointer to 's' (allocated elsewhere) */
curr->eqkid = (tst_node *) *s;
return (void *) *s;
}
REF
模式應該將字串另外儲存,理想情況應該直接採用原始資料儲存的位置,但在這個例子中原始資料是讀檔取得,因此無法直接使用
fopen
, fread
, fclose
) 替換為低階 I/O (如 open
, read
, close
)針對這個問題和 HTYISABUG 討論,我們想到可以將原始資料儲存起來,但若同樣採用 malloc
個別分配記憶體空間,則與 CPY
版本沒有差異,因此我們決定採用 mempool 儲存原始資料
實作 mempool 後發現無法正常完成 delete 操作,經 gdb 檢查找到問題在 tst.c
中:
if (del) { /* delete instead of insert */
(curr->refcnt)--; /* decrement reference count */
/* chk refcnt, del 's', return NULL on successful del */
- return tst_del_word(root, curr, &stk, 1);
+ return tst_del_word(root, curr, &stk, cpy);
}
這裡直接使用1傳入造成 delete word 時錯誤對 mempool 進行 free , 修改為傳入正確的模式變數 cpy
即可正常運作
首先,為兩個不同的 test 檔加入讀取 --bench
選項的部份:
/* process --bench flag */
int bench_flag = 0;
struct option long_opt[] = {
{"bench", no_argument, &bench_flag, 1},
{0, 0, 0, 0}
};
getopt_long(argc, argv, "", long_opt, NULL);
並加入以下計時用程式碼,嘗試計算 tst_search_prefix
所需時間
for (int i = 0; i < SEARCH_TIMES; ++i) {
fscanf(infp, "%3s", prefix);
t1 = tvgetf();
tst_search_prefix(root, word, sgl, &sidx, LMAX);
t2 = tvgetf();
fprintf(outfp, "%d %.6lf\n", i, t2 - t1);
}
但檢視輸出檔後發現由於搜尋所花時間太短,無法有效測量
為何不透過統計模型來分析呢?
"jserv"
此處消耗時間已低於本計時函式精確度,因此數據無法使用
"HMKRL
因此引入作業1中曾出現過的方法,利用 RDTSC 計算所消耗的 cpu cycle
for (int i = 0; i < SEARCH_TIMES; ++i) {
fscanf(infp, "%3s", prefix);
get_cycles(&timec_high1, &timec_low1);
tst_search_prefix(root, word, sgl, &sidx, LMAX);
get_cycles_end(&timec_high2, &timec_low2);
timec = diff_in_cycles(timec_high1, timec_low1, timec_high2, timec_low2);
fprintf(outfp, "%d %ld\n", i, timec);
}
你輸入 search 的 prefix 從頭到尾沒變啊
HTYISABUG
結果:
重新設定範圍消除過高資料後(來源應為作業系統其他背景工作):
搭配使用 chrt,將排程策略變更為 real-time FIFO,以及用 taskset 限定在某個/某些處理器上運作程式
"jserv"
使用 sudo chrt -f 99 taskset -c 0 ./test_cpy --bench
進行測試
並修改 gnuplot script 改用長條顯示使結果更明顯,得到以下結果:
計算標準差及95%信賴區間
CPY:
AVG = 1032.330220
SIGMA = 2273.788154
95%: 1023.575096 - 1041.085344
REF:
AVG = 998.183646
SIGMA = 1967.461852
95%: 990.608019 - 1005.759274
計算標準差及95%信賴區間
CPY:
AVG = 34.966300
SIGMA = 2.620935
95%: 34.914930 - 35.017670
REF:
AVG = 32.950000
SIGMA = 3.629959
95%: 32.878853 - 33.021147
其中 CPY
模式的 cache-miss 為 24.305%
REF
模式則為 19.859%
可見採用連續記憶體空間可以有效降低 cache-miss
從隨機事件的觀點,用 poisson distribution 解釋 REF 和 CPY 實作
jserv
此處對 tst_node
進行 forward declaration:
/* forward declaration of ternary search tree */
typedef struct tst_node tst_node;
這樣做可以帶來的優點:
typedef
讓後續程式碼可讀性增加(省略 struct
)tst_node
中的實作時,其他使用到 tst.h
的原始碼檔案不需要重新編譯,當專案規模增加時能節省許多編譯時間但使用上有條件限制:若程式碼中需要得知 sizeof(tst_node)
即無法使用(例如用到 malloc
或將 tst_node
用於其他 struct 中)
在 phonebook 中,也能以同樣的方法將 entry 做 forward declaration ,在 opt 和 origin 中在詳細定義,即可共用 phonebook.c
本 function 提供了一個 debug 介面,使用者可以使用自己的函式作為 function pointer 傳入,即可用於除錯/觀察資料結構/效能量測等用途