# 2024q1 Homework5 (assessment)
contributed by < `LindaTing0106` >
## 因為自動飲料機而延畢的那一年
看完此篇文章不經讓我思考,其實很多現在在我們日常中隨手可見的物品,一定也是經過前人不斷失敗,再不斷嘗試,最終才成為改善我們生活品質的物品。我想這也恰恰是老師從課程一開始就一直跟我們講的事情:「要誠實面對自己。」,正是因為可以誠實面對自己,才可以坦然接受失敗,再從失敗中改進並且學習。
## 研讀第 1 到第 6 週「課程教材」和 CS:APP 3/e
### 第 2 週
#### 你所不知道的 C 語言:數值系統
- [ ] DETECT 巨集
提到的 DETECT 巨集,偵測是否有 NULL 。 而在 strlen() ,也有使用到 '\0' 來判斷字串長度。其計算方式是透過迴圈,在指標遇到字元 '\0' 前,將長度 + 1 。但這樣明顯會有時間複雜度為 n 的問題。使用 (((X) - 0x01010101) & ~(X) & 0x80808080) 我們可以直接得到 '\0' 在 X 中的位置。
藉由此例子可以看出懂得運用 bit-wise 可以節省函式所需要的時間。
#### C 語言的 bit-field
```c
struct foo {
int a : 3;
int b : 2;
int : 0; /* Force alignment to next boundary */
int c : 4;
int d : 3;
};
int main() {
int i = 0xFFFF;
struct foo *f = (struct foo *) &i;
printf("a=%d\nb=%d\nc=%d\nd=%d\n", f->a, f->b, f->c, f->d);
return 0;
}
```
其中 `int : 0;` 會導致 c 和 d 應該去對齊下一個 4 bytes ,也就導致 c 和 d 不是存取到 i 的地址,而是存取到其他地址,故每次執行時的值也會不一樣。
### 第 3 週
#### 你所不知道的 C 語言:遞迴呼叫篇
- [ ] [字串反轉](https://hackmd.io/@sysprog/c-recursion#%E6%A1%88%E4%BE%8B%E5%88%86%E6%9E%90%EF%BC%9A%E5%AD%97%E4%B8%B2%E5%8F%8D%E8%BD%89)
:::info
其中提到的 in-place string reverse 想法:一直 swap '\0'之前的character 。但在描述演算法處,在把頭尾互換之後,需要再把尾端和 '\0' 互換,重複以上步驟直到 '\0' 至於字串的中間,這裡不太理解的是為什麼不能直接互換頭尾,直到頭端位置大於等於尾端,為什麼要去動到 '\0' ?
:::
#### 你所不知道的 C 語言:技巧篇
- [ ] 善用 GNU extension 的 typeof
在巨集中,因為無法知道參數的型態,所以運用 typeof 去宣告變數會是一個很好的手法。
### 第 4 週
#### 你所不知道的 C 語言:編譯器和最佳化原理篇
- [ ] Pointer Aliasing
```c
void foo(int *i1, int *i2, int *res)
{
for (int i = 0; i < 10; i++) {
*res += *i1 + *i2;
}
}
```
如果改成下面這種形式,則可以減少在每次迴圈時需要先將 i1 和 i2 相加步驟。
```c
void foo(int *i1, int *i2, int *res)
{
int tmp = *i1 + *i2;
for (int i = 0; i < 10; i++) {
*res += tmp;
}
}
```
但若考慮到這樣寫 foo(&var, &var, &var) 來呼叫 foo() ,也就是在傳入的三個參數的地址為一樣的情況下,這兩段程式碼的執行結果則會天差地遠。
先假設 var = 1
- 上方程式碼中 `*res += *i1 + *i2` 後 *res 會等於 3 ,而由於這三個引數的地址相同,則 *i1 和 *i2 的值也會是 3 ,故下一次運算後 *res 則變為 9 ,以此類推。
- 下方程式碼中 `*res += *i1 + *i2` 後 *res 會等於 3 ,而由於這三個引數的地址相同,則 *i1 和 *i2 的值也會是 3 ,但由於 tmp 已經把最一開始 *i1 和 *i2 的值相加並存取下來,故之後每次運算其實只是在對 *res += 2。
由於上述兩種情況對於運算的結果有極大的不同,所以此時編譯器不會對此程式碼做最佳化。
- [ ] [ Page 69 ]
> 編譯器可刪去沒在使用的 static global variable 以節省空間,但不能刪去 (目前) 沒在使用的 non-static global variable,因為無法確定其他的 Compilation Unit 是否會用到此變數
> 這是為何建議 local function 要宣告成 static 的用意!
### CS:APP 第 2 章重點提示和練習
> 算術位移的應用,若要判斷一個 int 型態的變數 n 是否為正數,可用 `n >> 31` 其等價於 `n >=0 ? 0: -1`
這是利用在編譯器中右移往往採用算數位移的關係,將負數向右位移 31 bit 後,即該數變為 $n = 1111...1111$ 或是 $0000...0000$ 。
>若 n 是 32-bit 整數,那麼 `abs(n)` 等同於 `((n >> 31) ^ n) - (n >> 31)`
- [ ] Integer C Puzzles
$x > y \Longrightarrow -x < -y (False)$
若 $x$ 為 int_max 而 $y$ 為 int_min 則 $x > y$ 成立。
此時 $-x =$ 1_0000..._0001 而 $-y =$ 1_0000..._0000 則應為 $-x > -y$ 。
故上述條件式為 $(False)$ 。
## 想投入的專案
- [ ] 實作高效記憶體配置器
- [ ] 排程器 / 並行
---
## 期末專題
- [期末專題](https://hackmd.io/@LindaTing/linux2024-final_project)
TODO: 閱讀 [並行程式設計: 實作輕量級的 Mutex Lock](https://hackmd.io/@sysprog/concurrency/%2F%40sysprog%2Fconcurrency-mutex) 並重現實驗,紀錄問題
TODO: 閱讀 https://www.doc.ic.ac.uk/~afd/homepages/papers/pdfs/2023/SPIN.pdf | https://github.com/mc-imperial/modelcheckingfutexes 重現實驗 (原本用 C++) 並紀錄問題
TODO: 用 C11 futex 重寫 https://github.com/mc-imperial/modelcheckingfutexes/tree/main/cpp 程式碼
---
## 與老師一對一約談紀錄
Q : 第三周的字串反轉範例,為什麼一定要和字串結尾的 '\0' 做更改呢?
A : 這是某間公司的面試題目,是為了考驗面試者能不能在 in-place 的限制下,將字串反轉,其實一般字串反轉也不是一定只能這樣實作,但這就只是面試規定。
Q : 在 lab0 中,希望我們將 tic-tac-toe 加入作業中,並且加入 ai vs ai 模式和處理鍵盤事件,我發現許多人都是使用搶占式的方式加入,所以我自己是使用協同式的方式,但結果發現使用協同式會導致每個 task 都需要等待一段時間才會開始工作,所以想問老師這個作業是其實一定要使用搶占式的模式嗎?另外如果使用起來搶占式的運作比較流暢,那為什麼還需要有協同式的存在呢?
A : 此作業的目的在於希望同學可以試著將一段程式加入多工運作,因此只要有加入都可以,但程度較好的同學當然可以在做改進,但至少應該要做到加入多工這件事。另外像 lab0-c 中的網路伺服器則適合使用協同式的方式運作,因為他的執行路徑明確,不會一件事做到一半突然要去做其他事。
preempt (搶佔)
lab0-c 內建網頁伺服器: 單工 (執行路徑明確)