如果 x > y,则用 y 交换 x 的无分支版本?

Branchless version of swapping x with y if x > y?

本文关键字:版本 交换 分支 gt 如果 则用      更新时间:2023-10-16

假设xy是签名的整数,是否有一些超级有效的技巧:

if (x < y) {
    std::swap(x, y);
}

我可以立即使用c = x < y来考虑解决方案,然后您将x分配给c * x + (1 - c) * y等。但是,此方法会发出乘法指令,我想避免使用。有没有办法独自摆弄?

编辑:只需澄清我真正关心的就是试图摆脱由if引起的分支。换句话说,我知道要交换的Xor技巧,但这不是我要问的。

我不确定,这是您的代码速度,但这是无分支解决方案:

#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {
  int a = atoi(argv[1]);
  int b = atoi(argv[2]);
  int c = a - b;
  c &= c >> 31; // SXT for signed int
  a -= c;
  b += c;
  printf("Result: %d %dn", a, b);
}

如果xy在此操作后写入内存,那么您可以使用写入动态内存位置而不是条件跳跃。例如,对a[0], a[1]进行排序:

int x = a[0];
int y = a[1];
a[x >= y] = x;
a[y > x] = y;

如果您需要立即读回值,那么它可能会比可预测的分支慢,但是它可能取决于处理器。

实现交换的最有效方法是认识到您具有与名称关联的名称和数据,并且可以交换名称而不是交换数据。

例如,编译器可以转换以下:

if (x < y) {
    std::swap(x, y);
}
do_something(x, y);
return x;

..进入:

if (x < y) {
    // Names of "x" and "y" swapped in subsequent code
    do_something(y, x);
    return y;
} else {
    do_something(x, y);
    return x;
}

当然要交换名称而不交换数据通常是免费的(用于性能),因为您实际上并没有交换任何内容。

现代CPU执行完全相同的技巧。

专门;CPU具有寄存器,并且寄存器是与数据关联的名称。对于xchg eax,ebx(在80x86)之类的指令,CPU将仅交换寄存器名称,而不会移动数据。这意味着,当尚不知道任一寄存器中的数据中的数据时,CPU可以进行互换(例如,由于先前的指令仍在计算或获取)。

换句话说;实现std::swap(x, y);的最快方法是确保为CPU生成正确的指令(例如,在80x86上给出CPU的xchg eax,ebx,该指令没有分支,而不必等到值才知道)。<<<<)。/p>

正如其他人建议的那样,您可以尝试根据std::min()std::max()

但是不能保证。该语言只是缺少一种表达您想要的编译器的方法。

关于我可能提供的唯一其他非C 解决方案将是内联汇编,您可以精确地编写所需的说明。但是,使用内联装配会影响编译器对周围的代码的作用,并且可能会产生负面影响(例如,使用寄存器,注册溢出等)可能会抵消或否定任何预期的收益。