SSE と union
はよ評価関数入れろやって感じなんですが、息抜きに高速化でも。
自分のCPU(Core2Duo T8300 2.4GHz)はSSE2までしか対応してないと思ってたけど、Bonanza6.0 の Makefile 見たらデフォルトでSSE4.1使ってて、普通に動いてました。
ってことでBitboardのSSE化にトライ。
SSE使うのもunion使うのも初めてでワクワクしました。
マジで? SSE使って速度1/4って何なん?
Bitboard のソースはこんな感じ。
class Bitboard; extern const Bitboard SetMaskBB[SquareNum]; extern const Bitboard ClearMaskBB[SquareNum]; class Bitboard { public: Bitboard() {} Bitboard(const u64 v0, const u64 v1) { this->p_[0] = v0; this->p_[1] = v1; } u64 p(const int index) const { return p_[index]; } void set(const int index, const u64 val) { p_[index] = val; } // SSE 化出来るならすること。 bool isNot0() const { #ifdef HAVE_SSE4 return !_mm_testz_si128(this->m_, _mm_set1_epi8(0xff)); #else return (this->merge()) ? true : false; #endif } u64 merge() const { return this->p(0) | this->p(1); } Bitboard operator ~ () const { Bitboard tmp; tmp.p_[0] = ~this->p(0); tmp.p_[1] = ~this->p(1); return tmp; } Bitboard operator &= (const Bitboard& rhs) { #if defined (HAVE_SSE2) || defined (HAVE_SSE4) this->m_ = _mm_and_si128(this->m_, rhs.m_); return *this; #else this->p_[0] &= rhs.p(0); this->p_[1] &= rhs.p(1); return *this; #endif } Bitboard operator |= (const Bitboard& rhs) { #if defined (HAVE_SSE2) || defined (HAVE_SSE4) this->m_ = _mm_or_si128(this->m_, rhs.m_); return *this; #else this->p_[0] |= rhs.p(0); this->p_[1] |= rhs.p(1); return *this; #endif } Bitboard operator ^= (const Bitboard& rhs) { #if defined (HAVE_SSE2) || defined (HAVE_SSE4) this->m_ = _mm_xor_si128(this->m_, rhs.m_); return *this; #else this->p_[0] ^= rhs.p(0); this->p_[1] ^= rhs.p(1); return *this; #endif } Bitboard operator <<= (const int i) { this->p_[0] <<= i; this->p_[1] <<= i; return *this; } Bitboard operator >>= (const int i) { this->p_[0] >>= i; this->p_[1] >>= i; return *this; } Bitboard operator & (const Bitboard& rhs) const { Bitboard temp(*this); temp &= rhs; return temp; } Bitboard operator | (const Bitboard& rhs) const { Bitboard temp(*this); temp |= rhs; return temp; } Bitboard operator ^ (const Bitboard& rhs) const { Bitboard temp(*this); temp ^= rhs; return temp; } Bitboard operator << (const int i) const { Bitboard temp(*this); temp <<= i; return temp; } Bitboard operator >> (const int i) const { Bitboard temp(*this); temp >>= i; return temp; } // todo: SSE化すること。 bool operator == (const Bitboard& rhs) const { return ((this->p(0) == rhs.p(0)) && (this->p(1) == rhs.p(1))) ? true : false; } // todo: SSE化すること。 bool operator != (const Bitboard& rhs) const { return ((this->p(0) != rhs.p(0)) || (this->p(1) != rhs.p(1))) ? true : false; } bool isSet(const Square sq) const { return (*this & SetMaskBB[sq]).isNot0(); } void setBit(const Square sq) { *this |= SetMaskBB[sq]; } void clearBit(const Square sq) { *this &= ClearMaskBB[sq]; } void xorBit(const Square sq) { (*this) ^= SetMaskBB[sq]; } void xorBit(const Square sq1, const Square sq2) { (*this) ^= (SetMaskBB[sq1] | SetMaskBB[sq2]); } void xorBit(const Bitboard bb) { (*this) ^= bb; } // Bitboard の right 側だけの要素を調べて、最初に 1 であるマスの index を返す。 // そのマスを 0 にする。 // Bitboard の right 側が 0 でないことを前提にしている。 Square firstOneRightFromI9() { const Square sq = static_cast<Square>(firstOneFromLSB(this->p(0))); // 最も LSB 側の 1 の bit を 0 にする this->p_[0] &= this->p(0) - 1; return sq; } // Bitboard の left 側だけの要素を調べて、最初に 1 であるマスの index を返す。 // そのマスを 0 にする。 // Bitboard の left 側が 0 でないことを前提にしている。 Square firstOneLeftFromB9() { const Square sq = static_cast<Square>(firstOneFromLSB(this->p(1)) + 63); // 最も LSB 側の 1 の bit を 0 にする this->p_[1] &= this->p(1) - 1; return sq; } // Bitboard を I9 から A1 まで調べて、最初に 1 であるマスの index を返す。 // そのマスを 0 にする。 // Bitboard が Bitboard(0, 0) でないことを前提にしている。 // VC++ の _BitScanForward() は入力が 0 のときに 0 を返す仕様なので、 // 最初に 0 でないか判定するのは少し損。 Square firstOneFromI9() { if(this->p(0)) { return firstOneRightFromI9(); } return firstOneLeftFromB9(); } // 返す位置を 0 にしないバージョン。不要かもしれない。 Square constFirstOneRightFromI9() const { return static_cast<Square>(firstOneFromLSB(this->p(0))); } Square constFirstOneLeftFromB9() const { return static_cast<Square>(firstOneFromLSB(this->p(1)) + 63); } Square constFirstOneFromI9() const { if(this->p(0)) { return constFirstOneRightFromI9(); } return constFirstOneLeftFromB9(); } // bit が 1 つだけ立っているかどうかを判定する。 // bit が 少なくとも 1 つは立っていると仮定する。 bool isOneBit() const { assert(this->isNot0()); if(this->p(0)) { return !((this->p(0) & (this->p(0) - 1)) | this->p(1)); } return !(this->p(1) & (this->p(1) - 1)); } void print() const { printf("%016"PRIx64", %016"PRIx64"\n", this->p(0), this->p(1)); } void printBoard() const; // for debug void printTable(const int part) const { for(Rank r = Rank9; r < RankNum; ++r) { for(File f = FileC; FileI <= f; --f) { printf("%"PRIx64"", UINT64_C(1) & (this->p(part) >> makeSquare(f, r))); } printf("\n"); } printf("\n"); } private: #if defined (HAVE_SSE2) || defined (HAVE_SSE4) union { u64 p_[2]; __m128i m_; }; #else u64 p_[2]; // p_[0] : 先手から見て、1一から7九までを縦に並べたbit. 63bit使用. right 呼ぶ。 // p_[1] : 8一から1九までを縦に並べたbit. 18bit使用. left と呼ぶ。 #endif };
使い方おかしいのかなぁ?もう少し調べます。