# 2023q1 Homework3 (quiz3)
contributed by < [Huaxin](https://github.com/p96114175) >
> [題目](https://hackmd.io/@sysprog/linux2023-quiz3)
:::info
* 測驗一
- [ ] 指出 treesort.c 程式碼的缺失並改進
- [ ] 利用 Linux 核心的 list.h 標頭檔改寫並整合進第一次作業的實驗程式碼中,探討 Tree sort 的效率
- [ ] 研讀 Linux 核心的 Red–black tree 程式碼,比較給定的 treesort.c 實作手法,指出後者的改進空間並予以實作
* 測驗二
- [ ] 解釋上述程式碼運作原理,並比較用紅黑樹實作的 priority queue,應提供完整的測試程式和效能分析
- [ ] Linux 核心也有 AVL tree 的身影,但陸續被紅黑樹替代,請揣摩 Linux 核心開發者的考慮因素,並搭配 git log 來解讀
- [ ] 研讀 Linux 核心原始程式碼,指出曾經/現在使用 AVL tree 的部分,對照 Linux 核心的 rbtree.h,探究是否可用後者替換,從而爭取到貢獻程式碼到 Linux 核心的機會
* 測驗三
- [ ] 解釋 Linear feedback shift register (LFSR) 的運作原理,搭配在 Linux 核心原始程式碼中,用 git log 和 grep 找出 LFSR 的應用案例
- [ ] 解釋上述程式碼運作的原理
- [ ] 在 Linux 核心原始程式碼中找到 log2(x) 的實作 (整數),要涵蓋不同的整數型態寬度,以及是否能運用微處理器提供的指令來加速。也從 Linux 核心找出整數log2(x) 的應用案例並解說。
- [ ] lab0-c 提供類似的實作 log2_lshift16.h,解釋其原理並嘗試改進其效能
* 測驗四
- [ ] 解釋上述程式碼運作原理
- [ ] 改進程式碼,使其得以處理 x = 0 的狀況,並仍是 branchless
- [ ] 在 Linux 核心原始程式碼找出 ceil 和 log2 的組合 (類似的形式),並解說應用案例,例如在 Linux 核心排程器就有
:::
## 測驗一
研究以下文件
[treesort.c](https://gist.github.com/jserv/920766d1fd893d0efd66ac7fdaf23401)
* 定義一個結構體,名為 `node_t` 用於實現紅黑樹。
* `uintptr_t color` 用於儲存節點在紅黑樹的顏色。
* `*left` `*right` 用來指向該節點的兩個左右子節點
* `*next` 用來指向該節點的下一個節點
* 定義一個 `long` 的 `data type` 儲存該節點的值
```c
typedef struct __node {
uintptr_t color;
struct __node *left, *right;
struct __node *next;
long value;
} node_t __attribute__((aligned(sizeof(long))));
```
使用結構體 `cmap_internal` 記錄了 `map` 資訊,其中也包含了紅黑樹的根節點 `*head` , 和比較的涵數指標
```c
struct cmap_internal {
node_t *head;
/* Properties */
size_t key_size, element_size, size;
cmap_iter_t it_end, it_most, it_least;
int (*comparator)(void *, void *);
};
```
定義出顏色中的紅和黑
```c
typedef enum { CMAP_RED = 0, CMAP_BLACK } color_t;
```
使用了 `color` 表示了兩項資訊,分別為節點的顏色、指向父節點的指標 ,這樣可以節省記憶體空間。
透過 `& ~7` ,也就是 `1...11000` 使得 `color` 末 `3` 位元皆為 `0`,便可獲得指向父節點的指標。
若要取得節點的顏色,則使用 `& 1` ,也就是0...00001,取得 LSB 的資訊
```c
#define rb_parent(r) ((node_t *) (((r)->color & ~7)))
#define rb_color(r) ((color_t) (r)->color & 1)
```
我們可以從 `rb_set_red` 名稱得知,這是個將節點顏色設定成紅色的功能,其中透過的方法就是 `&= ~1` ,也就是 `color` 對 `1...1110` 做 `&` 操作,使得 LSB 為 `0`。
而 `rb_set_black` 則是將節點設定成黑色,其中會透過 `|= 1` 來達成,也就是將 `color` 對 `0...0001` 做 `|` 操作
```c
#define rb_set_parent(r, p) \
do { \
(r)->color = rb_color(r) | (uintptr_t) (p); \
} while (0)
#define rb_set_red(r) \
do { \
(r)->color &= ~1; \
} while (0)
#define rb_set_black(r) \
do { \
(r)->color |= 1; \
} while (0)
```
下方函數則用於初始化一個新的紅黑數節點,將左右子節點指標和父節點指標設為 `null`,並將節點顏色初始化設為紅色,但我不明白為何註解上是寫成黑色為 `dedault`,還需要 jserv 老師指教一下
```c
static node_t *cmap_create_node(node_t *node)
{
/* Setup the pointers */
node->left = node->right = NULL;
rb_set_parent(node, NULL);
/* Set the color to black by default */
rb_set_red(node);
return NULL;
}
```
下方函數則是在對 `node` 進行左旋操作
```c
static node_t *cmap_rotate_left(cmap_t obj, node_t *node)
{
node_t *r = node->right, *rl = r->left, *up = rb_parent(node);
/* Adjust */
rb_set_parent(r, up);
r->left = node;
node->right = rl;
rb_set_parent(node, r);
if (node->right)
rb_set_parent(node->right, node);
if (up) {
if (up->right == node)
up->right = r;
else
up->left = r;
}
if (node == obj->head)
obj->head = r;
return r;
}
```
下方函數則是在對 `node` 進行右旋操作
```c
static node_t *cmap_rotate_right(cmap_t obj, node_t *node)
{
node_t *l = node->left, *lr = l->right, *up = rb_parent(node);
rb_set_parent(l, up);
l->right = node;
node->left = lr;
rb_set_parent(node, l);
if (node->left)
rb_set_parent(node->left, node);
if (up) {
if (up->right == node)
up->right = l;
else
up->left = l;
}
if (node == obj->head)
obj->head = l;
return l;
}
```
`cmap_l_l`, `cmap_l_r`, `cmap_r_r`, `cmap_r_l` 則是在平衡我們的紅黑樹時所使用的函數
在 `cmap_insert` 我們可以觀察到如何插入一個 `key/value pair` 到我們的 `cmap`,下方程式則是 `cmap_insert` 中的一部分,主要是在遍尋 `tree` 找尋可以插入的位置,透過比較 `node->value` 和 `cur->value` 得到 `res`,當 `res` 小於零時便會往左子節點走,反之則往右子節點走,若要終止 `for loop` 的條件為 `res` 為 `false` ,也就意味著走訪到的節點為空,便直接做插入的動作並跳出 `loop`。
```c
for (node_t *cur = obj->head;;) {
int res = obj->comparator(&node->value, &cur->value);
if (!res) /* If the key matches something else, don't insert */
assert(0 && "not support repetitive value");
if (res < 0) {
if (!cur->left) {
cur->left = node;
rb_set_parent(node, cur);
cmap_fix_colors(obj, node);
break;
}
cur = cur->left;
} else {
if (!cur->right) {
cur->right = node;
rb_set_parent(node, cur);
cmap_fix_colors(obj, node);
break;
}
cur = cur->right;
}
}
```
在 `tree_sort` 裡輸入需要排序的 `list`,並用 `record` 指向 `list`,首先透過 `cmap_new` 建立了 `map` 並將 `list` 的節點依序輸入進去 `map` ,然後再用 `cmap_first` 從 `map` 裡找出第一個節點,並用 `first` 指向它,`for (; node; node = cmap_next(node))` 透過迭代 `cmap` 中的每一個節點,儲存到 `list` 中,最後再將 `list` 中最後一個節點的 `next` 指標設定為 `null` ,再把 `record` 指向新的 `list` 的頭節點 `first` ,將使用完的 `map` 釋放掉內存,即可完成排序。
```c=
void tree_sort(node_t **list)
{
node_t **record = list;
cmap_t map = cmap_new(sizeof(long), sizeof(NULL), cmap_cmp_int);
while (*list) {
cmap_insert(map, *list, NULL);
printf("%ld\n", map->size);
list = &(*list)->next;
}
node_t *node = cmap_first(map), *first = node;
for (; node; node = cmap_next(node)) {
*list = node;
list = &(*list)->next;
}
*list = NULL;
*record = first;
free(map);
}
```
## 測驗二
## 測驗三
## 測驗四