2017q3 Homework2(prefix-search) ======== contributed by <`loolofen`> ### Reviewed by `HexRabbit` * **Trie 介紹** 「但是因為沒有共同前綴( prefix ),所以耗費空間十分龐大」,敘述錯誤,Trie 跟 Ternary Search Tree 的其中一個相似點就是共同前綴,且空間龐大是因其產生的大量空指標造成。 * **Ternary search tree 介紹** 引用的TST圖表結構與作業中的實做不同,建議做出提示。 (思考先插入 cat 再 cats 的情況 string 的位置) * **test_ref 修正**「發現應該是要 free 掉 data storage external,而不是 data storage internal」,「看起來是沒有 free 到正確的 pointer」,用詞過於模糊, 無法從字面上理解究竟修正了什麼以及有什麼差別。 * **memory pool改進** 需要更多數據,圖表,分析! ## trie 及 ternary serch tree 介紹 ### trie 又稱為字典樹,是一種多分支的樹狀結構,像是英文單字就是有26個分支(a-z),阿拉伯數字就是10個分(0-9)。 e.g. 假設某字串只有五種字元(a,b,c,d,e)組合而成,那麼其trie結構如以下 ![](https://i.imgur.com/qNqME1t.png) ~~不過此種方法雖然簡單好實現,但是因為沒有共同前綴( prefix ),所以耗費空間十分龐大,如果電腦還要支援其它字元型態累積的耗費空間更是大中加大。~~ 空間龐大是因其產生的大量空指標造成。 trie 其特性如以下 : 1. 根節點不包含字元,除根節點以外,每個節點只包含一種字元。 2. 從根節點到某個節點,路徑上經過的字元連起來,就是該節點所對應的字串, 3. 每個節點的子節點所包含的字串都不相同。 ### ternary serch tree 和 trie 結構類似,但其每個節點只有三個分支,所以在進行插入節點操作的時候,只需判斷插入字元與當前節點關係(大於、小於、等於),結合了 trie 及 binary serch tree 的優點。 依查尋到的資料整理出幾個分類原則 1. left pointer : 插入值比所經節點值小,則存取 left pointer。 2. equal pointer : 插入值和所經節點值相等,則以插入字串的下一個字元存取 equal pointer。 3. right pointer : 插入值比所經節點值大,則存取 right pointer。 4. 若存取時遇到空節點,則插入字元,並存取 equal pointer 。 e.g. 插入方法如以下,若有四個單字 CAT,BUG,CATS,UP 按照順序插入。 1. 字串 C A T ,根節點為空節點插入 C,並存取 equal pointer,之後一一插入字元 2. 字串 B U G ,和根節點 C 比較,由於較小所以存取 left pointer 之後一一插入字元 3. 字串 C A T S ,和根節點 C 比較,由於相等所以以插入字串的下一個字元存取 equal pointer 4. 字串 U P , 和根節點比較,由於較大所以存取 right pointer 之後一一插入字元 ![](https://i.imgur.com/iIuQ8f0.png) ### 參考資料 [演算法筆記](http://www.cnblogs.com/edwinchen/p/4580190.html) [ternary serch tree](http://www.geeksforgeeks.org/ternary-search-tree/) ## 作業探討 ### 開發環境 使用 lscpu 查看CPU、快取內容 ``` Ubuntu 16.04.1 LTS CPU:Intel(R) Core(TM) i5-4460 CPU @ 3.20GHz L1d cache: 32K L1i cache: 32K L2 cache: 256K L3 cache: 6144K ``` ### 修改 test_ref.c 以下參考'st9007a'同學共筆 打開 test_ref.c 把所有 FIXME 底下的 CPY 皆改成 REF 並編譯得到以下結果。 輸入 Tain 就全部都出現 Tain 根本看不出 array 包含的內容。 ```= find words matching prefix (at least 1 char): Tain Tain - searched prefix in 0.000007 sec suggest[0] : Tain suggest[1] : Tain suggest[2] : Tain suggest[3] : Tain suggest[4] : Tain suggest[5] : Tain suggest[6] : Tain suggest[7] : Tain suggest[8] : Tain suggest[9] : Tain suggest[10] : Tain suggest[11] : Tain ``` 觀察以下 code 發現, cpy = 0 時,*s 會存入 curr->eqkid 中,而在 test_ref.c 中 , *s 對映到的是 word 。 因此 test_ref.c 中,所有 insert 操作的節點都存到以 word 為起點的連續記憶體位址的其中一個位址。 所以選用 strdup() ,其會先用 malloc() 配置與 word 相同的空間 ,存入到此位址,然後反回位址值。如此就可以讓他們的位址皆不同。 但每個都要配置額外的空間給他,此種方法十分沒有效率,也很容易增加 ~~cash~~ cache miss 的機會。 ```= void *tst_ins_del(tst_node **root, char *const *s, const int del, const int cpy) { ... for (;;) { ... if (*p++ == 0) { 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; } } pcurr = &(curr->eqkid); } } ``` 參考 `ChiuYiTang`同學共筆,發現單純把 CPY 修改成 REF 在 q 會有錯誤 ``` *** Error in `./test_ref': free(): invalid pointer: 0xbfc3e3dc *** ``` 以上錯誤看起來是沒有 free 到正確的 pointer。 所以觀查 tst.c 中的 tst_free_all () 發現還有一個 tst_free () 發現應該是要 free 掉 data storage external,而不是 data storage internal。 修改成以下,即可正常運作。 ```= case 'q': tst_free(root); return 0; break; ``` ### memory pool 參考 `ChiuYiTang` 同學共筆,引入 memory pool 的概念,給定一個經過需求精算過的記憶體空間。 :::danger 1. 「經過需求精算」是怎麼一回事?請算給我們看 2. 重新把定義找出來 :notes: jserv ::: 優點如以下 : * 避免造成 memory fragmentation * 避免了配置和釋放記憶體所花費的效能問題 * 記憶體間是緊密相鄰的,所以能提高系統cache命中率,進而提高軟體效能 不過缺點是在執行期間會一直佔用資源。 ```Clike /* the memory pool size */ #define MemoryPool 10000000 ... char *pptr = (char *) malloc(MemoryPool * sizeof(char)); char *pTop = pptr; ... /* Use memory pool top pointer to insert reference to each string */ while ((rtn = fscanf(fp, "%s", pTop)) != EOF) { char *p = pTop; if (!tst_ins_del(&root, &p, INS, REF)) { fprintf(stderr, "error: memory exhausted, tst_insert.\n"); fclose(fp); return 1; } idx++; pTop += (strlen(pTop) + 1);/* If memory exhausted, pTop++ for next memory */ } t2 = tvgetf(); ... ``` ### 參考資料 [ChiuYiTang 同學共筆](https://hackmd.io/s/ByeocUhnZ#) [st9007a 同學共筆](https://hackmd.io/s/SkeKQEq3Z#)