有没有办法将条件赋值转换为无分支代码?

时间:2017-04-13 02:08:04

标签: c branch-prediction

有没有办法将以下C代码转换为没有任何条件语句的东西?我已经描述了我的一些代码,并注意到它在if语句中得到了很多分支遗漏,与此非常相似。

int cond = /*...*/;
int a = /*...*/;
int b = /*...*/;

int x;
if (cond) {
   x = a;
} else {
   x = b;
}

3 个答案:

答案 0 :(得分:4)

这取决于您定位的指令集。对于x86,有cmov。对于arm64,有csel。对于armv7,mov有一个可选的条件操作码。

任何体面的编译器都应该能够将您拥有的代码优化到最佳指令集中。 GCC和clang这样做(在https://gcc.godbolt.org/处自己试试。)

更直接地回答你的问题:没有办法在直接C中强制执行此操作,因为CPU指令集可能没有可用作替代的无分支指令。所以你要么必须依赖你的编译器(这可能是一个好主意),或者手工编写你自己的程序集。

为了举个例子,请考虑以下C代码:

int min(int a, int b) {
  int result;
  if (a < b) {
    result = a;
  } else {
    result = b;
  }
  return result;
}

gcc 5.4.1 for armv7生成:

min(int, int):
        cmp     r0, r1
        movge   r0, r1
        bx      lr

arm64的gcc 5.4生成:

min(int, int):
        cmp     w0, w1
        csel    w0, w0, w1, le
        ret

clang 4.0 for x86生成:

min(int, int):                               # @min(int, int)
        cmp     edi, esi
        cmovle  esi, edi
        mov     eax, esi
        ret

x86的gcc 5生成:

min(int, int):
        cmp     edi, esi
        mov     eax, esi
        cmovle  eax, edi
        ret

xc的icc 17生成:

min(int, int):
        cmp       edi, esi                                      #8.10
        cmovl     esi, edi                                      #8.10
        mov       eax, esi                                      #8.10
        ret                                                     #8.10

如您所见,他们全部无分支(在-O1或以上编译时)。

答案 1 :(得分:1)

更完整的示例会更有帮助,因为访问变量when(mapper.writeValueAsString(any(Object.class))).thenThrow(new JsonProcessingException("Error"){}); xab的方式可以发挥作用。如果它们是在执行条件赋值的函数之外声明的全局变量,那么将使用加载和存储来访问它们,编译器可能认为它们过于昂贵而无法有条件地执行。 查看https://godbolt.org/g/GEZbuf处的示例,其中对全局变量和cond以及foo中的本地参数执行相同的条件赋值

答案 2 :(得分:0)

x = (!cond * b) | (!!cond * a);