在英特爾 AVX 上,可能會出現無分支代碼。您可以計算這兩種情況,并根據條件混合結果,而不是為 case0 或 case1 分支。
AVX 使用vblendps指令以 8 種方式進行浮點運算。
您也可以使用 x86 指令CMOVcc 有條件地執行移動操作,以標量方式執行此操作,無需向量。
注意:ARM 有CSEL,NEON 有VBSL。
RISCV64可以像這樣做一個標量移動,這樣你就不必分支了
a = c ? x : y;
據我了解,RISCV 實作是有序的,因此在不必分支時它會比 x86 受益更多。(后者至少可以對一些指令進行洗牌,甚至可以推測性地分支以隱藏延遲。)
我能找到的最接近 riscv 的 wrt 無分支操作是SLT(設定小于),但設定為 1 或 0,然后需要乘法?將 SLT 設定為 -1 或 0 不是更有用,以便我們可以 AND 嗎?
更新
做的時候:
int foo(int a, int b, int x, int y)
{
return a < b ? x : y;
}
我使用 SLT 嘗試了窮人版本的無分支。我不確定我是否完全正確,通過使用位掩碼作為 0 - 條件(0|1),我想出了:
branchless:
SLT t0,a0,a1
SUB t0,zero,t0
NOT t1,t0
AND t0,a2,t0
AND t1,a3,t1
OR a0,t0,t1
RET
.size branchless, .-branchless
作為無分支版本:
branched:
BGE a0,a1,.L2
MV a3,a2
.L2:
MV a0,a3
RET
.size branched, .-branched
我想知道我是否為此使用了太多指令,但我測量到分支版本比隨機資料上的非分支版本稍快,但速度并不快。
uj5u.com熱心網友回復:
提議的 RISC-V 擴展 B 包括cmov(有 4 個運算元:3 個輸入和一個單獨的目標!)。
我認為 David Patterson(MIPS 和 RISC-V 背后的首席架構師之一)真的不喜歡cmov(以及像 SSE/AVX 這樣的短向量 SIMD),并且認為 CPU 應該專門處理“吊床”分支(在單個指令上跳轉,例如一個動作)如果他們想這樣做。類似的東西。因此,這似乎是哲學純潔性妨礙了包含有用說明的情況。(AArch64 是一種更加實用的設計,在高性能實作方面仍然是 RISC。)
和/或可能希望將指令限制為最多 2 個輸入,如果沒有任何其他 3 輸入指令。這意味著一個標量流水線只需要 2 個暫存器讀取埠,而不是 3 個,如果它嚴格遵循這個限制的話。(這也意味著沒有帶進位的加法運算,當您必須處理同一個加法運算的進位和進位時,擴展精度數學對于超過 2 個暫存器的數字來說非常痛苦。)
您可以cmov使用 AND/ANDnot/OR 的掩碼進行模擬,但這需要相當多的指令,并且通常不值得,除非可能在廣泛而深度的無序機器上,其中作業量被丟棄一個分支小姐要大得多。(mask = (c == 0) - 1;您可以使用sltiu/add reg,reg, -1將 0 變為 -1 并將 1 變為 0。)
就哪種微架構從 CMOV 中獲益更多而言,你有點倒退了,盡管這兩種方式都有潛在的好處。有序機器已經不得不在條件分支處等待條件解決,而無序機器處理控制依賴關系與資料依賴關系非常不同。正如gcc 優化標志 -O3 使代碼比 -O2 慢中所討論的,資料依賴關系cmov可以創建一個回圈攜帶的依賴關系鏈,這是高度可預測分支的更大瓶頸。
有一些亂序的 exec RISC-V 設計,甚至可能是開源的。例如,Erik Eidt將 Berkeley Out-of-Order Machine (BOOM)聯系起來。
擴展 B:他們把他們遺漏的所有有趣的指令放在了哪里
RISC-V 擴展 B 提案具有條件移動,以及標量最小/最大值、popcount、前導/尾隨零計數、位域插入/提取、兩個暫存器移位以及一堆更深奧的東西。 https://five-embeddev.com/riscv-bitmanip/draft/bext.html#conditional-move-cmov
查看提議的指令串列,令人驚訝的是基線 RISC-V 遺漏了什么,例如窄整數的符號擴展(當前需要 slli/srai),如果呼叫約定或加載指令尚未保證,以及標準大多數 ISA 擁有的諸如 popcount 和前導/尾隨零計數之類的東西。
Godboltcmov使用、min和顯示 clang 12.0sext.b。在那個鏗鏘的版本中,-O3 -Wall -menable-experimental-extensions -march=rv32gcb0p93是魔法咒語做到這一點。擴展 B 0.93 由b0p93部分字串啟用。(擴展 B 尚未最終確定,我不知道 clang 14.0 正在尋找什么版本;它的錯誤訊息沒有幫助,只是-march=rv32gcb沒有讓編譯器實際使用cmov。)
// -march=rv32gcb0p93 includes extension b 0.93 (0p93)
int sel(int x, int y, int c){
return c ? x : y;
}
# extension B clang
cmov a0, a2, a0, a1
ret
# baseline gcc11.3 (clang and GCC12 waste several mv instructions)
bne a2,zero,.L2
mv a0,a1
.L2:
ret
int min(int x, int y, int c){
return (x<y) ? x : y;
}
# extension B clang
min a0, a0, a1
ret
# baseline gcc
ble a0,a1,.L5
mv a0,a1
.L5:
ret
int sext(int c){
return (signed char)c;
}
# extension B clang
sext.b a0, a0
ret
# baseline gcc
slli a0,a0,24
srai a0,a0,24
ret
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/481976.html
下一篇:是否可以檢查記憶體地址是否有效?
