挑最差的 3 號出來研究
3. Bypasses stalled add 108 107 92 109
case 3: independent branch, taken
addi x10, x0, 80
addi x11, x0, 2
div x1, x10, x11 # x1 = 40
add x2, x1, x1 # waits for DIV; x2 = 80
beq x0, x0, target # ready without DIV
addi x2, x0, 99 # skipped architecturally
target: mul x3, x10, x11 # x3 = 160
主要是因為 ooo_retire 的 redirect 會在 retire 階段才發生
導致在 execution 階段後又經過了 wb, complete, retire,之後才重新 fetch, decode, dispatch, issue
相對來說 block 版本的 ooo 早了很多,在 branch target 確定之後就可以把不在目標的 flush 掉
不過這個 cycle count 實在有點過長
假設 ooo 會 block mul 直到 beq 的結果出來 (exeution 結束)
算一算 ooo_retire 從 beq 從 wb -> retire,還有 mul 從 fetch 到 exe 也就差 7 cycle
多出來的 10 cycle 哪來的?
只好又翻出 waveform 來瞧瞧
看完發現是由兩個部分造成
驚不驚喜,意不意外
ooo 的 redirect 時間點不一樣就不特別截圖了,只截了 ooo_retire 的
我們只要知道 ooo 在更早的時候觸發 redirect 會比較快跑完就可以了
可以從下面的 waveform 觀察到剛剛說到的兩個意外
關鍵在 600 ns 的地方,兩邊的 branch 都 retire 了
這邊每個 cycle 10 ns,可以計算 mul 在 execute unit 執行了 33 cycle (560 -> 890 ns)
ooo mul exec:
ooo mul retire:
ooo_retire handshake:
ooo_retire retire:
另外加了一個 sequence 讓有 rename recovery 的 out of order cpu 平反一下
addi x10, x0, 120
addi x11, x0, 3
div x1, x10, x11 # x1 = 40
div x2, x1, x11 # Waits for first DIV; x2 = 13
add x3, x2, x2 # Waits for second DIV; x3 = 26
beq x1, x0, done # Needs only first DIV; NOT taken
# Independent of both divides: a chain of useful younger work
addi x4, x0, 7
addi x5, x4, 1
addi x6, x5, 1
addi x7, x6, 1
addi x8, x7, 1
addi x9, x8, 1
addi x10, x9, 1
addi x11, x10, 1
addi x12, x11, 1
addi x13, x12, 1
addi x14, x13, 1
done:
add x20, x14, x3 # Joins both dependency chains; x20 = 43