contributed by < RoyWFHuang
>
linux_class
開發環境為:
OS: ubuntu 20.04
kernel ver: 5.4.0-99-generic
CPU arch: x86_64
EXP1 = a & b & 1
EXP2 = a & b
EXP3 = a ^ b
延伸問題:
移動平均(Moving average),又稱滾動平均值、滑動平均,在統計學中是種藉由建立整個資料集合中不同子集的一系列平均數,來分析資料點的計算方法。
移動平均通常與時間序列資料一起使用,以消除短期波動,突出長期趨勢或周期。短期和長期之間的閾值取決於應用,移動平均的參數將相應地設置。例如,它通常用於對財務數據進行技術分析,如股票價格、收益率或交易量。它也用於經濟學中研究國內生產總值、就業或其他宏觀經濟時間序列。
所以運算式變成 (a >> 1) + (b >> 1), 但是 x >> 1 之後, 原本的 .5 中的 0.5會因為位移而消失, 而當兩個 odd 相加的時候, 兩個的 0.5 就會造成結果進位, 所以最後要在多一個補償 (a & b & 1) 判別兩個數字是否都為奇數.
O1 下
(a >> 1) + (b >> 1) + (EXP1)
(a & b) + ((a ^ b) >> 1)
O3 / Os 下
(a >> 1) + (b >> 1) + (EXP1)
(a & b) + ((a ^ b) >> 1)
EXP4 = a ^ b
EXP5 = a < b
延伸問題:
解釋上述程式碼運作的原理
針對 32 位元無號/有號整數,撰寫同樣 branchless 的實作
Linux 核心也有若干 branchless / branch-free 的實作,例如 lib/sort.c:
請在 Linux 核心原始程式碼中,找出更多類似的實作手法。請善用 git log 檢索。
COND = v
RET = (u << shift)
延伸問題:
解釋上述程式運作原理;
在 x86_64 上透過 __builtin_ctz 改寫 GCD,分析對效能的提升;
Linux 核心中也內建 GCD (而且還不只一種實作),例如 lib/math/gcd.c,請解釋其實作手法和探討在核心內的應用場景。
EXP6 = bitset & (-bitset)
延伸問題:
解釋上述程式運作原理,並舉出這樣的程式碼用在哪些真實案例中;
設計實驗,檢驗 ctz/clz 改寫的程式碼相較原本的實作有多少改進?應考慮到不同的 bitmap density;
思考進一步的改進空間;
閱讀 Data Structures in the Linux Kernel 並舉出 Linux 核心使用 bitmap 的案例;
PPP = pos–
MMM = list_add
EEE = &heads[remainder % size]
延伸問題:
例如,判斷負號只要寫作 bool isNegative = numerator < 0 ^ denominator < 0;
搭配研讀 The simple math behind decimal-binary conversion algorithms
M = _h
X = 0
延伸問題:
解釋上述程式碼運作原理
在 Linux 核心原始程式碼中找出 alignof 的使用案例 2 則,並針對其場景進行解說
在 Linux 核心源使程式碼找出 ALIGN, ALIGN_DOWN, ALIGN_UP 等巨集,探討其實作機制和用途,並舉例探討 (可和上述第二點的案例重複)。思索能否對 Linux 核心提交貢獻,儘量共用相同的巨集
KK1 = div3
KK2 = div5
KK3 = div3 << 2
過程中,你可能會發現可貢獻到 Linux 核心的空間,請充分討論
首先先來分析 UINT64_C(0xFFFFFFFFFFFFFFFF) / 3 + 1 這段的作用, 參考(Faster remainders when the divisor is a constant: beating compilers and libdivide) 中的範例
The intuition is as follows. To divide by four, you might choose to multiply by 0.25 instead. Take 5 * 0.25, you get 1.25. The integer part (1) gives you the quotient, and the decimal part (0.25) is indicative of the remainder: multiply 0.25 by 4 and you get 1
(0xFFFFFFFFFFFFFFFF) / 3 這個其實就可以視為是 0.25的意義, 用圖來解釋如下 (以下縮短至16bit解釋)
在系統中超過 16bit (overflow) 會自動被切斷, 所以在此數值系統中, 若落在 0x0000 ~ 0x5555之間, 則會被視為可以被整除, 0x5556 ~ 0xAAAA 則是餘一, 0xAAAB ~ 0xFFFF 則是餘二.
而 +1 的目的, 是為了調整讓他確保可以落於區間內.
PS: 這也就是為什麼需要 +1 做為調整, 必須讓數值完全落於區段內. 如果沒有 +1, 2 * (0x5555) = 0xAAAA 則會剛好落於邊界, 無法判別是 1 or 2.
基於這個原理, 於是我們可以改寫
這段變成
可以變成求取餘數.
但問題來了, 為什麼他只能用來計算 uint32_t 以內, 超過不行呢? 以下我們來做個實驗 n = 0x7FFFFFFFFFFFFFFF 跟 n = 0x8000000000000000 套入看看
很明顯的錯誤了, 至於未什麼會出現這錯誤, 原因就在於 +1, 因為每次運算的數值都會被多1出來
e.g
同樣以 16bit為例
num | hex after mul M3 | over num(compare with 0x5555) |
---|---|---|
1 | 0x5556 | 1 |
2 | 0xAAAC | 2 |
3 | 0x0002 | 2 |
4 | 0x5558 | 3 |
5 | 0xAAAE | 4 |
6 | 0x0004 | 4 |
我們換個陳列方式
N | hex | over | N | hex | over | |
---|---|---|---|---|---|---|
1 | 0x5556 | 1 | 4 | 0x5558 | 3 | |
2 | 0xAAAC | 2 | 5 | 0xAAAE | 4 | |
3 | 0x0002 | 2 | 6 | 0x0004 | 4 |
很明顯以2為倍數去增加, 那什麼時候會 overflow? 當他超過到下個區間的時候, 也就是 0x5555 + 0x5555(over flow) 此時的數值就會是錯誤的了.
所以就可以推算出他的最大可運算的數字是多少了(0x5555/2 + 0x5555 = 0x7FFF)
但問題來了 /3 /5 都可以正確算出數字是多少
但是 /7 跟 /9 卻不是此規則
猜測是 0xFFFF…FFFF/7 之後不是整除, 而是 FFFFFFFFFFFFFFFE / 7 才是整除造成, 但我找不出公式可以解決, 有人可以幫忙解決嗎?