mirror of
https://github.com/official-stockfish/Stockfish.git
synced 2026-07-22 12:47:08 +00:00
Constexpr attacks (pext only)
LLR: 2.97 (-2.94,2.94) <0.00,2.00> Total: 38400 W: 10057 L: 9749 D: 18594 Ptnml(0-2): 93, 4110, 10498, 4394, 105 https://tests.stockfishchess.org/tests/view/69d2073261a12cebe17edc04 compile time generation leads to sharing tables through the binary, resulting in gain on fishtest, as well as reduced memory usage. The downside is slightly longer compilation time. Using pdep reduces table size 4x, and gains a little outside of fishtest as well. closes https://github.com/official-stockfish/Stockfish/pull/6706 No functional change
This commit is contained in:
committed by
Joost VandeVondele
parent
ed651aab54
commit
e17725f445
+5
-5
@@ -506,14 +506,14 @@ ifeq ($(COMP),mingw)
|
|||||||
CXX=i686-w64-mingw32-c++-posix
|
CXX=i686-w64-mingw32-c++-posix
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
CXXFLAGS += -pedantic -Wextra -Wshadow -Wmissing-declarations
|
CXXFLAGS += -pedantic -Wextra -Wshadow -Wmissing-declarations -fconstexpr-ops-limit=500000000
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(COMP),icx)
|
ifeq ($(COMP),icx)
|
||||||
comp=icx
|
comp=icx
|
||||||
CXX=icpx
|
CXX=icpx
|
||||||
CXXFLAGS += --intel -pedantic -Wextra -Wshadow -Wmissing-prototypes \
|
CXXFLAGS += --intel -pedantic -Wextra -Wshadow -Wmissing-prototypes \
|
||||||
-Wconditional-uninitialized -Wabi -Wdeprecated
|
-Wconditional-uninitialized -Wabi -Wdeprecated -fconstexpr-steps=500000000
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(COMP),clang)
|
ifeq ($(COMP),clang)
|
||||||
@@ -524,7 +524,7 @@ ifeq ($(COMP),clang)
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
CXXFLAGS += -pedantic -Wextra -Wshadow -Wmissing-prototypes \
|
CXXFLAGS += -pedantic -Wextra -Wshadow -Wmissing-prototypes \
|
||||||
-Wconditional-uninitialized -flax-vector-conversions=none
|
-Wconditional-uninitialized -flax-vector-conversions=none -fconstexpr-steps=500000000
|
||||||
|
|
||||||
ifeq ($(filter $(KERNEL),Darwin OpenBSD FreeBSD),)
|
ifeq ($(filter $(KERNEL),Darwin OpenBSD FreeBSD),)
|
||||||
ifeq ($(target_windows),)
|
ifeq ($(target_windows),)
|
||||||
@@ -632,7 +632,7 @@ ifeq ($(COMP),gcc)
|
|||||||
profile_make = clang-profile-make
|
profile_make = clang-profile-make
|
||||||
profile_use = clang-profile-use
|
profile_use = clang-profile-use
|
||||||
else
|
else
|
||||||
CXXFLAGS += -Wstack-usage=128000 -fno-ipa-cp-clone
|
CXXFLAGS += -Wstack-usage=128000 -fno-ipa-cp-clone -fconstexpr-ops-limit=500000000
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
@@ -838,7 +838,7 @@ endif
|
|||||||
ifeq ($(pext),yes)
|
ifeq ($(pext),yes)
|
||||||
CXXFLAGS += -DUSE_PEXT
|
CXXFLAGS += -DUSE_PEXT
|
||||||
ifeq ($(comp),$(filter $(comp),gcc clang mingw icx))
|
ifeq ($(comp),$(filter $(comp),gcc clang mingw icx))
|
||||||
CXXFLAGS += -mbmi2
|
CXXFLAGS += -mbmi2 -DUSE_COMPTIME_ATTACKS
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|||||||
+96
-50
@@ -35,13 +35,11 @@ Bitboard RayPassBB[SQUARE_NB][SQUARE_NB];
|
|||||||
|
|
||||||
alignas(64) Magic Magics[SQUARE_NB][2];
|
alignas(64) Magic Magics[SQUARE_NB][2];
|
||||||
|
|
||||||
namespace {
|
#ifdef USE_PEXT
|
||||||
|
using MagicMask = uint16_t;
|
||||||
Bitboard RookTable[0x19000]; // To store rook attacks
|
#else
|
||||||
Bitboard BishopTable[0x1480]; // To store bishop attacks
|
using MagicMask = Bitboard;
|
||||||
|
#endif
|
||||||
void init_magics(PieceType pt, Bitboard table[], Magic magics[][2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns an ASCII representation of a bitboard suitable
|
// Returns an ASCII representation of a bitboard suitable
|
||||||
// to be printed to standard output. Useful for debugging.
|
// to be printed to standard output. Useful for debugging.
|
||||||
@@ -64,45 +62,33 @@ std::string Bitboards::pretty(Bitboard b) {
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
// Initializes various bitboard tables. It is called at
|
[[maybe_unused]] constexpr Bitboard constexpr_pext(Bitboard b, Bitboard m) {
|
||||||
// startup and relies on global objects to be already zero-initialized.
|
Bitboard result = 0, bit = 0;
|
||||||
void Bitboards::init() {
|
while (m)
|
||||||
|
|
||||||
for (unsigned i = 0; i < (1 << 16); ++i)
|
|
||||||
PopCnt16[i] = uint8_t(std::bitset<16>(i).count());
|
|
||||||
|
|
||||||
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
|
|
||||||
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
|
|
||||||
SquareDistance[s1][s2] = std::max(distance<File>(s1, s2), distance<Rank>(s1, s2));
|
|
||||||
|
|
||||||
init_magics(ROOK, RookTable, Magics);
|
|
||||||
init_magics(BISHOP, BishopTable, Magics);
|
|
||||||
|
|
||||||
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
|
|
||||||
{
|
{
|
||||||
for (PieceType pt : {BISHOP, ROOK})
|
Bitboard last = m & -m;
|
||||||
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
|
result |= bool(b & last) << bit++;
|
||||||
{
|
m ^= last;
|
||||||
if (PseudoAttacks[pt][s1] & s2)
|
|
||||||
{
|
|
||||||
LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2;
|
|
||||||
BetweenBB[s1][s2] =
|
|
||||||
(attacks_bb(pt, s1, square_bb(s2)) & attacks_bb(pt, s2, square_bb(s1)));
|
|
||||||
RayPassBB[s1][s2] =
|
|
||||||
attacks_bb(pt, s1, 0) & (attacks_bb(pt, s2, square_bb(s1)) | s2);
|
|
||||||
}
|
|
||||||
BetweenBB[s1][s2] |= s2;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
// Computes all rook and bishop attacks at startup or optionally, compile time. Magic
|
||||||
// Computes all rook and bishop attacks at startup. Magic
|
|
||||||
// bitboards are used to look up attacks of sliding pieces. As a reference see
|
// bitboards are used to look up attacks of sliding pieces. As a reference see
|
||||||
// https://www.chessprogramming.org/Magic_Bitboards. In particular, here we use
|
// https://www.chessprogramming.org/Magic_Bitboards. In particular, here we use
|
||||||
// the so called "fancy" approach.
|
// the so called "fancy" approach.
|
||||||
void init_magics(PieceType pt, Bitboard table[], Magic magics[][2]) {
|
#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
|
#ifndef USE_PEXT
|
||||||
// Optimal PRNG seeds to pick the correct magics in the shortest time
|
// Optimal PRNG seeds to pick the correct magics in the shortest time
|
||||||
@@ -111,9 +97,9 @@ void init_magics(PieceType pt, Bitboard table[], Magic magics[][2]) {
|
|||||||
|
|
||||||
Bitboard occupancy[4096];
|
Bitboard occupancy[4096];
|
||||||
int epoch[4096] = {}, cnt = 0;
|
int epoch[4096] = {}, cnt = 0;
|
||||||
|
Bitboard reference[4096] = {};
|
||||||
#endif
|
#endif
|
||||||
Bitboard reference[4096];
|
int size = 0;
|
||||||
int size = 0;
|
|
||||||
|
|
||||||
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
||||||
{
|
{
|
||||||
@@ -125,9 +111,12 @@ void init_magics(PieceType pt, Bitboard table[], Magic magics[][2]) {
|
|||||||
// all the attacks for each possible subset of the mask and so is 2 power
|
// all the attacks for each possible subset of the mask and so is 2 power
|
||||||
// the number of 1s of the mask. Hence we deduce the size of the shift to
|
// the number of 1s of the mask. Hence we deduce the size of the shift to
|
||||||
// apply to the 64 or 32 bits word to get the index.
|
// apply to the 64 or 32 bits word to get the index.
|
||||||
Magic& m = magics[s][pt - BISHOP];
|
Magic& m = magics[s][pt - BISHOP];
|
||||||
m.mask = Bitboards::sliding_attack(pt, s, 0) & ~edges;
|
Bitboard attacks = Bitboards::sliding_attack(pt, s, 0);
|
||||||
#ifndef USE_PEXT
|
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
|
#endif
|
||||||
// Set the offset for the attacks table of the square. We have individual
|
// Set the offset for the attacks table of the square. We have individual
|
||||||
@@ -137,16 +126,22 @@ void init_magics(PieceType pt, Bitboard table[], Magic magics[][2]) {
|
|||||||
|
|
||||||
// Use Carry-Rippler trick to enumerate all subsets of masks[s] and
|
// Use Carry-Rippler trick to enumerate all subsets of masks[s] and
|
||||||
// store the corresponding sliding attack bitboard in reference[].
|
// store the corresponding sliding attack bitboard in reference[].
|
||||||
Bitboard b = 0;
|
Bitboard b = 0;
|
||||||
|
[[maybe_unused]] Bitboard prevSliding = -1;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
#ifndef USE_PEXT
|
#ifdef USE_PEXT
|
||||||
|
if (!tableAlreadyInit)
|
||||||
|
{
|
||||||
|
Bitboard sliding = Bitboards::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;
|
||||||
#endif
|
|
||||||
reference[size] = Bitboards::sliding_attack(pt, s, b);
|
reference[size] = Bitboards::sliding_attack(pt, s, b);
|
||||||
|
#endif
|
||||||
if (HasPext)
|
|
||||||
m.attacks[pext(b, m.mask)] = reference[size];
|
|
||||||
|
|
||||||
size++;
|
size++;
|
||||||
b = (b - m.mask) & m.mask;
|
b = (b - m.mask) & m.mask;
|
||||||
@@ -184,6 +179,57 @@ void init_magics(PieceType pt, Bitboard table[], Magic magics[][2]) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(USE_COMPTIME_ATTACKS) && defined(USE_PEXT)
|
||||||
|
constexpr auto RookTable = []() {
|
||||||
|
std::array<uint16_t, 0x19000> result{};
|
||||||
|
Magic magics[64][2] = {};
|
||||||
|
init_magics(ROOK, result.data(), magics, false);
|
||||||
|
return result;
|
||||||
|
}();
|
||||||
|
constexpr auto BishopTable = []() {
|
||||||
|
std::array<uint16_t, 0x1480> result{};
|
||||||
|
Magic magics[64][2] = {};
|
||||||
|
init_magics(BISHOP, result.data(), magics, false);
|
||||||
|
return result;
|
||||||
|
}();
|
||||||
|
#else
|
||||||
|
std::array<MagicMask, 0x19000> RookTable;
|
||||||
|
std::array<MagicMask, 0x1480> BishopTable;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Initializes various bitboard tables. It is called at
|
||||||
|
// startup and relies on global objects to be already zero-initialized.
|
||||||
|
void Bitboards::init() {
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < (1 << 16); ++i)
|
||||||
|
PopCnt16[i] = uint8_t(std::bitset<16>(i).count());
|
||||||
|
|
||||||
|
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
|
||||||
|
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
|
||||||
|
SquareDistance[s1][s2] = std::max(distance<File>(s1, s2), distance<Rank>(s1, s2));
|
||||||
|
|
||||||
|
init_magics(ROOK, const_cast<MagicMask*>(RookTable.data()), Magics, true);
|
||||||
|
init_magics(BISHOP, const_cast<MagicMask*>(BishopTable.data()), Magics, true);
|
||||||
|
|
||||||
|
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
|
||||||
|
{
|
||||||
|
for (PieceType pt : {BISHOP, ROOK})
|
||||||
|
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
|
||||||
|
{
|
||||||
|
if (PseudoAttacks[pt][s1] & s2)
|
||||||
|
{
|
||||||
|
LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2;
|
||||||
|
BetweenBB[s1][s2] =
|
||||||
|
(attacks_bb(pt, s1, square_bb(s2)) & attacks_bb(pt, s2, square_bb(s1)));
|
||||||
|
RayPassBB[s1][s2] =
|
||||||
|
attacks_bb(pt, s1, 0) & (attacks_bb(pt, s2, square_bb(s1)) | s2);
|
||||||
|
}
|
||||||
|
BetweenBB[s1][s2] |= s2;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Stockfish
|
} // namespace Stockfish
|
||||||
|
|||||||
+21
-11
@@ -76,11 +76,14 @@ extern Bitboard RayPassBB[SQUARE_NB][SQUARE_NB];
|
|||||||
|
|
||||||
// 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
|
||||||
|
uint16_t* attacks;
|
||||||
|
Bitboard pseudoAttacks;
|
||||||
|
#else
|
||||||
Bitboard* attacks;
|
Bitboard* attacks;
|
||||||
#ifndef USE_PEXT
|
Bitboard magic;
|
||||||
Bitboard magic;
|
unsigned shift;
|
||||||
unsigned shift;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Compute the attack's index using the 'magic bitboards' approach
|
// Compute the attack's index using the 'magic bitboards' approach
|
||||||
@@ -98,7 +101,13 @@ struct Magic {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
Bitboard attacks_bb(Bitboard occupied) const { return attacks[index(occupied)]; }
|
Bitboard attacks_bb(Bitboard occupied) const {
|
||||||
|
#ifdef USE_PEXT
|
||||||
|
return pdep(attacks[index(occupied)], pseudoAttacks);
|
||||||
|
#else
|
||||||
|
return attacks[index(occupied)];
|
||||||
|
#endif
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Magic Magics[SQUARE_NB][2];
|
extern Magic Magics[SQUARE_NB][2];
|
||||||
@@ -338,17 +347,18 @@ constexpr Bitboard safe_destination(Square s, int step) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constexpr Bitboard sliding_attack(PieceType pt, Square sq, Bitboard occupied) {
|
constexpr Bitboard sliding_attack(PieceType pt, Square sq, Bitboard occupied) {
|
||||||
Bitboard attacks = 0;
|
Bitboard attacks = 0, dest = 0;
|
||||||
Direction RookDirections[4] = {NORTH, SOUTH, EAST, WEST};
|
constexpr Direction RookDirections[4] = {NORTH, SOUTH, EAST, WEST};
|
||||||
Direction BishopDirections[4] = {NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST};
|
constexpr Direction BishopDirections[4] = {NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST};
|
||||||
|
|
||||||
for (Direction d : (pt == ROOK ? RookDirections : BishopDirections))
|
for (Direction d : (pt == ROOK ? RookDirections : BishopDirections))
|
||||||
{
|
{
|
||||||
Square s = sq;
|
Square s = sq;
|
||||||
while (safe_destination(s, d))
|
while ((dest = safe_destination(s, d)))
|
||||||
{
|
{
|
||||||
attacks |= (s += d);
|
attacks |= dest;
|
||||||
if (occupied & s)
|
s += d;
|
||||||
|
if (occupied & dest)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -87,8 +87,7 @@
|
|||||||
#if defined(USE_PEXT)
|
#if defined(USE_PEXT)
|
||||||
#include <immintrin.h> // Header for _pext_u64() intrinsic
|
#include <immintrin.h> // Header for _pext_u64() intrinsic
|
||||||
#define pext(b, m) _pext_u64(b, m)
|
#define pext(b, m) _pext_u64(b, m)
|
||||||
#else
|
#define pdep(b, m) _pdep_u64(b, m)
|
||||||
#define pext(b, m) 0
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace Stockfish {
|
namespace Stockfish {
|
||||||
|
|||||||
Reference in New Issue
Block a user