c++optimizationbit-manipulation

Branchless code that maps zero, negative, and positive to 0, 1, 2


Write a branchless function that returns 0, 1, or 2 if the difference between two signed integers is zero, negative, or positive.

Here's a version with branching:

int Compare(int x, int y)
{
    int diff = x - y;
    if (diff == 0)
        return 0;
    else if (diff < 0)
        return 1;
    else
        return 2;
}

Here's a version that may be faster depending on compiler and processor:

int Compare(int x, int y)
{
    int diff = x - y;
    return diff == 0 ? 0 : (diff < 0 ? 1 : 2);
}

Can you come up with a faster one without branches?

SUMMARY

The 10 solutions I benchmarked had similar performance. The actual numbers and winner varied depending on compiler (icc/gcc), compiler options (e.g., -O3, -march=nocona, -fast, -xHost), and machine. Canon's solution performed well in many benchmark runs, but again the performance advantage was slight. I was surprised that in some cases some solutions were slower than the naive solution with branches.


Solution

  • int Compare(int x, int y) {
         return (x < y) + (y < x) << 1;
    }
    

    Edit: Bitwise only? Guess < and > don't count, then?

    int Compare(int x, int y) {
        int diff = x - y;
        return (!!diff) | (!!(diff & 0x80000000) << 1);
    }
    

    But there's that pesky -.

    Edit: Shift the other way around.

    Meh, just to try again:

    int Compare(int x, int y) {
        int diff = y - x;
        return (!!diff) << ((diff >> 31) & 1);
    }
    

    But I'm guessing there's no standard ASM instruction for !!. Also, the << can be replaced with +, depending on which is faster...

    Bit twiddling is fun!

    Hmm, I just learned about setnz.

    I haven't checked the assembler output (but I did test it a bit this time), and with a bit of luck it could save a whole instruction!:

    IN THEORY. MY ASSEMBLER IS RUSTY

    subl  %edi, %esi
    setnz %eax
    sarl  $31, %esi
    andl  $1, %esi
    sarl  %eax, %esi
    mov   %esi, %eax
    ret
    

    Rambling is fun.

    I need sleep.