これは、より簡潔で 1.6 倍高速な C++ のソリューションです。深いパイプラインと複雑な分岐予測ロジックを備えたハイエンド マイクロプロセッサに適したコードを生成します。比較/分岐なし、テーブル ルックアップなし (データ キャッシュ ミスなし) で true/false を生成します。
ニブルには 4 ビットがあり、したがって 16 の値の 1 つを保持します。各入力の 2 つのニブルを符号なし値 (少なくとも 16 ビットを持つ) にマップし、対応するビット位置に設定されたビットを使用して、入力。次にAND
、2 つのビットセットを計算して、セットの共通部分を計算します。最後AND
は nibble との一致を破棄します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
$