# 2023q1 Homework1 (lab0)
contributed by < `mickey30606` >
> 目前 `make test` 分數 95/100
:::danger
HackMD 筆記不是讓你張貼完整程式碼的地方,此處著重於「開發紀錄」,亦即你的觀察、描述遭遇到的問題,還有如何克服和改進。
:notes: jserv
:::
## 研讀 Linux 核心原始程式碼的 lib/list_sort.c
### `merge()`
- 將兩個 linked list 以 compare function 為基準合併成一個 linked list 。
- 使用到 pointer to pointer 的技巧。
- 只有做到 linked list 的單向串接。
- 如果節點相等,則取 `a` 來維持排序的 stability 。
:::danger
改進漢語表達!
:notes: jserv
:::
### `merge_final()`
- 做的事情與 `merge()` 相同,不過有將 merge 完的 list 變成 doubly linked list 。
- 比較有趣的是,在這個 `merge_final()` 的後半段,是將剩餘的 linked list 變成雙向的,因為擔心那個迴圈跑太久,所以設定了一個 8-bit 的 `count` ,若是 overflow ,則會呼叫一次 `cmp()` ,看註解是說,可以在 `cmp()` 中定期呼叫 `cond_resched()` (目前唯一想到的解法是維護一個 global variable ),這樣 cpu 就不會一直卡在這個迴圈裡面。
```c
do {
/*
* If the merge is highly unbalanced (e.g. the input is
* already sorted), this loop may run many iterations.
* Continue callbacks to the client even though no
* element comparison is needed, so the client's cmp()
* routine can invoke cond_resched() periodically.
*/
if (unlikely(!++count))
cmp(priv, b, b);
b->prev = tail;
tail = b;
b = b->next;
} while (b);
```
## 閱讀論文 〈Dude, is my code constant time?〉
在論文中有提到
> a) Pre-processing: We pre-process measurements prior to statistical processing. We discard measurements that are larger than the p percentile, for various values of p.
並在 `dudect/src/dudect.h` 中有看到程式碼的實作
```clike
if (first_time) {
// throw away the first batch of measurements.
// this helps warming things up.
prepare_percentiles(ctx);
} else {
update_statistics(ctx);
ret = report(ctx);
}
```
```c
static int64_t percentile(int64_t *a, double which, size_t size) {
qsort(a, size, sizeof(int64_t), (int (*)(const void *, const void *))cmp);
size_t array_position = (size_t)((double)size * (double)which);
assert(array_position >= 0);
assert(array_position < size);
return a[array_position];
}
/*
set different thresholds for cropping measurements.
the exponential tendency is meant to approximately match
the measurements distribution, but there's not more science
than that.
*/
static void prepare_percentiles(dudect_ctx_t *ctx) {
for (size_t i = 0; i < DUDECT_NUMBER_PERCENTILES; i++) {
ctx->percentiles[i] = percentile(
ctx->exec_times, 1 - (pow(0.5, 10 * (double)(i + 1) / DUDECT_NUMBER_PERCENTILES)),
ctx->config->number_measurements);
}
}
```
在第一次的 measurement 做完之後,程式會將紀錄下的結果進行排序,然後依照百分比,取得在該百分比下能接受的最大值。
:::warning
- 不過在 `percentile()` 當中每次的 `a` 和 `size` 都一樣,那位什麼不先排序好之後在乎叫到這個函式?
- 註解中提到,程式碼第 18 行的 `1 - (pow(0.5, 10 * (double)(i + 1) / DUDECT_NUMBER_PERCENTILES))` 是沒有什麼理論解釋的,只是大概去抓符合 distribution 的數值,那有什麼比較好的解法嗎?
> 讀論文! :notes: jserv
:::
並在之後的 measurement 當中只要大於 `ctx->percentiles[crop_index]` 的測量值,都不會加入計算。
```clike
// t-test on cropped execution times, for several cropping thresholds.
for (size_t crop_index = 0; crop_index < DUDECT_NUMBER_PERCENTILES; crop_index++) {
if (difference < ctx->percentiles[crop_index]) {
t_push(ctx->ttest_ctxs[crop_index + 1], difference, ctx->classes[i]);
}
}
```
:::warning
不過之後取得最終值 `t` 的程式碼中一樣是取得最大值作為最終的值,那有沒有進行 crop 目前看來沒有達成原本的目的?
```clike
static ttest_ctx_t *max_test(dudect_ctx_t *ctx) {
size_t ret = 0;
double max = 0;
for (size_t i = 0; i < DUDECT_TESTS; i++) {
if (ctx->ttest_ctxs[i]->n[0] > DUDECT_ENOUGH_MEASUREMENTS) {
double x = fabs(t_compute(ctx->ttest_ctxs[i]));
if (max < x) {
max = x;
ret = i;
}
}
}
return ctx->ttest_ctxs[ret];
}
```
:::