mirror of
https://github.com/official-stockfish/Stockfish.git
synced 2026-07-22 12:47:08 +00:00
Simplify out pext attacks
Passed STC non-regression on avx512icl (https://tests.stockfishchess.org/tests/view/6a421b46f97ff95f78795061) LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 81632 W: 21240 L: 21075 D: 39317 Ptnml(0-2): 184, 8907, 22464, 9082, 179 Passed STC non-regression on bmi2 (https://tests.stockfishchess.org/tests/view/6a41f6f9f97ff95f7879503e) LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 78112 W: 20411 L: 20245 D: 37456 Ptnml(0-2): 188, 8449, 21632, 8583, 204 After #6845 , pext attacks are slower or equal to parallel-hyperbola attacks which works on all architectures that pext supports. SVE2 supports pext/pdep in vector registers, but it's much slower than the `rbit`-based HQ that we have on ARM, so I don't foresee the code being useful there. Ofc I don't think we should remove the `bmi2` build or anything, because it's nice for people to be able to freely use pext when testing ideas, and I'm sure we'll some day find another use for it! closes https://github.com/official-stockfish/Stockfish/pull/6939 No functional change Co-authored-by: Dubslow <bunslow@gmail.com>
This commit is contained in:
committed by
Joost VandeVondele
co-authored by
Dubslow
parent
46428b9346
commit
99489f57dd
+7
-65
@@ -38,12 +38,6 @@ alignas(64) Magic Magics[SQUARE_NB][2];
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_PEXT
|
|
||||||
using MagicMask = u16;
|
|
||||||
#else
|
|
||||||
using MagicMask = Bitboard;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
[[maybe_unused]] static Bitboard line_mask(Square sq, Direction d1, Direction d2) {
|
[[maybe_unused]] static Bitboard line_mask(Square sq, Direction d1, Direction d2) {
|
||||||
Bitboard mask = 0, dest;
|
Bitboard mask = 0, dest;
|
||||||
for (Direction d : {d1, d2})
|
for (Direction d : {d1, d2})
|
||||||
@@ -76,7 +70,7 @@ static void init_magics(Magic magics[][2]) {
|
|||||||
|
|
||||||
// Sliding attacks within a rank, indexed by the slider's file and the
|
// Sliding attacks within a rank, indexed by the slider's file and the
|
||||||
// 8-bit rank occupancy, yielding the 8-bit attack set on that rank
|
// 8-bit rank occupancy, yielding the 8-bit attack set on that rank
|
||||||
constexpr auto RankAttacks = []() {
|
[[maybe_unused]] constexpr auto RankAttacks = []() {
|
||||||
std::array<std::array<u8, 256>, FILE_NB> table{};
|
std::array<std::array<u8, 256>, FILE_NB> table{};
|
||||||
for (int file = 0; file < 8; ++file)
|
for (int file = 0; file < 8; ++file)
|
||||||
for (int occ = 0; occ < 256; ++occ)
|
for (int occ = 0; occ < 256; ++occ)
|
||||||
@@ -102,37 +96,14 @@ static void init_dual_magics(DualMagic magics[]) {
|
|||||||
#else
|
#else
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
[[maybe_unused]] constexpr Bitboard constexpr_pext(Bitboard b, Bitboard m) {
|
void init_magics(PieceType pt, Bitboard table[], Magic magics[][2]) {
|
||||||
Bitboard result = 0, bit = 0;
|
|
||||||
while (m)
|
|
||||||
{
|
|
||||||
Bitboard last = m & -m;
|
|
||||||
result |= bool(b & last) << bit++;
|
|
||||||
m ^= last;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef USE_COMPTIME_ATTACKS
|
|
||||||
constexpr
|
|
||||||
#endif
|
|
||||||
void
|
|
||||||
init_magics(PieceType pt,
|
|
||||||
MagicMask table[],
|
|
||||||
Magic magics[][2],
|
|
||||||
[[maybe_unused]] bool tableAlreadyInit) {
|
|
||||||
#if !defined(USE_COMPTIME_ATTACKS)
|
|
||||||
tableAlreadyInit = false;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef USE_PEXT
|
|
||||||
int seeds[][RANK_NB] = {{8977, 44560, 54343, 38998, 5731, 95205, 104912, 17020},
|
int seeds[][RANK_NB] = {{8977, 44560, 54343, 38998, 5731, 95205, 104912, 17020},
|
||||||
{728, 10316, 55013, 32803, 12281, 15100, 16645, 255}};
|
{728, 10316, 55013, 32803, 12281, 15100, 16645, 255}};
|
||||||
|
|
||||||
Bitboard occupancy[4096];
|
Bitboard occupancy[4096];
|
||||||
int epoch[4096] = {}, cnt = 0;
|
int epoch[4096] = {}, cnt = 0;
|
||||||
Bitboard reference[4096] = {};
|
Bitboard reference[4096] = {};
|
||||||
#endif
|
|
||||||
int size = 0;
|
int size = 0;
|
||||||
|
|
||||||
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
||||||
@@ -142,11 +113,7 @@ constexpr
|
|||||||
Magic& m = magics[s][pt - BISHOP];
|
Magic& m = magics[s][pt - BISHOP];
|
||||||
Bitboard attacks = sliding_attack(pt, s, 0);
|
Bitboard attacks = sliding_attack(pt, s, 0);
|
||||||
m.mask = attacks & ~edges;
|
m.mask = attacks & ~edges;
|
||||||
#ifdef USE_PEXT
|
|
||||||
m.pseudoAttacks = attacks;
|
|
||||||
#else
|
|
||||||
m.shift = (Is64Bit ? 64 : 32) - popcount(m.mask);
|
m.shift = (Is64Bit ? 64 : 32) - popcount(m.mask);
|
||||||
#endif
|
|
||||||
m.attacks = s == SQ_A1 ? table : magics[s - 1][pt - BISHOP].attacks + size;
|
m.attacks = s == SQ_A1 ? table : magics[s - 1][pt - BISHOP].attacks + size;
|
||||||
size = 0;
|
size = 0;
|
||||||
|
|
||||||
@@ -154,24 +121,13 @@ constexpr
|
|||||||
[[maybe_unused]] Bitboard prevSliding = -1;
|
[[maybe_unused]] Bitboard prevSliding = -1;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
#ifdef USE_PEXT
|
|
||||||
if (!tableAlreadyInit)
|
|
||||||
{
|
|
||||||
Bitboard sliding = sliding_attack(pt, s, b);
|
|
||||||
m.attacks[size] =
|
|
||||||
sliding != prevSliding ? constexpr_pext(sliding, attacks) : m.attacks[size - 1];
|
|
||||||
prevSliding = sliding;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
occupancy[size] = b;
|
occupancy[size] = b;
|
||||||
reference[size] = sliding_attack(pt, s, b);
|
reference[size] = sliding_attack(pt, s, b);
|
||||||
#endif
|
|
||||||
|
|
||||||
size++;
|
size++;
|
||||||
b = (b - m.mask) & m.mask;
|
b = (b - m.mask) & m.mask;
|
||||||
} while (b);
|
} while (b);
|
||||||
|
|
||||||
#ifndef USE_PEXT
|
|
||||||
PRNG rng(seeds[Is64Bit][rank_of(s)]);
|
PRNG rng(seeds[Is64Bit][rank_of(s)]);
|
||||||
|
|
||||||
for (int i = 0; i < size;)
|
for (int i = 0; i < size;)
|
||||||
@@ -192,26 +148,12 @@ constexpr
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(USE_COMPTIME_ATTACKS) && defined(USE_PEXT)
|
#if !defined(USE_DUAL_HYPERBOLA_QUINT) && !defined(USE_HYPERBOLA_QUINT)
|
||||||
constexpr auto RookTable = []() {
|
static std::array<Bitboard, 0x19000> RookTable;
|
||||||
std::array<u16, 0x19000> result{};
|
static std::array<Bitboard, 0x1480> BishopTable;
|
||||||
Magic magics[64][2] = {};
|
|
||||||
init_magics(ROOK, result.data(), magics, false);
|
|
||||||
return result;
|
|
||||||
}();
|
|
||||||
constexpr auto BishopTable = []() {
|
|
||||||
std::array<u16, 0x1480> result{};
|
|
||||||
Magic magics[64][2] = {};
|
|
||||||
init_magics(BISHOP, result.data(), magics, false);
|
|
||||||
return result;
|
|
||||||
}();
|
|
||||||
#elif !defined(USE_DUAL_HYPERBOLA_QUINT) && !defined(USE_HYPERBOLA_QUINT)
|
|
||||||
std::array<MagicMask, 0x19000> RookTable;
|
|
||||||
std::array<MagicMask, 0x1480> BishopTable;
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,8 +166,8 @@ void init() {
|
|||||||
#elif defined(USE_DUAL_HYPERBOLA_QUINT)
|
#elif defined(USE_DUAL_HYPERBOLA_QUINT)
|
||||||
init_dual_magics(DualMagics);
|
init_dual_magics(DualMagics);
|
||||||
#else
|
#else
|
||||||
init_magics(ROOK, const_cast<MagicMask*>(RookTable.data()), Magics, true);
|
init_magics(ROOK, RookTable.data(), Magics);
|
||||||
init_magics(BISHOP, const_cast<MagicMask*>(BishopTable.data()), Magics, true);
|
init_magics(BISHOP, BishopTable.data(), Magics);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
|
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
|
||||||
|
|||||||
+2
-16
@@ -31,7 +31,7 @@
|
|||||||
#define USE_HYPERBOLA_QUINT
|
#define USE_HYPERBOLA_QUINT
|
||||||
#elif defined(__loongarch__) && __loongarch_grlen == 64
|
#elif defined(__loongarch__) && __loongarch_grlen == 64
|
||||||
#define USE_HYPERBOLA_QUINT
|
#define USE_HYPERBOLA_QUINT
|
||||||
#elif defined(USE_AVX2) && !defined(USE_PEXT)
|
#elif defined(USE_AVX2)
|
||||||
#include <immintrin.h>
|
#include <immintrin.h>
|
||||||
#define USE_DUAL_HYPERBOLA_QUINT
|
#define USE_DUAL_HYPERBOLA_QUINT
|
||||||
#endif
|
#endif
|
||||||
@@ -75,7 +75,7 @@ const Magic& magic(Square s, PieceType pt);
|
|||||||
|
|
||||||
#elif defined(USE_DUAL_HYPERBOLA_QUINT)
|
#elif defined(USE_DUAL_HYPERBOLA_QUINT)
|
||||||
|
|
||||||
struct DualMagic {
|
struct alignas(32) DualMagic {
|
||||||
// file, diagonal, unused, antidiagonal
|
// file, diagonal, unused, antidiagonal
|
||||||
Bitboard maskFile, maskDiag, maskNone, maskAntidiag;
|
Bitboard maskFile, maskDiag, maskNone, maskAntidiag;
|
||||||
// Precomputed 2 * square_bb(sq), 2 * reverse(square_bb(sq))
|
// Precomputed 2 * square_bb(sq), 2 * reverse(square_bb(sq))
|
||||||
@@ -129,36 +129,22 @@ const DualMagic& dual_magic(Square s);
|
|||||||
// Magic holds all magic bitboards relevant data for a single square
|
// Magic holds all magic bitboards relevant data for a single square
|
||||||
struct Magic {
|
struct Magic {
|
||||||
Bitboard mask;
|
Bitboard mask;
|
||||||
#ifdef USE_PEXT
|
|
||||||
u16* attacks;
|
|
||||||
Bitboard pseudoAttacks;
|
|
||||||
#else
|
|
||||||
Bitboard* attacks;
|
Bitboard* attacks;
|
||||||
Bitboard magic;
|
Bitboard magic;
|
||||||
unsigned shift;
|
unsigned shift;
|
||||||
#endif
|
|
||||||
|
|
||||||
// Compute the attack's index using the 'magic bitboards' approach
|
// Compute the attack's index using the 'magic bitboards' approach
|
||||||
unsigned index(Bitboard occupied) const {
|
unsigned index(Bitboard occupied) const {
|
||||||
|
|
||||||
#ifdef USE_PEXT
|
|
||||||
return unsigned(pext(occupied, mask));
|
|
||||||
#else
|
|
||||||
if (Is64Bit)
|
if (Is64Bit)
|
||||||
return unsigned(((occupied & mask) * magic) >> shift);
|
return unsigned(((occupied & mask) * magic) >> shift);
|
||||||
|
|
||||||
unsigned lo = unsigned(occupied) & unsigned(mask);
|
unsigned lo = unsigned(occupied) & unsigned(mask);
|
||||||
unsigned hi = unsigned(occupied >> 32) & unsigned(mask >> 32);
|
unsigned hi = unsigned(occupied >> 32) & unsigned(mask >> 32);
|
||||||
return (lo * unsigned(magic) ^ hi * unsigned(magic >> 32)) >> shift;
|
return (lo * unsigned(magic) ^ hi * unsigned(magic >> 32)) >> shift;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Bitboard attacks_bb([[maybe_unused]] Square s, Bitboard occupied) const {
|
Bitboard attacks_bb([[maybe_unused]] Square s, Bitboard occupied) const {
|
||||||
#ifdef USE_PEXT
|
|
||||||
return pdep(attacks[index(occupied)], pseudoAttacks);
|
|
||||||
#else
|
|
||||||
return attacks[index(occupied)];
|
return attacks[index(occupied)];
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+2
-83
@@ -63,76 +63,6 @@ inline Move* splat_moves(Move* moveList, Square from, Bitboard to_bb) {
|
|||||||
return moveList + popcount(to_bb);
|
return moveList + popcount(to_bb);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rook/bishop, indexed by (Pt - BISHOP) and from sq
|
|
||||||
// Moves are provided in ascending order of the piece's attacks on an empty board
|
|
||||||
alignas(64) constexpr auto SliderMoves = []() {
|
|
||||||
std::array<std::array<std::array<Move, 16>, SQUARE_NB>, 2> arr{};
|
|
||||||
for (PieceType pt : {BISHOP, ROOK})
|
|
||||||
{
|
|
||||||
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
|
||||||
{
|
|
||||||
Bitboard bb = Attacks::PseudoAttacks[pt][s];
|
|
||||||
int i = 0;
|
|
||||||
while (bb)
|
|
||||||
{
|
|
||||||
arr[pt - BISHOP][s][i++] = Move(s, Square(constexpr_lsb(bb)));
|
|
||||||
bb &= bb - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return arr;
|
|
||||||
}();
|
|
||||||
|
|
||||||
// Knight/king analog of the above
|
|
||||||
alignas(64) constexpr auto KnightKingMoves = []() {
|
|
||||||
std::array<std::array<std::array<Move, 8>, SQUARE_NB>, 2> arr{};
|
|
||||||
for (PieceType pt : {KNIGHT, KING})
|
|
||||||
{
|
|
||||||
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
|
||||||
{
|
|
||||||
Bitboard bb = Attacks::PseudoAttacks[pt][s];
|
|
||||||
int i = 0;
|
|
||||||
while (bb)
|
|
||||||
{
|
|
||||||
arr[pt == KING][s][i++] = Move(s, Square(constexpr_lsb(bb)));
|
|
||||||
bb &= bb - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return arr;
|
|
||||||
}();
|
|
||||||
|
|
||||||
template<PieceType Pt>
|
|
||||||
inline Move*
|
|
||||||
splat_precomputed_moves(Move* moveList, Square from, Bitboard occupied, Bitboard target) {
|
|
||||||
static_assert(Pt != QUEEN && Pt != PAWN, "Unsupported piece type");
|
|
||||||
|
|
||||||
// The nth bit in the mask corresponds to the nth square in the piece's pseudo-attacks
|
|
||||||
u32 mask;
|
|
||||||
if constexpr (Pt == BISHOP || Pt == ROOK)
|
|
||||||
{
|
|
||||||
const Attacks::Magic& magic = Attacks::magic(from, Pt);
|
|
||||||
|
|
||||||
mask = magic.attacks[magic.index(occupied)];
|
|
||||||
mask &= pext(target, magic.pseudoAttacks);
|
|
||||||
|
|
||||||
const __m256i moves =
|
|
||||||
*reinterpret_cast<const __m256i*>(SliderMoves[Pt - BISHOP][from].data());
|
|
||||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(moveList),
|
|
||||||
_mm256_maskz_compress_epi16(mask, moves));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mask = pext(target, Attacks::PseudoAttacks[Pt][from]);
|
|
||||||
|
|
||||||
__m128i moves = *reinterpret_cast<const __m128i*>(KnightKingMoves[Pt == KING][from].data());
|
|
||||||
_mm_storeu_si128(reinterpret_cast<__m128i*>(moveList),
|
|
||||||
_mm_maskz_compress_epi16(mask, moves));
|
|
||||||
}
|
|
||||||
|
|
||||||
return moveList + popcount(mask);
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
template<Direction offset>
|
template<Direction offset>
|
||||||
@@ -264,13 +194,6 @@ Move* generate_moves(const Position& pos, Move* moveList, Bitboard target) {
|
|||||||
while (bb)
|
while (bb)
|
||||||
{
|
{
|
||||||
Square from = pop_lsb(bb);
|
Square from = pop_lsb(bb);
|
||||||
#ifdef USE_AVX512ICL
|
|
||||||
if constexpr (Pt != QUEEN)
|
|
||||||
{
|
|
||||||
moveList = splat_precomputed_moves<Pt>(moveList, from, pos.pieces(), target);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
Bitboard b = Attacks::attacks_bb<Pt>(from, pos.pieces()) & target;
|
Bitboard b = Attacks::attacks_bb<Pt>(from, pos.pieces()) & target;
|
||||||
|
|
||||||
moveList = splat_moves(moveList, from, b);
|
moveList = splat_moves(moveList, from, b);
|
||||||
@@ -303,13 +226,9 @@ Move* generate_all(const Position& pos, Move* moveList) {
|
|||||||
moveList = generate_moves<Us, QUEEN>(pos, moveList, target);
|
moveList = generate_moves<Us, QUEEN>(pos, moveList, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
Bitboard b = Type == EVASIONS ? ~pos.pieces(Us) : target;
|
Bitboard b = Attacks::attacks_bb<KING>(ksq) & (Type == EVASIONS ? ~pos.pieces(Us) : target);
|
||||||
|
|
||||||
#ifdef USE_AVX512ICL
|
moveList = splat_moves(moveList, ksq, b);
|
||||||
moveList = splat_precomputed_moves<KING>(moveList, ksq, 0ULL, b);
|
|
||||||
#else
|
|
||||||
moveList = splat_moves(moveList, ksq, Attacks::attacks_bb<KING>(ksq) & b);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if ((Type == QUIETS || Type == NON_EVASIONS) && pos.can_castle(Us & ANY_CASTLING))
|
if ((Type == QUIETS || Type == NON_EVASIONS) && pos.can_castle(Us & ANY_CASTLING))
|
||||||
for (CastlingRights cr : {Us & KING_SIDE, Us & QUEEN_SIDE})
|
for (CastlingRights cr : {Us & KING_SIDE, Us & QUEEN_SIDE})
|
||||||
|
|||||||
Reference in New Issue
Block a user