快速按位比较

时间:2012-08-28 02:18:43

标签: math language-agnostic bitwise-operators

我有两个字节,由两个打包在一起的4位数字组成。我需要知道第一个字节中的两个数字中的任何一个是否匹配第二个字节中的任何一个数字。零被视为空,不应与自身匹配。

显然,我可以通过打开数字并逐个比较来做到这一点:

a = 0b10100101;
b = 0b01011111; // should have a match because 0101 is in both

a1 = a>>4; a2 = a&15;
b1 = b>>4; b2 = b&15;

return (a1 != 0 && (a1 == b1 || a1 == b2)) || (a2 != 0 && (a2 == b1 || a2 == b2));

//     ( true   && (  false  ||   false )) || ( true   && (  true   ||   false ))
//     ( true   &&         false         ) || ( true   &&         true          )
//            false                        ||         true
//                                        TRUE

然而,我只是想知道是否有人知道更清洁的方法吗?

3 个答案:

答案 0 :(得分:2)

预先计算答案并将其存储在查找表中。表格的关键是16位((a<<8)+b)。它只需要1位输出(使用8K),但为简单起见,您可以使用8位(使用64K)。

答案 1 :(得分:0)

更简洁的方法是摆脱难以解析的表达式并使代码更具可读性。

def sameNybble (a, b):
    # Get high and low nybbles.

    ahi = (a >> 4) & 15 ; alo = a & 15;
    bhi = (b >> 4) & 15 ; blo = b & 15;

    # Only check ahi if non-zero, then check against bhi/blo

    if ahi != 0:
        if ahi == bhi or ahi == blo:
            return true

    # Only check alo if non-zero, then check against bhi/blo

    if alo != 0:
        if alo == bhi or alo == blo:
            return true

    # No match

    return false

任何体面的优化编译器基本上都会为您提供相同的底层代码,因此有时可以更好地优化可读性。

答案 2 :(得分:0)

这是一个C ++解决方案,它更简洁,速度提高了1.6倍。它生成的代码对于具有深度流水线和复杂分支预测逻辑的高端微处理器更友好。它生成true / false,没有比较/分支,也没有表查找(没有数据缓存未命中)。

半字节有4位,因此保持16个值中的一个,我将每个输入中的两个半字节映射到无符号值(至少有16位),在相应的位位置设置位,表示两个半字节值出现在输入中。然后我AND两个位集,从而计算集合的交集。最后一个AND会丢弃与半字节0匹配的所有匹配项。

inline unsigned set( unsigned char X ) {
    return (1 << (X & 15)) | (1 << (X >> 4));
}

// Return true if a nibble in 'A' matches a non-null nibble in 'B'
inline bool match( unsigned char A, unsigned char B ) {
    return set( A ) & set( B ) & ~set( 0 );
}

我在Intel Xeon X5570 @ 2.93GHz上计时,它比问题原版快1.6倍。这是我用来计时的代码:

#include <time.h>
#include <iostream>

bool original( unsigned char A, unsigned char B ) {
    unsigned char a1 = A >> 4;
    unsigned char a2 = A & 15;
    unsigned char b1 = B >> 4;
    unsigned char b2 = B & 15;

    return (a1 != 0 && (a1 == b1 || a1 == b2)) || (a2 != 0 && (a2 == b1 || a2 == b2));
}

static inline unsigned set( unsigned char X ) {
    return (1 << (X & 15)) | (1 << ((X >> 4)&15));
}

bool faster( unsigned char A, unsigned char B ) {
    return set( A ) & set( B ) & ~set( 0 );
}

class Timer {
    size_t _time;
    size_t & _elapsed;
    size_t nsec() {
        timespec ts;
        clock_gettime( CLOCK_REALTIME, &ts );
        return ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
    }
public:
    Timer(size_t & elapsed) : _time(nsec()), _elapsed(elapsed) {}
    ~Timer() { _elapsed = nsec() - _time; }
};

int main()
{
    size_t original_nsec, faster_nsec;
    const size_t iterations = 200000000ULL;
    size_t count = 0;

    { Timer t(original_nsec);
        for(size_t i=0; i < iterations; ++i) {
            count += original( 0xA5 , i & 0xFF );
        }
    }

    { Timer t(faster_nsec);
        for(size_t i=0; i < iterations; ++i) {
            count += faster( 0xA5 , i & 0xFF );
        }
    }

    std::cout << double(original_nsec) / double(faster_nsec)
              << "x faster" << std::endl;

    return count > 0 ? 0 : 1;
}

这是输出:

$ g++ -o match -O3 match.cpp -lrt && ./match
1.61564x faster
$