在围绕Compiler Explorer进行测试时,我尝试了以下无溢出函数来计算 2 个无符号 32 位整数的平均值:
uint32_t average_1(uint32_t a, uint32_t b)
{
if(a < b){
a ^= b;
b ^= a;
a ^= b;
}
return b + (a - b) / 2;
}
Run Code Online (Sandbox Code Playgroud)
被编译成这样:(与激活的-O1, -O2,-O3优化相同)
average_1:
cmp edi, esi
jnb .L2
mov eax, edi
mov edi, esi
mov esi, eax
.L2:
sub edi, esi
shr edi
lea eax, [rdi+rsi]
ret
Run Code Online (Sandbox Code Playgroud)
其中代码的交换部分经过优化,可以使用mov具有 1 个附加寄存器的指令。
我已经阅读了这些问题:
并得到:
在英特尔 AVX 上,存在无分支代码的可能性。您可以计算这两种情况,并根据条件混合结果,而不是针对 case0 或 case1 进行分支。
AVX 使用vblendps指令以 8 种方式实现浮动。
您还可以使用 x86 指令CMOVcc以标量方式(无需向量)执行此操作,该指令有条件地执行移动操作。
RISCV64 可以做这样的标量移动吗,这样你就不必分支
a = c ? x : y;
Run Code Online (Sandbox Code Playgroud)
据我了解,RISCV 实现是有序的,因此在不需要分支时它比 x86 更有好处。(后者至少可以围绕一些指令进行洗牌,甚至可以推测性地分支以隐藏延迟。)
我能找到的最接近 riscv 的无分支操作是SLT(设置小于),但设置为 1 或 0,然后需要乘法?将 SLT 设置为 -1 或 0 不是更有用,这样我们就可以进行 AND 运算吗?
做时:
int foo(int a, int b, int x, int y)
{
return a < b ? x : y;
}
Run Code Online (Sandbox Code Playgroud)
我尝试了使用 SLT 的穷人版本的无分支。我不确定我是否完全正确,通过使用位掩码作为 0 - 条件(0|1),我想出了:
branchless: …Run Code Online (Sandbox Code Playgroud)