2022q1 Homework2 (quiz2)
contributed by < Destiny0504
>
題目連結
測驗一
用 a & b 來實現 a - b 的操作,
兩種實作方式的組合語言比較
|
第一種實作方式 |
第二種實作方式 |
register 使用數量 |
4 |
3 |
add指令數 |
2 |
1 |
and指令數 |
2 |
1 |
mov指令數 |
2 |
1 |
shr指令數 |
2 |
1 |
or指令數 |
0 |
1 |
結論
第一種實作方式需要用到 a 、b 個別的值,所以需要花費額外的 register 將它存起來,所以在實作的要盡量避免這種所有要加起來的值都有關係的情況。
Exponentially weighted moving average ( EWMA )
優點
- 只要紀錄上一次的值就可以得到下一項應該是多少,能減少使用的記憶體空間。
- 可以動態的計算結果,不需要一次就看完所有的資料。
在 linux kernel 中的實作
- linux kernel 中是以一個 pointer 指向一個 unsigned long 的變數,而這個變數就是用來儲存過去計算過得結果的。
- 考慮以下實作的方法,internal 先乘以 2 的 weight_rcp 次方減去原本的 internal,相當於 ,在加上新的 val 。
- e.g. 如果 weight_rcp = 8 ,相當於每次把過去運算的結果乘以 ,再加上新的 val 乘以 。
測驗二
由 a <= b 來判斷我們應該回傳 a 還是 b
- 當 a > b 的時候 -(a <= b) 會得到零,所以可以直接無視 (b ^ a) 會得到的值,而零跟 a 做 XOR 則會得到 a ,所以回傳值為 a
- 當 a > b 的時候-(a <= b) 會得到 1 ,所以可以直接視為回傳的值為 a ^ (b ^ a),而 a 跟 b ^ a 做 XOR 則會得到 b ,最後得到的回傳值為 b
- 正確答案是給 a ^ b 和 a < b, 但我想我的實作也可以達到同樣的效果。
Branch-free 版本
利用 a < b 時,a - b 會 overflow 的特性,所以只要判斷是否有 overflow 的情況即可。
- 因為 a 和 b 都是 unsigned int,所以不能只由最高位來判斷是否出現 overflow,還要接著判斷 a 的最高位是否大於 ,如果兩者皆符合,才能判斷為遇到 overflow
測驗三
以下程式碼的運作原理為 Euclidean algorithm ,差別只在於每一步找都先把 v 換成奇數而已。而之所以可以只留下奇數的部份,是因為我們前面已經把 u 跟 v 所擁有的 2 的倍數的公因數都去除掉了。( 可以參考 reference 提到的演算法 )
用 __builtin_ctz
改寫 GCD 的版本
Reference
GCD 演算法
測驗四
- 因為 2 complement 的編碼特性,所以一個數字
a
在跟 -a
取 bitwise and 會剛好找出目前最低位元的 1 。
- e.g. a = 12 轉換為二進位 =
01100
, -a 轉換為二進位 = 10100
,取 bitwise and 之後剛好等於 00100
測驗五
測驗六
- 我們在測驗中所作的版本能夠直接回傳 _h 的 datatype 在 memory 中所佔有的 byte 數量。
- 運作的方式是先拿到 _h 的記憶體位置,減去 char 的 pointer 的位置。
- 作法有點類似 container_of,差別在於一個是加上 offset 得到指向某一個變數的 pointer,而 alignof 是算出 offset
- 注意:這邊 char c 要擺在 t _h 前面,否則無法算出 t 的記憶體大小。( 因為 struct 為連續的記憶體區塊的關係 )
align function 說明
- ALIGN(x, a) 要拿 x 以 a 作為對齊的基準進行向上對齊
- e.g. a = 128, x = 12 會回傳 128
- 注意:這個 function 只有 a 為 2 的整數次方算出來的對齊結果才是對的。因為這個 function 通常是用來計算記憶體的位置的,所以只有在上述的情況下算出來的結果才是對的也沒問題。
- ALIGN_DOWN(x, a) 要拿 x 以 a 作為對齊的基準進行向下對齊
- e.g. a = 128, x = 12 會回傳 0
- 注意:這個 function 跟 ALIGN(x, a) 的使用條件一樣
Reference
linux 實作的 align
linux 實作的 align_kernel
測驗七
因為在 compile 的時候 M3 、M5 都已經被計算好,而且乘法的運算速度又比除法快,所以會比另一種實作方法快。
兩種方法的時間比較
- 用 linux 的 time 指令進行測量,且迴圈執行
100000
次
- 方法一為題目中的 naive.c ,方法二為上面的程式碼
- 從下方的結果來看,方法二確實有更好的 performance
|
方法一 |
方法二 |
實驗一 |
0.438 |
0.330 |
實驗二 |
0.442 |
0.445 |
實驗三 |
0.478 |
0.447 |
實驗四 |
0.427 |
0.464 |
實驗五 |
0.457 |
0.478 |
實驗六 |
0.494 |
0.451 |
實驗七 |
0.518 |
0.303 |
實驗八 |
0.480 |
0.375 |
實驗九 |
0.394 |
0.447 |
實驗十 |
0.586 |
0.269 |
average |
0.4714 |
0.4009 |
max |
0.586 |
0.478 |
min |
0.394 |
0.269 |