# 2020q1 Homework 1 (lab0) contributed by < `hankchang805` > ###### tags : `linux2020` ## 開發紀錄 ### queue.h #### `queue_t` 為了讓 q_insert_tail 以及 q_size 可以在 $O(1)$ 的時間複雜度內完成,所以增加 `tail` (指向 queue 中最後一個元素)以及 `size` (紀錄目前 queue 有幾個元素) ```clike typedef struct { list_ele_t *head; list_ele_t *tail; int size; } queue_t; ``` ### queue.c #### `q_new` ```clike queue_t *q_new() { queue_t *q = malloc(sizeof(queue_t)); if (!q) return NULL; q->head = NULL; q->tail = NULL; q->size = 0; return q; } ``` #### `q_free` ```clike void q_free(queue_t *q) { if (!q) return; while (q->head) { list_ele_t *ptr = q->head; q->head = q->head->next; free(ptr->value); free(ptr); } free(q); } ``` #### `q_insert_head` ```clike bool q_insert_head(queue_t *q, char *s) { list_ele_t *newh; if (!q) return false; newh = malloc(sizeof(list_ele_t)); if (!newh) return false; char *in = malloc(sizeof(char) * (strlen(s) + 1)); if (!in) { free(newh); return false; } int len = strlen(s); strncpy(in, s, len); in[len] = '\0'; newh->value = in; newh->next = q->head; q->head = newh; if (!q->tail) { q->tail = q->head; } q->size = q->size + 1; return true; } ``` #### `q_insert_tail` ```clike bool q_insert_tail(queue_t *q, char *s) { list_ele_t *newt; if (!q) return false; newt = malloc(sizeof(list_ele_t)); if (!newt) return false; char *in = malloc((strlen(s) + 1) * sizeof(char)); if (!in) { free(newt); return false; } int len = strlen(s); strncpy(in, s, len); in[len] = '\0'; newt->value = in; newt->next = NULL; if (!q->tail) { q->head = q->tail = newt; q->size = q->size + 1; return true; } q->tail->next = newt; q->tail = newt; q->size = q->size + 1; return true; } ``` #### `q_remove_head` ```clike bool q_remove_head(queue_t *q, char *sp, size_t bufsize) { if (!q || !q->head) return false; if (sp) { strncpy(sp, q->head->value, bufsize); sp[bufsize - 1] = '\0'; } list_ele_t *ptr = q->head; q->head = q->head->next; q->size = q->size - 1; free(ptr->value); free(ptr); return true; } ``` #### `q_size` ```clike int q_size(queue_t *q) { if (q) return q->size; return 0; } ``` #### `q_reverse` ```clike void q_reverse(queue_t *q) { if (!q) return; list_ele_t *temp = q->head; list_ele_t *middle = NULL; list_ele_t *last = NULL; while (temp) { last = middle; middle = temp; temp = temp->next; middle->next = last; } temp = q->head; q->head = q->tail; q->tail = temp; } ``` #### `q_sort` 這邊是採用 MergeSort 的方式進行排序,`q_sort` 將 list 分成兩段,如果 list 大小是 2 的倍數則分成剛好 $size / 2$,若不為 2 的倍數則左半將會切成 $\lceil size/2 \rceil$,再將左右分別遞迴下去切直到遇到 Base Case ;接著執行 `q_merge` 將左右兩半排好 ```clike void q_sort(queue_t *q) { if (!q || q->size < 2) return; queue_t left; queue_t right; left.head = q->head; right.tail = q->tail; left.size = (q->size >> 1) + (q->size & 1); right.size = (q->size >> 1); list_ele_t *ptr = q->head; for (int i = 0; i < left.size - 1; i++) { ptr = ptr->next; } left.tail = ptr; right.head = ptr->next; left.tail->next = NULL; q->tail = q->head = NULL; q_sort(&left); q_sort(&right); q_merge(&left, &right, q); } ``` * `q_merge` ```clike void q_merge(queue_t *left, queue_t *right, queue_t *q) { list_ele_t *l = left->head; list_ele_t *r = right->head; if (less_than(l, r)) { q->head = l; l = l->next; } else { q->head = r; r = r->next; } q->tail = q->head; list_ele_t *cur = NULL; while (l && r) { if (less_than(l, r)) { cur = l; l = l->next; } else { cur = r; r = r->next; } q->tail->next = cur; q->tail = cur; } while (l) { q->tail->next = l; q->tail = l; l = l->next; } while (r) { q->tail->next = r; q->tail = r; r = r->next; } } ``` * `less_than` 比較 `list_ele_t` 的字典序 ```clike bool less_than(list_ele_t *l, list_ele_t *r) { char *str_l = l->value; char *str_r = r->value; while (*str_l && *str_r) { if (*str_l > *str_r) return false; else if (*str_l++ < *str_r++) return true; } if (!*str_l && *str_r) return true; return false; } ``` ## Valgrind 和 Massif 工具的使用 ### Valgrind 利用 `make valgrind` 來查看是否有 Memory leak ...等記憶體錯誤發生,此階段並沒有發生什麼記憶體失誤 ### 利用 Massif 查看記憶體的使用 用 `valfrind --tool=massif ./qtest` 來執行 Massif 工具的分析 實驗的方式如下: ``` ./qtest (qtest)new (qtest)ih RAND 10 (qtest)reverse (qtest)sort (qtest)rh ....10 times (qtest)quit ``` 再利用 `massif-visualizer [massif_output_file]` 來把實驗結果輸出成以下圖表 ![](https://i.imgur.com/pOp6yBb.png) 可以看見一開始直線上升,應該是在執行 insert head ,後面一段雖在下降但是稍微平緩一點,應該是在執行 reverse 跟 sort ,之後開始較劇烈下降是在執行 rh 直到結束之後記憶體用量就將至0 (**後續想再設計不同的方式去追蹤記憶體的使用**) ## TODO ## 研讀論文 Dude, is my code constant time?,解釋本程式的 “simulation” 模式是如何透過以實驗而非理論分析,達到驗證時間複雜度,需要解釋 Student’s t-distribution 及程式實作的原理; ## select 系統呼叫 解釋 select 系統呼叫在本程式的使用方式,並分析 console.c 的實作,說明其中運用 CS:APP RIO 套件 的原理和考量點。可對照參考 CS:APP 第 10 章重點提示 :arrow_forward: 為避免「舉燭」,請比照 qtest 實作,撰寫獨立的終端機命令解譯器 (可建立新的 GitHub repository) ## Reference [colinyoyo26](https://hackmd.io/xmc_rKZLSpm3IagR_yGVLw?view#%E5%AF%A6%E4%BD%9C%E9%81%8E%E7%A8%8B) [AndybnACT](https://hackmd.io/oUxknXCUQdWq847MVXIhcA?view#queuec) [Valgrind massif manual](https://valgrind.org/docs/manual/ms-manual.html)