# linux2023: zoo868e
## 測驗$\alpha-1$
- `st_rotate_left`: 旋轉左子樹變平衡
- `st_rotate_right`: 旋轉右子樹變平衡
- `st_balance`: 左子樹高度-右子樹高度
- `st_max_hint`: 樹的高度
- `st_update`: `st_balance` < -1,代表右子樹太重,所以要`st_rotate_right`,反之,要`st_rotate_left`。在`st_update`的最後會檢查hint是否改變,若是改變,親代的hint就可能改變,因此要向上更新。另外,由於rotate的操作只有在update中會發生,所以也只有在`st_update`中需要更新hint
- `st_insert`: 需要提供`root`, `parent`, `node`和`dir`。`root`是整棵樹的根,`parent`代表`node`的親代,`dir`則代表`node`是在`parent`的左邊還是右邊。實作方法直觀,將`node`附值給`parent`的`dir`,將`parent`設為`node`的親代,`st_update`樹的`parent`這個節點,因為可能需要rotate,這會更新整棵樹。
- `st_replace_[left/right]`: 用`l/r`節點覆寫`n`節點
- `st_remove`: 刪除節點`del`。如果有右子樹會先用右子樹的最小值複寫`del`,若沒有右子樹則會用左子樹的最大值覆寫`del`。若沒有子樹則直接刪除即可。當我們把least/most節點移動到`del`時,我們需要更新他原本的親代的`hint`,同時,如果因為移動導致不平衡,需要旋轉成平衡子樹。
```c
void st_remove(struct st_node **root, struct st_node *del)
{
if (st_right(del)) {
struct st_node *least = st_first(st_right(del));
if (del == *root)
*root = least;
st_replace_right(del, st_first(least));
st_update(root, st_parent(del));
return;
}
if (st_left(del)) {
struct st_node *most = st_last(st_left(del));
if (del == *root)
*root = most;
st_replace_left(del, st_last(most));
st_update(root, st_parent(del));
return;
}
if (del == *root) {
*root = 0;
return;
}
/* empty node */
struct st_node *parent = st_parent(del);
if (st_left(parent) == del)
st_left(parent) = 0;
else
st_right(parent) = 0;
st_update(root, parent);
}
```
- `container_of`: 透過`offsetof`取得類別成員在記憶體中,在pack中的相對位置,再用該成員的記憶體位置減去該相對位置,從而得到該類的記憶體位置
## 測驗$\alpha-2$
在`st_remove`中會先檢查是否有右子樹,若沒有右子樹則會檢查左子樹。由於這是一個平衡樹,且左右子樹高度差不大於1,所以在沒有右子樹的時候,即便有左子樹也只會有一個節點。所以我們不需要再去找左子樹中的最大值,可以省略`st_last`。並且,由於`st_replace_left`之後,`del`不會再有子代,所以直接更新`del`即可。
```c
if (st_left(del)) {
if (del == *root)
*root = st_left(del);
st_replace_left(del, st_left(del));
st_update(root, st_parent(del));
return;
}
```
## 測驗$\alpha-3$
根據[doc/Documentation/rbtree.txt](https://www.kernel.org/doc/Documentation/rbtree.txt)比較`rbtree`與`S-Tree`的效能。使用**Uftrace**比較兩者在Insert以及Remove時,所使用的旋轉次數以及耗費時間。
1. Insert
- 無序
- 有序
2. Search
- 無序
- 有序
4. Remove
- 無序
- 有序
## 測驗$\beta-1$
觀察alignment = 4 = 0100的情況
```
118 = 01110110 --align_up--> 1111000
119 = 01110111 --align_up--> 1111000
120 = 01111000 --align_up--> 1111000
121 = 01111001 --align_up--> 1111100
122 = 01111010 --align_up--> 1111100
```
可以發現`align_up`做的事情就是在第一、二個位元有1的時候,將其清空,並且加上`alignment`。再觀察alignment = 8 = 1000的情況
```
62 = 00111110 --align_up--> 01000000
63 = 00111111 --align_up--> 01000000
64 = 01000000 --align_up--> 01000000
65 = 01000001 --align_up--> 01001000
66 = 01000010 --align_up--> 01001000
```
跟alignment = 4時的狀況相似,只是多了第三的位元。所以我們根據觀察寫出第一個版本的`align_up`。
```clike=
static inline uintptr_t align_up(uintptr_t sz, size_t alignment)
{
uintptr_t mask = alignment - 1;
if ((alignment & mask) == 0) {
uintptr_t ret = sz;
if (sz & mask)
return (sz & ~mask) + mask + 1;
return sz;
}
return (((sz + mask) / alignment) * alignment);
}
```
但是我們希望不要使用`if`來完成`align_up`。再觀察一次`align_up`的步驟
```
sz = 119 = 01110111
alignment = 4 = 00000100
mask = 00000011, ~mask = 11111100
sz & mask = 00000011
sz & ~mask = 01110100
(sz & ~mask) + mask + 1 = 1110100 + 0100
```
再觀察`align_up`之後會是一樣的數,可以發現在需要進行上述步驟的數值都是即便-1之後再做一樣的步驟還是會有一樣的結果。同時,已經對其的數值(116)在-1之後,如果做同樣的步驟,也會得到相同的結果
```
alignment = 4 = 00000100
113 = 01110001 --align_up--> 01111100
114 = 01110010 --align_up--> 01111100
115 = 01110011 --align_up--> 01111100
116 = 01111100 --align_up--> 01111100
```
所以先將`sz`-1,就不需要判斷是否需要對齊
```clike=
static inline uintptr_t align_up(uintptr_t sz, size_t alignment)
{
uintptr_t mask = alignment - 1;
if ((alignment & mask) == 0) {
return ((sz - 1) & ~mask) + mask + 1;
}
return (((sz + mask) / alignment) * alignment);
}
```
由於經過`(sz - 1) & ~mask`之後,`+ mask`等價於`| mask`,所以我們可以改寫成這樣
```c
static inline uintptr_t align_up(uintptr_t sz, size_t alignment)
{
uintptr_t mask = alignment - 1;
if ((alignment & mask) == 0) {
return ((sz - 1) & ~mask | mask) + 1;
}
return (((sz + mask) / alignment) * alignment);
}
```
最後,可以發現先`& ~mask`再`| mask`等價於`| mask`,因此答案應該是這樣
```c
static inline uintptr_t align_up(uintptr_t sz, size_t alignment)
{
uintptr_t mask = alignment - 1;
if ((alignment & mask) == 0) {
return ((sz - 1) | mask) + 1;
}
return (((sz + mask) / alignment) * alignment);
}
```
---
## 測驗$\beta-2$
## 測驗$\gamma-1$
觀察後發現`HHHH`是發生在狀態改變為`ts_idle`的時候,只有在做完`qsort-algo`之後才會改變為`ts_idle`的狀態。所以再來要做的事情應該是等待`ts_term`。查找當狀態改變為`ts_term`時的行為,可以看到在`f3`以及`JJJJ`之前都會改變狀態為`ts_term`,由於`JJJJ`是我們要寫的作業,因此推測這裡的行為跟`f3`是一樣的,都需要發送訊號給`qs->cond_st`。
## 測驗$\gamma-2$
透過**ThreadSanitizer**檢查data race的問題
1. 先用`clang`編譯`qsort-mt.c`
```shell=
clang -fsanitize=thread -O2 qsort-mt.c -o qsort -g
```
2. 執行`qsort`得到ThreadSanitizer的報告,發現有一個data race的問題
```=
==================
WARNING: ThreadSanitizer: data race (pid=49080)
Write of size 4 at 0x7b4000000080 by thread T1 (mutexes: write M0):
#0 allocate_thread /home/matt_jan/LinuxSummer2023/W1/gamma/qsort-mt.c:201:21 (qsort+0xd184a) (BuildId: 4651c84b87659265877e79949d900d74d464d177)
#1 qsort_algo /home/matt_jan/LinuxSummer2023/W1/gamma/qsort-mt.c:301:14 (qsort+0xd184a)
#2 qsort_thread /home/matt_jan/LinuxSummer2023/W1/gamma/qsort-mt.c:338:3 (qsort+0xcc91a) (BuildId: 4651c84b87659265877e79949d900d74d464d177)
Previous read of size 4 at 0x7b4000000080 by thread T2 (mutexes: write M1):
#0 qsort_thread /home/matt_jan/LinuxSummer2023/W1/gamma/qsort-mt.c:329:14 (qsort+0xcc8c7) (BuildId: 4651c84b87659265877e79949d900d74d464d177)
Location is heap block of size 256 at 0x7b4000000000 allocated by main thread:
#0 calloc <null> (qsort+0x4be67) (BuildId: 4651c84b87659265877e79949d900d74d464d177)
#1 qsort_mt /home/matt_jan/LinuxSummer2023/W1/gamma/qsort-mt.c:124:17 (qsort+0xcc1f6) (BuildId: 4651c84b87659265877e79949d900d74d464d177)
#2 main /home/matt_jan/LinuxSummer2023/W1/gamma/qsort-mt.c (qsort+0xcd0f2) (BuildId: 4651c84b87659265877e79949d900d74d464d177)
Mutex M0 (0x7ffc84940bb0) created at:
#0 pthread_mutex_init <null> (qsort+0x4ec1f) (BuildId: 4651c84b87659265877e79949d900d74d464d177)
#1 qsort_mt /home/matt_jan/LinuxSummer2023/W1/gamma/qsort-mt.c:122:7 (qsort+0xcc1bd) (BuildId: 4651c84b87659265877e79949d900d74d464d177)
#2 main /home/matt_jan/LinuxSummer2023/W1/gamma/qsort-mt.c (qsort+0xcd0f2) (BuildId: 4651c84b87659265877e79949d900d74d464d177)
Mutex M1 (0x7b40000000a8) created at:
#0 pthread_mutex_init <null> (qsort+0x4ec1f) (BuildId: 4651c84b87659265877e79949d900d74d464d177)
#1 qsort_mt /home/matt_jan/LinuxSummer2023/W1/gamma/qsort-mt.c:128:9 (qsort+0xcc254) (BuildId: 4651c84b87659265877e79949d900d74d464d177)
#2 main /home/matt_jan/LinuxSummer2023/W1/gamma/qsort-mt.c (qsort+0xcd0f2) (BuildId: 4651c84b87659265877e79949d900d74d464d177)
Thread T1 (tid=49088, running) created by main thread at:
#0 pthread_create <null> (qsort+0x4d41d) (BuildId: 4651c84b87659265877e79949d900d74d464d177)
#1 qsort_mt /home/matt_jan/LinuxSummer2023/W1/gamma/qsort-mt.c:136:9 (qsort+0xcc2c0) (BuildId: 4651c84b87659265877e79949d900d74d464d177)
#2 main /home/matt_jan/LinuxSummer2023/W1/gamma/qsort-mt.c (qsort+0xcd0f2) (BuildId: 4651c84b87659265877e79949d900d74d464d177)
Thread T2 (tid=49089, running) created by main thread at:
#0 pthread_create <null> (qsort+0x4d41d) (BuildId: 4651c84b87659265877e79949d900d74d464d177)
#1 qsort_mt /home/matt_jan/LinuxSummer2023/W1/gamma/qsort-mt.c:136:9 (qsort+0xcc2c0) (BuildId: 4651c84b87659265877e79949d900d74d464d177)
#2 main /home/matt_jan/LinuxSummer2023/W1/gamma/qsort-mt.c (qsort+0xcd0f2) (BuildId: 4651c84b87659265877e79949d900d74d464d177)
SUMMARY: ThreadSanitizer: data race /home/matt_jan/LinuxSummer2023/W1/gamma/qsort-mt.c:201:21 in allocate_thread
==================
ThreadSanitizer: reported 1 warnings
```
## 測驗$\gamma-3$