contributed by < stevendd543
>
SuNsHiNe-75
你的洞見呢?與其談「心法」,不如設想「如果我是你」,我會怎麼做,示範一次給同學看,而且檢討自己哪裡沒做好,從而呼應原本學員的不足處。
清楚標示學員的 git commits 和敘述的不足處,予以檢討。
軟體工程師要學會說故事,本作業要求學員從良性詳盡的批評開始。繼續檢討!
jservImage Not Showing Possible ReasonsLearn More →
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
在 queue.h 中我發現了一個函式 q_release_elements
裡面使用了 test_free
而非 free
,一開始還有個疑問是為何 q_release_element
要先將 value 釋放掉記憶體在釋放掉 list ,後來仔細看 element
這個結構後才發現原來當初 value 的型態是 char*,因此在動態建立 element
這個結構時,也同時動態配置了 value 空間,因此在釋放空間的時候,如果沒有將 value 釋放將會產生錯誤。
function call 是「函式呼叫」,其權限沒有大到可以「調用」你。
在 INIT_LIST_HEAD
註解中提到,一個沒被初始化的節點,有可能會被其他函式呼叫 ,並使用 list_del
將其刪除,這樣的結果是不安全,雖然配置記憶體空間給了q_head
,但是結構中的鏈結串列沒有使用 INIT_LIST_HEAD
初始化的話會造成解引用空指標。
store 的翻譯是「儲存」,而非「存儲」,本處針對者是存取記憶體的邊界議題。
strcpy() 與 memcpy() 皆有可能會造成儲存 溢位要小心使用 !
兩者差異在於複製的型態限制不一樣,但不像 strdup
在配置空間時會返還配置失敗的訊息,而會直接覆蓋原始資料。
這裡嘗試了使用strncpy
來複製字串,透過 bufsize-1 限制了複製位元組的次數,可以避免掉存儲溢位,但要注意要預留最後給空白字元進行字串中止 strcpy(sp+bufsize-1,"\0")
其餘操作像是 q_delete_dup、q_swap、q_reverseK、q_ascend、 q_descend 等等程式碼詳見 Github: lab0-c/queue.c
名詞解釋
合併過程
此處擷取 list_sort.c 說明了 merge 兩個 list 的時機,
每當我們增加 count 時,發生第 k-bit 且 0~k-1-bit 都設為0時進行 merge
合併條件
裡面提到了當 count 也就是紀錄 pending list 的節點數量,剛好是 的奇數倍,就進行合併,但我覺得這裡一開始看相當不值觀,因為 的奇數倍換句話說就是 的偶數倍 1、2、4、8… 不進行合併,那再搭配表看看
count decimal | count binary | merge |
---|---|---|
0 | 0000 | No |
1 | 0001 | No |
2 | 0010 | Yes |
3 | 0011 | No |
4 | 0100 | Yes |
5 | 0101 | Yes |
明顯的搭不起來。
無論標題和內文中,中文和英文字元之間要有空白字元 (對排版和文字搜尋有利);
改進你的漢語表達。
後來我直接去了解搭不起來的原因,主要是 count 的變化時間點與剛剛的程式碼註解有稍微一點落差,在理解上比較容易搞混,因此我按照原本程式碼圖解了 pending list 、 merge 、 bits 的個別用意,以下為擷取部分lib/list_sort.c 程式碼。
假設目前 bits = count
計算到 01002,且目前 pending list 如下圖,表示剛剛加入一個長度為 1 的元素後,就會有兩個一樣大小的要準備合併,現在 pendlist 狀況長這樣。
合併後再加入一個長度為 1。然後這個時候 count 變成 01012,在上面有提到說除了第一次 flip外,如果第 k 個 bit 被轉成 1 且 0~k-1 bits 都因為進位變 0代表有長度 2k 的 list 要做合併,那此處就是 a 與 b進行合併,且 count + 1 變成 01102。
合併後再加一個進來變成下圖。那這邊 count + 1 後變成 01112 此處同上第 0 bit 進位了,所以 20 長度要進行合併。
合併且加入元素變成,可以注意到 01112 中每個 1都代表長度為 2k 各有一個。
同理可推 10002 因為是第一次轉成 1 所以不做任何合併。
明確標示 GitHub 帳號名稱及出處。
有了上面的圖在看整理圖表(來源 : kdnvt)就可以理解整個設計了。
count decimal | count binary | merge | before merge | after merge |
---|---|---|---|---|
0 | 0000 | No | NULL | X |
1 | 0001 | No | 1 | X |
2 | 0010 | Yes | 1-1 | 2 |
3 | 0011 | No | 1-2 | X |
4 | 0100 | Yes | 1-1-2 | 2-2 |
5 | 0101 | Yes | 1-2-2 | 1-4 |
6 | 0110 | Yes | 1-1-4 | 2-4 |
7 | 0111 | No | 1-2-4 | X |
8 | 1000 | Yes | 1-1-2-4 | 2-2-4 |
9 | 1001 | Yes | 1-2-2-4 | 1-4-4 |
10 | 1010 | Yes | 1-1-4-4 | 2-4-4 |
11 | 1011 | Yes | 1-2-4-4 | 1-2-8 |
12 | 1100 | Yes | 1-1-2-8 | 2-2-8 |
13 | 1101 | Yes | 1-2-2-8 | 1-4-8 |
14 | 1110 | Yes | 1-1-4-8 | 2-4-8 |
15 | 1111 | No | 1-2-4-8 | X |
16 | 10000 | Yes | 1-1-2-4-8 | 2-2-4-8 |
回到第一手材料,閱讀作業說明所及的三篇論文。
參考 chiangkd
perf 效能測試,參見 perf 原理和實務
資料量 | q_sort | list_sort |
---|---|---|
500,000 | 0.45 | 0.406 |
1,000,000 | 0.987 | 0.859 |
資料量 | q_sort | list_sort | difference |
---|---|---|---|
cache-misses | 126,801,501 | 107,195,973 | 19,605,528 |
cache-references | 210,171,035 | 171,331,504 | 38,839,531 |
可以從結果看出 list_sort 對 cache 相當友善!
TODO : 改進 q_sort
Valgrind 簡介
valgrind 是個在使用者層級 (user space) 對程式在執行時期的行為提供動態分析的系統軟體框架,具備多種工具,可以用來追蹤及分析程式效能。
valgrind 會自行維護一份暫存器與記憶體的副本來安全地使用並測試,因為他屬於 dynamic Binary Instrumentation (DBI) 框架,因此會操作機械碼來進行分析,但這過程會經過轉譯成 VEX 中間表示法 (intermediate representation,簡稱 IR,是種虛擬的指令集架構),並插入分析工具來進行檢測。
論文網址: <Dude, is my code constant time?>
Timing attacks 是一種 side-channel 實體攻擊手法,攻擊者主要是基於不同的 logical operation,來量測每個 opertaion 執行時間推敲出輸入資料的資訊
為了解決人工解查耗時的問題,現今研究提出了由動態分析工具Valgrind
延伸而來的ctgrind
去追蹤 secret-dependant paths 或者 memory accesses。也有人利用靜態指令分析去分析部分程式碼是否為常數執行時間
嵌入硬體過程繁雜,難以建模實現。
避免非必要的項目縮排 (即 *
和 -
),以清晰、明確,且流暢的漢語書寫。
What is timing variability ?
step 1. Measure execution time
根據兩組不同資料,執行 leakage detection test 去統計執行時間,因為有多種 leakage detection test 常見的手法有兩種 fix 與 random
step 2. Apply post-processing
在做統計分析前會先做以下一些處理
Right skewed = Postively skewed
Step 3. Apply statistical test
去驗證 「兩個時間分布是相等」 的虛無假設
指出論文和程式碼之間的差異。
從dudect/src/dudect.h開始分析
從參數名稱int64_t *a_sorted
知道輸入資料必須是經過排序,輸入資料要從另一個函式prepare_percentiles
取得
這兩個函式在 控制抽樣的比例
如何透過以實驗而非理論分析來驗證時間複雜度
解釋 Student's t-test 以及實作
避免非必要的項目縮排 (即 *
和 -
),以清晰、明確,且流暢的漢語書寫。
Student's t-test : 先介紹什麼是假說檢定,主要功能是透過現有的數據去支持特定假設的方法,因此會有兩個假說 Null hypothesis 以及對立的假說 Alternative hypothesis ,根據結果來推論真正的母體。再來就是 Student's t-test,它是用來檢定兩組資料的母體平均數是否有顯著差異,但再進行檢定前必須滿足三個前提假設,獨立性、常態性、變異數同質性,才會去進行 Student's t-test 倘若兩組資料變異數不具同質性,才會使用 Welch’s t-test 來檢定會更具可靠性
來自 lab0-c/dudect/ttest.c 實作 Welch’s t-test 分成三組函數
void t_push(t_context_t *ctx, double x, uint8_t class)
: 裡面有計算變異數 Welford’s 方法double t_compute(t_context_t *ctx)
: 計算在變異數不同下的 t 統計量void t_init(t_context_t *ctx)
: 初始化所有統計變數將 dudect/src/dudect.h 中的 percentile 引入 lab0-c,新增在commit : da59019
可以知道 percentiles[i]
由小至大由 (下圖為變化曲線) 加權設置不同的閾值來限制 measurements ,但仔細看曲線可以發現所有的percentiles
並不全然相異,等於從時間統計中抽樣出來的值會重複在 larger time
錄影 : Amazing Code Reviews
有時候我們會像單細胞生物一樣 PR 與 merge,但事實上這樣很容易會有程式問題,因此作者強調著重在 code review 的工程上會讓團隊生產更有效率
前提 : 在 monte-calro 中大量使用 浮點數 計算,當我們無法接受浮點數帶來的計算負擔時,就需要使 定點數 來解決。
其實一開始直觀的想法,浮點數與定點數都屬於小數,為什麼定點數就能比浮點數還低的計算開銷,參考了作業講解中提到,kernel 會多了 trap 以及 initiate 這兩步驟,那定點數呢?
When using floating-point instructions kernel normally catches a trap and then initiates the transition from integer to floating point mode.
定點數雖然是小數,但他不屬於 IEEE 754 的浮點數規範,不必處裡浮動的小數點,在某些精度要求不高的地方,定點數可以比浮點數更有效率,也要特別注意浮點數與定點數的表示方式是有差異的,在這裡很容易搞混。
以下簡單的整理作業提到的定點數運算範例
本範例的精確度取決於 frac_bits
,也就是當你固定小數位置,後面表示小數的位數有多少個 bits。
result = 1UL << frac_bits
這段程式碼的用意主要是在於,將其結果初始化成定點數的形式,因為日後的計算都是以定點數做操作。
再來迴圈內的所有乘法都屬於定點數乘法,因此經過乘法後小數點偏移,因此要做一次反操作成規定的定點數位置。
line editing library 是在 CLI 中提供給使用者功能的 library,linenoise 就是其中一種輕型的應用。以下來自官方 github 提供的用途
我從 do_web
中發現當執行 web 命令後 use_lineboise 會被設為 false 也就是會將 linenoise 關閉,那這個會如何影響 web 呢? 先回去看一下 linenoise。
linenoise.c/linenoise
這個函式主要用來檢查終端的基本能力。
首先會透過 isatty
檢查是否連上 terminal ,因為每個 terminal 都會有一個唯一的 device file 儲存在 dev/ 下,也可透過 tty
命令將當前終端的名稱顯示出來。如果 terminal 支援但無法解析輸入命令就會執行 line_raw
切換到 raw mode 讀取命令,使用者不管有沒有 enter 命令都會被完整 read
。
Terminal mode
假設使用者在 terminal 中輸入 “ABC<Backspace>D”
- cooked mode :在資料傳進程式之前會先做預處理,變成 ABD
- raw mode :不做任何的特殊字轉換就將其送入程式,變成 ABC<Backspace>D
在 console.c
中如果 linenoise
被關閉後,只會執行 cmd_select
而不會去透過 linenoise
處理終端輸入的命令。
不過目前還是沒辦法理解作業中提到的「當 read 阻塞時,便無法接收 web 傳來的資訊」,我有去參考 vax-r 將 cmd_select
中的 char *cmdline = readline()
將其改成 linenoise(prompt)
去處理輸入的命令,但會發現只能在 web 成功完成命令,在終端卻無法同時使用,這裡給的結論是 read 被阻塞,不過我覺得不夠清楚理解(TODO:解釋為何無法同時操作)。