在Intel AVX上,有可能出现无分支代码。而不是对case0或case1进行分支,您可以计算这两种情况,并根据条件混合结果。
AVX使用喷雾器指令实现浮点的8种方式。
您也可以使用x86指令CMOVcc (有条件地执行移动操作),以标量方式完成这一操作,而不需要使用向量。
RISCV64是否可以像这样做标量移动,这样您就不必为
a = c ? x : y;据我所知,RISCV实现是有序的,因此当不需要分支时,它将比x86受益更多。(后者至少可以左右一些指令,甚至可以推测地分支以隐藏延迟。)
对riscv最接近的w.r.t无分支操作是SLT (Set小于),但它设置为1或0,然后需要乘法吗?将SLT设置为-1或0不是更有用吗?这样我们就可以这样做了?
更新
在做时:
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我想知道我是否对此使用了太多的说明,但我测量到的分支版本比随机数据上的非分支版本稍微快一些,但没有太多。
发布于 2022-05-22 22:48:12
提议的RISC扩展B包括cmov (有4个操作数:3个输入和一个单独的目的地!)
我认为David ( MIPS和RISC-V的主要架构师之一)真的不喜欢cmov (以及SSE/AVX这样的短向量SIMD ),并认为CPU应该专门处理“吊床”分支(如果他们想要这样做的话,就可以跳过一条指令)。差不多吧。因此,这似乎是一个哲学纯洁的例子,妨碍了包括有用的指令。(AArch64是一种更加实用的设计,在高性能实现的关键方面仍然是RISC。)
如果没有任何其他的3输入指令,并且/或可能希望将指令限制在最多2个输入。这意味着如果标量管道严格遵循这一限制,它只需要2个寄存器读取端口,而不是3个。(这也意味着,当你必须处理进位和执行相同的加法运算时,扩展精度的数学对于大于2个寄存器的数字来说是相当痛苦的。)
你可以像你说的那样模仿cmov和/ANDnot/OR,但这需要相当多的指令,而且通常不值得,除非可能是在宽而深的无序机器上,因为分支机构丢失的工作量要大得多。(mask = (c == 0) - 1;,您可以使用sltiu / add reg,reg, -1将0转换为-1,将1转换为0。)
在哪种微体系结构从CMOV中获益更多的问题上,你可以向后看,尽管无论哪种方式都有潜在的好处。一台有序机器已经不得不在一个条件分支等待条件来解决问题,而一台处理控制依赖与数据依赖的无序机器则有很大的不同。正如在gcc优化标志-O3使代码比-O2慢中所讨论的,通过cmov的数据依赖可以创建一个循环携带的依赖链,这是高度可预测分支的一个更大的瓶颈。
有一些无序的执行RISC-V设计,甚至有些是开源的.例如,Erik链接了伯克利故障机(BOOM)。
分机B:他们把他们遗漏的所有有趣的指令放在那里
RISC-V扩展B方案有一个有条件的移动,还有标量min/max、popcount、前导/尾随零计数、位字段插入/提取、两寄存器移位以及一系列更深奥的内容。https://five-embeddev.com/riscv-bitmanip/draft/bext.html#conditional-move-cmov
从建议的指令列表来看,令人惊讶的是,在基准RISC-V中遗漏了什么,就像窄整数的符号扩展(目前需要slli/srai),如果调用约定或加载指令还没有保证的话,以及大多数ISAs所拥有的流行计数和前导/尾随零计数之类的标准内容。
哥德波特使用cmov、min和sext.b显示clang12.0。在那个经典版本中,-O3 -Wall -menable-experimental-extensions -march=rv32gcb0p93是实现这一目标的神奇咒语。扩展B0.93由字符串的b0p93部分启用。(扩展B还没有完成,我也不知道clang14.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:
retint 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:
retint 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
rethttps://stackoverflow.com/questions/72340698
复制相似问题