# 2016q3 Homework1 (phonebook) contributed by <`Quexint`> ## 報告 ### 大綱 - [作業須知](https://hackmd.io/s/S1RVdgza) ### 環境 - OS: Arch Linux 4.7.4-1-ARCH - CPU: Intel(R) Core(TM)2 Quad CPU @ 2.40GHz - MEM: 4G - GCC: 6.2.1 ### 規格 給定 N 筆字串,然後給定 Q 筆搜索,求搜索時間最短。 $N \leq 500000$ $Q \leq 500000$ $L_i \leq 16$ ### 實驗 對於每種版本,記錄執行 100 次實驗的時間。 每次實驗 N 為 349990,Q 為 3499。 ### 比較 - `Trie` 的建構時間是 `Linked-List` 的 5 倍左右。 - `Trie` 的搜索時間是 `Linked-List` 的 12000 倍左右。(27.397376:0.002337, $O(NL):O(L)$) ![image alt](https://d17oy1vhnax1f7.cloudfront.net/items/1Y0d441x0z2U3a0u3E3r/runtime.png?v=c2a8f115) ## 開發記錄 ### `v1.0` Structure Compression 根據影片中的提示,先對 structure 做瘦身。 ```c typedef struct __PHONE_BOOK_ENTRY { char lastName[MAX_LAST_NAME_SIZE]; struct __CONTENT_ENTRY *contentPtr; struct __PHONE_BOOK_ENTRY *pNext; } entry; typedef struct __CONTENT_ENTRY { char firstName[16]; char email[16]; char phone[10]; char cell[10]; char addr1[16]; char addr2[16]; char city[16]; char state[2]; char zip[5]; } content; ``` ```c entry *append(char lastName[], entry *e) { e->pNext = (entry *)malloc(sizeof(entry)); e = e->pNext; e->contentPtr = (content *)malloc(sizeof(content)); strcpy(e->lastName, lastName); e->pNext = NULL; return e; } ``` 發現沒比較好,而且 `append` 因為 `malloc` 兩次變慢了。 ![image alt](https://d17oy1vhnax1f7.cloudfront.net/items/2b2n0n2p2F432J2y280v/runtime-v1_0.png?v=2530d886) 結論:因為沒有做到多次的資料搬移,所以用指標取代的效益沒有出現。 #### `v1.1` `append` 加速 對 `append` 做瘦身:一次 `malloc` 好所需的大小再切割為兩塊。 ```c entry *append(char lastName[], entry *e) { e->pNext = (entry *)malloc(sizeof(entry) + sizeof(content)); e = e->pNext; e->contentPtr = (content *)(((uint8_t*)e) + sizeof(entry)); strcpy(e->lastName, lastName); e->pNext = NULL; return e; } ``` 結論:`append` 的速度跟之前差不多了。 ![image alt](https://d17oy1vhnax1f7.cloudfront.net/items/3J091g43112z0y1n1D0c/runtime-v1_1.png?v=2530d886) ### `v2a` Memory Comparison 想法:將 char 的 16 次比較降為 int 的 4次比較。 避免 `'\0'` 之後的雜訊,再 `mallc` 前,先清空記憶體。 ```c memset(e->lastName, 0, sizeof(char) * MAX_LAST_NAME_SIZE); strcpy(e->lastName, lastName); ``` 考慮到 branch prediction,放棄使用`if(a[0]==b[0] && a[1]==b[1]...)`,而改用`memcmp`。 結論:沒差。 ![image alt](https://d17oy1vhnax1f7.cloudfront.net/items/3H1Q1O0W1R2O0q343d3W/runtime-v2a.png?v=2530d886) ### `v2b` Trie 先重構 `main.c,phonebook_orig.{h,c}` 將 initial, free 寫成函式. 將 Key (lastName) 建構一顆 Trie,而 Value 則是 Sturct content。 結論:建構超慢(0.29xxx),搜索超快(0.0000),重構測試加多單一測試時的搜索次 數。 ![image alt](https://d17oy1vhnax1f7.cloudfront.net/items/383S2y3d3k0V1q1H0u1S/runtime-v2b.png?v=2530d886) ### 重構實驗 - 把 I/O 時間提出,純測 `append` 的時間。 ```c for(n = 0; fgets(last_name_record[n], sizeof(char) * MAX_LAST_NAME_SIZE, fp); n++) { i = 0; while(last_name_record[n][i] != '\0') i++; last_name_record[n][i - 1] = '\0'; } ``` ```c clock_gettime(CLOCK_REALTIME, &start); for(i = 0; i < n; i++) e = append(last_name_record[i], e); clock_gettime(CLOCK_REALTIME, &end); ``` - 將搜索次數提高到 1% * 資料數 ```c int test_num = max(n / 100, 1); int *test_array = (int *) malloc(sizeof(int) * test_num); for(i = 0; i < test_num; i++) test_array[i] = rand() % n; ``` ```c clock_gettime(CLOCK_REALTIME, &start); for(i = 0; i < test_num; i++) assert(NULL != findName(last_name_record[test_array[i]], e)); clock_gettime(CLOCK_REALTIME, &end); ``` 結論:測的出 Trie 的效益,但單一測試的 orig 會跑很久。 ``` size of entry : 136 bytes execution time of append() : 0.052206 sec execution time of findName() : 27.529620 sec ``` ``` size of entry : 216 bytes execution time of append() : 0.255502 sec execution time of findName() : 0.002327 sec ``` 單一測試下,Linked-List / Trie 的搜索時間複雜度為: Linked-List: O(L) * O(N) Trie: O(L)