# 2019q3 Homework3 (list)
contributed by < `colinyoyo26` >
## 自我檢查事項
#### 為何 Linux 採用 macro 來實作 linked list?一般的 function call 有何成本?
==回答==
* 使用 macro preprocessor 會把 code 直接展開,而且不用重複寫相同的 code
* 一般 fucntion call 需要儲存 caller 所使用的暫存器以及傳遞 argument 的額外操作
#### Linux 應用 linked list 在哪些場合?舉三個案例並附上對應程式碼,需要解說,以及揣摩對應的考量
#### GNU extension 的 [typeof](https://gcc.gnu.org/onlinedocs/gcc/Typeof.html) 有何作用?在程式碼中扮演什麼角色?
==回答==
- 可以得到 operand 的 type
- 可以讓程式碼更有彈性且避免出錯,舉以下例子
- [GNU extension](https://gcc.gnu.org/onlinedocs/gcc/Typeof.html)
- The reason for using names that start with underscores for the local variables is to avoid conflicts with variable names that occur within the expressions that are substituted for a and b.
> 避免替換 a b 時 conflicts with variable names
```cpp
#define max(a,b) \
({ typeof (a) _a = (a); \
typeof (b) _b = (b); \
_a > _b ? _a : _b; })
```
#### 解釋以下巨集的原理
```cpp
#define container_of(ptr, type, member) \
__extension__({ \
const __typeof__(((type *) 0)->member) *__pmember = (ptr); \
(type *) ((char *) __pmember - offsetof(type, member)); \
})
```
==回答==
- `const __typeof__(((type *) 0)->member) *__pmember`
- declare `_pmember` as pointer type of `(type *) 0)->member`
- (char *) `__pmember - offsetof(type, member)`
- `member` 的 base address 減掉 structure `type` 到 `member` 的 offset 得到 type 的 base address
#### 除了你熟悉的 add 和 delete 操作,`list.h` 還定義一系列操作,為什麼呢?這些有什麼益處?
==回答==
- 方便 programmer 開發,易讀性高,容易 reuse 以及 debug
* `LIST_POISONING` 這樣的設計有何意義?提示: 和 [Linux 核心記憶體管理](https://hackmd.io/@sysprog/rJBXOchtE)有關
* linked list 採用環狀是基於哪些考量?
* 什麼情境會需要對 linked list 做排序呢?舉出真實世界的應用,最好在作業系統核心出現
#### 什麼情境會需要需要找到 [第 k 大/小元素](https://www.geeksforgeeks.org/kth-smallestlargest-element-unsorted-array/) 呢?又,你打算如何實作?
==回答==
- 需要一直 reuse 時先 sort 之後用 binary search (如果是 array)
- 若不用 reuse 則用 [selection algorithm](https://en.wikipedia.org/wiki/Selection_algorithm) 實作
#### `list_for_each_safe` 和 `list_for_each` 的差異在哪?"safe" 在執行時期的影響為何?
==回答==
- `list_for_each_safe` 有段註解如下,比對可以看出 `list_for_each_safe` 刪除 current node 時其實不會有影響,但是 `list_for_each` 會產生 dereference null ptr 的情況
> The current node (iterator) is allowed to be removed from the list. Any other modifications to the the list will cause undefined behavior.
- list_for_each
```cpp
#define list_for_each(node, head) \
for (node = (head)->next; node != (head); node = node->next)
```
- list_for_each_safe
```cpp
#define list_for_each_safe(node, safe, head) \
for (node = (head)->next, safe = node->next; node != (head); \
node = safe, safe = node->next)
```
#### for_each 風格的開發方式對程式開發者的影響為何?
> 提示:對照其他程式語言,如 Perl 和 Python
==回答==
- 可以抽象化資料結構細節,方便開發者撰寫 code
#### 程式註解裡頭大量存在 `@` 符號,這有何意義?你能否應用在後續的程式開發呢?
> 提示: 對照看 Doxygen
==回答==
- Doxygen 是程式文件產生工具,將程式特定註解轉換成說明文件,須遵循其註釋撰寫格式
- 延伸閱讀: [doxygen的注釋的寫法的介紹](https://b8807053.pixnet.net/blog/post/3612235-doxygen%E7%9A%84%E6%B3%A8%E9%87%8B%E7%9A%84%E5%AF%AB%E6%B3%95%E7%9A%84%E4%BB%8B%E7%B4%B9)
#### `tests/` 目錄底下的 unit test 的作用為何?就軟體工程來說的精神為何?
==回答==
- 確保 code quality
- Correctness
- Performance
- 方便維護
- 容易團隊合作
- 加速開發
- 延伸閱讀: [Unit Test 教學:核心觀念](https://medium.com/@ji3g4kami/unit-test-%E6%95%99%E5%AD%B8-ba39e54fcbc5)
#### `tests/` 目錄的 unit test 可如何持續精進和改善呢?
## 修改過程
- 因為想要寫的自動化一點,所以把個 sort 的 main function 都拿出來利用 `strcmp` 選擇 method
- 但是這樣還是要寫很多類似的 code 在 makefile 我在想想怎麼改進
```cpp
#define ANALYSIS(name) \
do { \
clock_gettime(CLOCK_REALTIME, &start); \
list_##name##sort(&name##_list); \
clock_gettime(CLOCK_REALTIME, &end); \
printf("%lu ", end.tv_nsec - start.tv_nsec); \
} while (0)
```
```cpp
if (!strcmp("all", argv[2])) {
ANALYSIS(m);
ANALYSIS(q);
ANALYSIS(insert);
}
```
- gnuplot
- 在 Makefile 內用 -e 傳遞 command line argument
```cpp
set title "perf"
set xlabel "size"
set ylabel "time (ns)"
set terminal png font " Times_New_Roman,12 "
set key left
set yrange[0: 20881933]
set output filename
if (!exist("name3")){
plot \
"out.txt" using 1:2 title name1, \
"out.txt" using 1:3 title name2 \
}
else{
plot \
"out.txt" using 1:2 title name1, \
"out.txt" using 1:3 title name2, \
"out.txt" using 1:4 title name3
}
```
- common.h
- 增加以下 function 作為 increasing 測資
```cpp
static inline void increasing_array(uint16_t *operations, uint16_t len)
{
for (uint16_t i = 0; i < len; i++) {
operations[i] = i;
}
}
```
- benchmark.c
- 用 `#ifdef` 篩選 input
- 參考 [bauuuu102 的共筆](https://hackmd.io/@jD9XFdyQS0iyAaFMPYi5Ww/rkqu6rqBV)
```cpp
#ifdef RANDOM
random_shuffle_array(values, (uint16_t) ARRAY_SIZE(values));
#elif INCREASE
increasing_array(values, (uint16_t) ARRAY_SIZE(values));
```
## 實作 Merge sort 效能分析
- 以下是 merge-sort 和 quick-sort insertion-sort 的效能比較 (random input)
- insertion-sort 如預期效能最差,mertge-sort 在 random input 時表現的比 quick sort 還差蠻多的,但下面改變 input 可以發現 merge-sort 表現的非常穩定
- 因為這個實作的 quick-sort 每次都取第一個元素當 pivot 所以當 input 數據有一定趨勢時會表現的很差
![](https://i.imgur.com/1YMBpvR.png)
![](https://i.imgur.com/ht0B0Kx.png)
![](https://i.imgur.com/TnfjigZ.png)
- 以下測試 increasing input
- 改成這樣 insertion-sort 效能就和 quick-sort 相近了,但是 merge-sort 還是遙遙領先
- 原本預期 insertion-sort 效能會最好,但是竟然只和 worst-case 的 quick-sort 相近,發現原因後再補上
![](https://i.imgur.com/VAz1G89.png)
![](https://i.imgur.com/MQKLJCR.png)
## qtest
- queue.h
- 把原本 list_ele_t 資料結構刪掉
- queue_t 改成以下
```cpp
typedef struct {
struct list_head list;
char* value;
} queue_t;
```
- 接著把 queue.c qtest.c 改成對應的操作還有資料結構就好,比較值得提的是 qtest.c 中的 show_queue function 原本的 `e == NULL` 要改成以下
- 否則會出現 `"ERROR: Either list has cycle, or queue has more than %d elements"`錯誤訊息
```cpp
if (list_entry(&e->list, queue_t, list) == q) {
```
- q_reverse
- 原本有 segmentation fault 的問題,回去寫自我檢查事項就發現問題了
- 更動 current node 的 `next` 指標要用 `list_for_each_safe` 否則會直接回到 head
```cpp
void q_reverse(queue_t *q)
{
if (!q || size < 2)
return;
struct list_head *prev, *node, *safe;
list_for_each_safe (node, safe, &q->list) {
prev = node->prev;
node->prev = node->next;
node->next = prev;
}
prev = (q->list).prev;
(q->list).prev = (q->list).next;
(q->list).next = prev;
}
```
#### 加入 sort command
- qtest.c
- 加入 `do_sort` function
- `init_console`內加入 `add_cmd("sort", do_sort, " | Sort the queue")`
```cpp
bool do_sort(int argc, char *argv[])
{
if (argc != 1) {
report(1, "%s takes no arguments", argv[0]);
return false;
}
bool ok = true;
error_check();
if (exception_setup(true))
q_sort(q);
exception_cancel();
show_queue(3);
return ok && !error_check();
}
```
- queue.h
- 加入以下宣告
- 並在 Makefile 內和 `merge-sort.c` link 在一起
- 目前是用 merge-sort 可以在 queue.c 內改成其他兩者
```cpp
void q_sort(queue_t *q);
extern void list_msort(struct list_head *head);
extern void list_qsort(struct list_head *head);
extern void list_insertsort(struct list_head *head);
```
- 成功 sort
```
q = [3 5 1 8 20]
cmd>sort
cmd>sort
q = [1 3 5 8 20]
```
- 交作業後發現忘記測試 make test 很多測資沒過,改完後再補上
```
TOTAL 51/100
```
## 參考資料
- [Unit Test 教學:核心觀念](https://medium.com/@ji3g4kami/unit-test-%E6%95%99%E5%AD%B8-ba39e54fcbc5)
- [bauuuu102 的共筆](https://hackmd.io/@jD9XFdyQS0iyAaFMPYi5Ww/rkqu6rqBV?type=view)
- [Unit Test 教學:核心觀念](https://medium.com/@ji3g4kami/unit-test-%E6%95%99%E5%AD%B8-ba39e54fcbc5)