# 2018q3 Homework3 (dict)
contributed by < `ChingChieh` >
###### tags: `2018q3`
## 開發環境
```
$ lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 4
On-line CPU(s) list: 0-3
Thread(s) per core: 2
Core(s) per socket: 2
Socket(s): 1
NUMA node(s): 1
Vendor ID: GenuineIntel
CPU family: 6
Model: 142
Model name: Intel(R) Core(TM) i7-7500U CPU @ 2.70GHz
Stepping: 9
CPU MHz: 500.067
CPU max MHz: 3500.0000
CPU min MHz: 400.0000
BogoMIPS: 5808.00
Virtualization: VT-x
L1d cache: 32K
L1i cache: 32K
L2 cache: 256K
L3 cache: 4096K
NUMA node0 CPU(s): 0-3
```
## bench.c bug
一開始先來實作讓 ./test_ref --bench 可以執行時發現產生出來的 bench__cpy.txt 有很多次的時間都是 0.00000 於是先去看 bench_test() 後發現這個函式裏面又呼叫了 tst_search_prefix 而且計時是計算這個函式花了多少時間,然後利用 printf 大法在最後 return NULL 前面加上 printf("Not Found\n")。
```
while (curr) {
...
}
printf("Not Found\n");
retunr NULL;
```
結果也時如預期:
```
...
Not Found
Not Found
Not Found
...
```
去看了一下發現問題是在test_bench裏面:
```
char prefix[3] = "";
...
strncpy(prefix, word, 3);
tst_search_prefix(root, prefix, sgl, &sidx, max);
```
因為使用 strncpy(char *dest, const char *src, size_t n) 時如果 src 前 n byte 沒有 NULL byte 那 dest 裡的 string 會被視為從 dest 開始到下一個 NULL byte,所以很可能包含原本沒有的 character 所以才會找不到。
此外在 tst_search_prefix 中有個if判斷式是多餘的,根本不會跑進去:
```c=
int diff = *s - curr->key; /* calculate the difference */
if (diff == 0) { /* handle the equal case */
/* check if prefix number of chars reached */
if ((size_t)(s - start) == nchr - 1) {
/* call tst_suggest to fill a with pointer to matching words */
tst_suggest(curr, curr->key, nchr, a, n, max);
return (void *) curr;
}
// -----------以下的if不會跑進去---------
if (*s == 0){ /* no matching prefix found in tree */
return (void *) curr->eqkid;
}
// -----------------------------------
s++;
curr = curr->eqkid;
}
```
因為當 *s == 0 發生之前一定會先跑進前面的 if 然後就 return 了
test_ref.c 沒有 free memory pool 因為 tst_free 只有 free tst 上的每個 node
```c=
void tst_free(tst_node *p)
{
if (!p)
return;
tst_free(p->lokid);
if (p->key)
tst_free(p->eqkid);
tst_free(p->hikid);
free(p);
}
```
所以要在 quit 時加上 free(pool):
```c
quit:
case 'q':
tst_free(root);
free(pool);
return 0;
break;
```
## test_cpy 和 test_ref 的差別
#### 建立 TST 時儲存字串的 memory 空間來源
* test_ref
```c=
/* memory pool */
char *pool = (char *) malloc(poolsize * sizeof(char));
char *Top = pool; //top is for recording top of the pool
while ((rtn = fscanf(fp, "%s", Top)) != EOF) {
char *p = Top;
/* insert reference to each string */
if (!tst_ins_del(&root, &p, INS, REF)) { /* fail to insert */
fprintf(stderr, "error: memory exhausted, tst_insert.\n");
fclose(fp);
return 1;
} else { /* update bloom filter */
bloom_add(bloom, Top);
}
idx++;
Top += (strlen(Top) + 1);
}
```
先建立一個可以放的下整個 cities.txt 裏面全部字串的 memory pool,然後利用 Top 指標依序把讀到的字串放到 pool 中。
因為已經建立好 memory pool 所以在 tst_ins_del 中要把字串存起來只需要
```c
curr->eqkid = (tst_node *) *s;
```
* test_cpy
這個版本在建立 TST 時是透過每讀到一個新的字串就 malloc 一塊空間來存相同的字串:
```c=
const char *eqdata = strdup(*s);
if (!eqdata)
return NULL;
curr->eqkid = (tst_node *) eqdata;
```
## gnuplot 初體驗
修改 Makefile 增加 plot 功能
```
plot:
gnuplot scripts/runtimept.gp
eog runtime2.png &
```
## perf 初體驗
* 測試./test_ref --bench
```
$ perf stat -e cache-misses,cache-references,instructions,cycles ./test_ref --bench
ternary_tree, loaded 259112 words in 0.162133 sec
Performance counter stats for './test_ref --bench':
1,923,015,911 cache-misses # 64.523 % of all cache refs
2,980,334,289 cache-references
47,108,955,394 instructions # 0.88 insn per cycle
53,604,126,759 cycles
18.511945817 seconds time elapsed
```
* 測試./test_cpy --bench
```
$ perf stat -e cache-misses,cache-references,instructions,cycles ./test_cpy --bench
ternary_tree, loaded 259112 words in 0.149182 sec
Performance counter stats for './test_cpy --bench':
1,749,974,080 cache-misses # 64.071 % of all cache refs
2,731,310,762 cache-references
47,123,815,504 instructions # 0.98 insn per cycle
48,108,397,687 cycles
15.847157502 seconds time elapsed
```
發現 test_ref 花比較多時間,因為 cache-misses 多了一億多次
> 究竟是發生了什麼事,待研究