contributed by < OscarShiang
>
queue.[ch]
和連帶的檔案,測試後用 Git 管理各項修改,記得也該實作 q_sort
函式。qtest
實作的記憶體錯誤,並透過 Massif 視覺化 "simulation" 過程中的記憶體使用量,需要設計對應的實驗qtest
命令列功能,使其支援單行或多行編輯、歷史命令處理、命令自動補完 (例如輸入 h
之後按下 Tab 按鍵,自動接續補上 elp
,成為完整的 help
命令)。除了整合程式碼外,應當說明 antirez/linenoise 的運作原理,注意到 termios 的運用
已整合在 sysprog21/lab0-c
master
分支
linux2020
分支
使用〈Dude, is my code constant time?〉中所實作出的工具 dudect
來檢測演算法的時間複雜度是否屬於 的特點有以下幾個
dudect
以重複測量程式執行時間建立樣本,利用 Welch's t-test 來分析樣本時間的分佈情形,進而推論程式執行的時間複雜度是否為常數時間。而不是通過公式推導的方式來驗證,進而降低了在實作上判斷時間複雜度為 的難度。dudect
在實作上是透過統計模型推斷時間複雜度是否為常數時間,所以不會因為使用不同的硬體進行檢測而有不同的結果。在判斷兩個 class 的樣本平均是否具有同質性,這邊使用到的是 Welch's t-test,在 Welch's t-test - Wikipedia 中提到的:
Welch's t-test is an adaptation of Student's t-test, and is more reliable when the two samples have unequal variances and/or unequal sample sizes.
在假設兩組樣本都具屬於常態分佈的前提下,可以使用 Welch's t-test 來檢測兩者的平均是否等值
Welch's t-test 所定義的 t value 如下
根據 Algorithms for calculating variance - Wikipedia 對於 Welford's online algorithm 的解釋:
It is often useful to be able to compute the variance in a single pass, inspecting each value only once; for example, when the data are being collected without enough storage to keep all the values, or when costs of memory access dominate those of computation.
可以得知 Welford's online algorithm 與直接計算平均與變異數的差別在於不需要先將所有數值加總後才進行計算,而是逐次將數值加入並計算對應的 與 。
其手法的原理大致如下:
假設現在我們有以下共 個的樣本
一般我們計算平均的方式是
若我們要在此計算出來的平均上再加入新的樣本 ,則須這樣重新計算平均的數值
對 做 的變換與化簡後則可以寫成
而這樣的做法也可以避免在加總所有實驗數據的時候發生 Overflow 。
但是若直接利用這樣的方式計算變異數的話,則可能會造成部分問題,因為若使用該方法計算 n - 1 個樣本的變異數 來計算 n 的樣本的 時
而在上式在使用電腦計算時發生 Underflow 的情況而產生 Numerical Instability 的情況。
為了免除這樣的問題,所以 Welford method 在操作上定義差值平方的總合為
並將上述 處的等式兩側同乘 後則變成
待我們計算出 後再換算回變異數即可
根據上述論文的理論,在 lab0-c/dudect
中對應的實作流程如下:
生成隨機字串
因為在我們測試的 list 中是使用字串作為資料存放,所以在測試的一開始會先透過在random.c
中定義的 randombyte
來生成 10000 長度為 7 byte 的字串。
亂數產生 list 長度
在我們的測試程式中,預設會重複測試 10000 組資料,所以會使用前一個步驟中所使用的 randombyte
函式來產生每組測試資料的 list 長度。並利用 randombit
隨機將該組資料分配到 2 個 class 之中。
依序初始化各個測試的 list 並測量函式的執行 cycle 數
在每次進行測試的時候,程式會先根據上個階段產生的 list 長度生成一個隨機的 list,接著執行測試的函式(q_size
或是 q_insert_tail
)進行測試。透過呼叫 Intel x86-64 架構的指令 rdtsc
取得 time stamp counter 的數值,以此來計算執行的 cycle 數。
計算 ,並使用 Welch's t-test 檢驗兩個 class 是否有類似的分佈
將我們在第 3 步測得的資料依照在第 2 步中分配到的 class 分別計算 與 ,使用上述的 Welford's online algorithm 來計算兩個 class 個別的 與 。最後利用前一步取得的資料計算出 t 值
在 lab0-c/dudect
的實作中,若 則表示兩組資料具有相同的分佈,也就可以合理的推論我們所測量的函式執行時間應為常數時間,也就是 的複雜度。
rdtsc
指令計算 cycle 數所造成的問題在〈Using the RDTSC Instruction for Performance Monitoring〉這篇文章中有提到因為 Intel Pentium 系列的處理器有支援 out-of-order execution ,也就是亂序執行的功能,所以在使用該指令取得 cycle count 的時候可能我們想要測量的函式還沒有被執行到,而導致無效的測量。
而在 多核时代不宜再用 x86 的 RDTSC 指令测试指令周期和时间 中也有提到使用 rdtsc
來計時或是 benchmarking 可能會也以下幾種潛在的問題:
dropsize
忽略部分資料在 dudect/constant.c
中可以看到工具中有設定 dropsize
的數值為 20,但在套用統計模型的時候卻沒有看到 dudect
有針對測試的數據做後處理,而是直接將前 20 筆的資料捨棄掉,計算第 20 ~ 9999 的資料。
在該篇論文中第三部分 Result 的 B. Memory Comparison 中可以看到在使用 Timing Leakage Detection 進行測試的時候,他們所使用的 t value 的 threshold 為 4.5 但是實際上在 lab0-c/dudect
中看的 threshold 則為 10
在目前的實作中並沒有在論文中提到的後處理,而是單純以一階 t-test 來判斷是否有 timing leakage
而在實際執行時有時會遭遇這樣的情形:
在演算法確定為 的情況下卻出現 的情況,而被判定為非常數時間。
推測其發生的狀況可能是因為 content switch 或是 interrupt 所造成,所以先導入前處理的方式來針對異常資料做剔除的動作。
在論文中提到兩種前處理的手法:
而在 dudect 中也有對應的實作,我將 lab0-c 的程式碼進行修改以導入上述兩個手法,但是在執行的時候發現在使用 percentile 剔除異常資料後,即使 q_size
已是常數時間的實作仍會被判定為 not constant time 的狀況。
因此我將 class 0 與 class 1 的測量結果,也就是 exec_time
的資料提取出來,從圖表進行分析:
q_size
的結果可以看到大部分的測量結果是落在 0 ~ 100 個 cycle 的區間中,但也可以看到有部份的資料是超過甚至遠大於 100 個 clock cycle 的情況,在 10000 筆資料中,有 154 筆的資料大於 100 個 cycle。
q_size
的結果在本圖中可以看到在 的實作下 fixed class 與 random class 的分佈其實是十分類似的。但在結果中可以看到 random class 的測量結果較 fixed class 的結果集中在約 15 ~ 35 個 cycle 的區間中。而在本結果中超過 100 個 cycle 的資料共有 156 筆。
為了比較 與 兩個實作的差異,我將 q_size
的函式修改為以下 的實作:
並將 fixed class 與 random class 的執行結果繪成下圖:
q_size
執行時間結果可以看到上圖其實與上述常數時間實作的結果大致相同,因為 fixed class 的測試都是對一個長度為 0 的 list 呼叫 q_size
,所以在測量時間上與常數時間的分佈不會有太大的差異。
q_size
執行時間結果從這個上圖的結果可以看到 版本的 q_size
在測量數值上不管是數量級或是 cycle 數的分佈上都與 fixed class 的版本或是 的情況相當的不同,所以在使用 t-test 來檢測的時候就可以很容易的得到 50 以上的 t value 進而推斷這樣的實作不是 的實作。
clock_gettime
進行測量由於上述提到使用 Intel 架構的 rdtsc
指令可能會有測量誤差的問題,因此我將 cpucycle.h
中用來測量的函式改以 time.h
的 clock_gettime
來進行測量。
原本想要用
sys/time.h
的gettimeofday
來進行測量,但是因為q_size
的執行時間太短,無法使用 microsecond 的精度來進行測量,因此改以clock_gettime
來實作。 [color= orange] Oscar
q_size
測量結果q_size
測量結果sysprog2020