2012/10/14
■ SSEによるbitboardの実装の検討
bitboardによる先手銀の駒を取らない移動手のコードは、
org=p->bitboard[BLACK][P_GIN]; // 銀 while ( (from=bit_pop1st(&org)) >= 0 ) { and128(t,attack_gin_b[from],target); mt=from+(P_GIN<<14); while ( (to=bit_pop1st(&t)) >= 0 ) { pin_check() *p_move++=mt+(to<<7); if ( (to <= 26) || (from <= 26) ) *p_move++=mt+(to<<7)+PROMOTE_BIT; } }
bit_pop1stはbitboardのLSBから立っているビット位置を返し、そのビットをクリアする関数である。bitboardが全てゼロのときは-1を返すようにした。
static inline int bit_pop1st(BITBOARD *x) { int pos=-1; #ifdef __x86_64__ if ( x->l64 ) { pos=__builtin_ctzll(x->l64); x->l64&=(x->l64-1); // reset bit } else if ( x->h64 ) { pos=__builtin_ctzll(x->h64)+64; x->h64&=(x->h64-1); // reset bit } #else if ( x->l ) { pos=__builtin_ctz(x->l); x->l&=(x->l-1); // reset bit } else if ( x->m ) { pos=__builtin_ctz(x->m)+32; x->m&=(x->m-1); // reset bit } else if ( x->h ) { pos=__builtin_ctz(x->h)+64; x->h&=(x->h-1); // reset bit } #endif return pos; }
SSE2ではand128などの128bit論理演算が可能である。
#define zero128(r) (r).x=_mm_xor_si128((r).x,(r).x) #define and128(r,a,b) (r).x=_mm_and_si128((a).x,(b).x) #define or128(r,a,b) (r).x=_mm_or_si128((a).x,(b).x) #define xor128(r,a,b) (r).x=_mm_xor_si128((a).x,(b).x) #define andnot128(r,a,b) (r).x=_mm_andnot_si128((b).x,(a).x) #define andor128(r,a,b) (r).x=_mm_or_si128((r).x,_mm_and_si128((a).x,(b).x))
SSE4では次の比較関数も使える。Zフラグに結果が入る。
#define test128(a) (!_mm_testz_si128((a).x,(a).x)) #define andtest128(a,b) (!_mm_testz_si128((a).x,(b).x))
上記手生成コードでSIMD命令が有効となるのはand128であろう。後の命令は非SIMD命令であり、SIMDレジスタと通常レジスタの移動のオーバーヘッドがあるだろう。