【问题标题】:Bithacks: Determine whether value is less, greater, or equal to some valueBithacks:确定值是小于、大于还是等于某个值
【发布时间】:2015-11-17 04:01:17
【问题描述】:

我正在研究的算法必须经常检查某个任意整数值“x”是否小于、大于或等于另一个任意整数值“y”。我实现它的语言是 C。

一种天真的方法是使用 if-then-else 分支来检查这一点,但这不会以最佳方式工作,因为处理器的分支预测器会搞砸。我试图仅使用算术/逻辑评估以及按位运算来实现这种比较,但老实说,我的大脑现在卡住了。

我将调用函数 f(x, y)。如果 x y。

我的一个想法是评估:

x = 3 * (x > y)

当 x > y 时返回 3,否则返回 0。如果 x == 0 使用一些按位运算符和条件 x == y 或 x

最后,我正在寻找任何函数 f(x, y),无论是否使用 bithacks,它都能以最少的操作次数为我提供结果;它只需要快。因此,如果您有任何其他我可能没有考虑过的想法,也非常感谢您向我指出另一个解决方案。

【问题讨论】:

  • 您的编译器可能不得不发出比较指令和分支,以获得正确的比较结果。在尝试过早优化之前查看汇编代码。如果速度是一个很大的问题,您可能会使用完全不同的方法来并行评估等。但这在很大程度上取决于您的架构、编译器和自己的能力。
  • 您的 xy 的域是什么?它们是整数吗?它们可以是负数吗?他们可以有不同的标志吗?
  • 嗯,大多数主要处理器都有条件移动,所以......除非立即需要结果,否则它或多或少是免费的操作。当然,如果你有一个依赖链,那么比较和移动都会开始(但是,这就像 5-6 个周期......)。
  • 你真的认为调用一个函数来比较两个整数比最优秀的大脑可以在 CPU 和编译器中进行的优化更有效吗?
  • 编写您的编译器的人已经研究了所有的 bithacks 技巧,然后是一些。如果有一个棘手的做事方式,编译器知道这一点。但是如果你对代码进行了足够的混淆,你最终可能会得到编译器理解的东西。

标签: c algorithm optimization bit-manipulation


【解决方案1】:

下面的表达式会做你想做的事。

1 + (x >= y) + (x > y)

在 x86-64 上这个compiles to a fairly-efficient code using SETcc instead of branches:

compare(int, int):
    xorl    %edx, %edx
    cmpl    %esi, %edi
    setg    %al
    setge   %dl
    movzbl  %al, %eax
    leal    1(%rdx,%rax), %eax
    ret

在 ARM 上:

compare(int, int):
    cmp r0, r1
    ite lt
    movlt   r0, #1
    movge   r0, #2
    it  gt
    addgt   r0, r0, #1
    bx  lr

【讨论】:

    【解决方案2】:

    只需减去xy 2 个变量。

    你会得到:

    1. 如果x<y 结果是res<0
    2. 如果x>y 结果是res>0
    3. 如果x==y 结果是res==0

    在宏中实现

    #define Chk(x, y) ((x)-(y))
    

    另一个优点是您可以简单地使用! 运算符来检查相等或不相等:

    if (!Chk(x, y))
    {
        // x == y
    }
    else
    {
        // x != y
    }
    

    附:这与strcmp() 等许多标准函数得出的结果相同。

    附言请考虑处理器机器指令cmp,至少对于我知道的所有 CPU 类型,执行两个操作数之间的减法并设置标志以反映结果。即使只是比较 C 中的两个值,也会产生具有 cmp 指令和一些分支(如 jzjl 等)的代码。

    仅存储值的差异,一个值,允许您保留信息,甚至用于以后的评估,包含您可能需要的所有元素。

    【讨论】:

    • 与简单地以自然方式使用比较运算符不同,这种减法可能会溢出导致不正确的结果。检查溢出会大大减慢它的速度。幸运的是,这不是必需的,因为您可以只使用比较运算符,而且很可能也可以获得最佳速度。
    • 感谢您的努力,但此解决方案使用分支
    【解决方案3】:

    一个选项是:

    int f(int x,int y)
    {
        return ((x-y)>>31)-((y-x)>>31) + 2;
    }
    
    
    int main(int argc, char *argv[])
    {
        int x,y;
        for(x=-3;x<=3;x++)
        for(y=-3;y<=3;y++)
            printf("x=%d y=%d f(x,y)=%d\n",x,y,f(x,y));
        return 0;
    }
    

    这依赖于 int 类型是 32 位数量。

    您可能还想查看 SIMD 指令(例如 x86 上的 SSE 或 Arm 上的 Neon),因为这些可以帮助您加速您的代码。

    【讨论】:

    • 有符号整数的右移由实现定义。
    • 优点:谨慎使用(例如,在具有明确移位行为的已定义架构上使用 SIMD 指令时)
    猜你喜欢
    • 1970-01-01
    • 2021-03-24
    • 1970-01-01
    • 2014-07-19
    • 1970-01-01
    • 2014-04-19
    • 2013-06-19
    • 2014-12-20
    • 2016-09-30
    相关资源
    最近更新 更多