Compare commits

...
4 Commits
Author SHA1 Message Date
f4bcd40409 Update nnue architecture to SFNNv16 and net nn-89cb98a217f7.nnue
Failed STC (https://tests.stockfishchess.org/tests/view/6a4f36355529b8472df7ff7e):
LLR: -2.94 (-2.94,2.94) <0.00,2.00>
Total: 144768 W: 38003 L: 38051 D: 68714
Ptnml(0-2): 521, 17154, 37050, 17170, 489

Passed LTC (https://tests.stockfishchess.org/tests/view/6a501dad5529b8472df80103):
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 189888 W: 49613 L: 48984 D: 91291
Ptnml(0-2): 122, 20406, 53256, 21041, 119

Passed VLTC (https://tests.stockfishchess.org/tests/view/6a52892c5529b8472df80481):
LLR: 2.94 (-2.94,2.94) <0.00,2.00>
Total: 46124 W: 12156 L: 11865 D: 22103
Ptnml(0-2): 12, 4523, 13696, 4824, 7

Passed VVLTC (https://tests.stockfishchess.org/tests/view/6a566a285529b8472df80b84):
LLR: 2.94 (-2.94,2.94) <0.50,2.50>
Total: 44698 W: 11805 L: 11507 D: 21386
Ptnml(0-2): 9, 3819, 14395, 4117, 9

This PR adds pawn pair features, a new set of features that is indexed by pairs of pawns occupying the same or adjacent files (hence "3-wide"). Because these are a superset of pawn–pawn interactions in threat inputs (including the recently added pawn-pusher inputs), those are removed. HalfKA features remain unchanged.

Pawn-pair features were invented by Jonathan for Pawnocchio, based on his observation that in a net trained on *all* pairs of pawns, the most important pawn pairs were those differing by at most one file.

sscg13 made the necessary changes to the trainer (https://github.com/official-stockfish/nnue-pytorch/pull/502) and initial changes to inference. The slowdown was originally far too large (some 10%); following major optimizations from Jonathan and a bit from me, the slowdown is small enough for a comfortable LTC pass; on my machine I get a 3.5% slowdown overall. I expect there are further "easy" speedups in the new code, so this gap should narrow a bit.

Nettest PR: https://github.com/vondele/nettest/pull/359

closes https://github.com/official-stockfish/Stockfish/pull/6982

Bench: 2718396

Co-Authored-By: anematode <timothy.herchen@gmail.com>
Co-Authored-By: Jonathan Hallström <lmj.hallstrom@gmail.com>
2026-07-20 11:06:41 +02:00
Syine MinetaandJoost VandeVondele 4d75fd4f70 Always use pthread
After a858defd33, the pthread implementation of `NativeThread` is always used (except MSVC), as:

1. `-DUSE_PTHREADS` is defined when `comp` is not `mingw`
2. the pthread implementation is used anyways because MinGW defines `__MINGW32__` or `__MINGW64__` macro

closes https://github.com/official-stockfish/Stockfish/pull/6985

No functional change
2026-07-20 11:01:40 +02:00
mstemberaandJoost VandeVondele d70dec7d6f Optimize attacks
STC:  https://tests.stockfishchess.org/tests/view/6a57f12c5529b8472df80e2a
LLR: 2.95 (-2.94,2.94) <0.00,2.00>
Total: 85856 W: 22400 L: 22037 D: 41419
Ptnml(0-2): 153, 8999, 24290, 9304, 182

closes https://github.com/official-stockfish/Stockfish/pull/6983

No functional change
2026-07-20 10:59:45 +02:00
anematodeandJoost VandeVondele b4ea9205a6 Allow trailing whitespace in str_to_size_t
Fixes #6984, first reported by sqrmax on the Discord, causing incorrect detection of NUMA and L3 domains on Linux due to trailing whitespace when reading from sysfs.

This was introduced by #6844 which made `str_to_size_t` too strict, since `std::stoull` allows trailing characters (and leading whitespace, although this part is not relevant for us).

Before:
$ ./bins/stockfish.master speedtest
Stockfish dev-20260716-ebcea3ef by the Stockfish developers (see AUTHORS file)
info string Using 224 threads with NUMA node thread binding: 64/32:64/32:64/32:32/16

After:
$ ./stockfish speedtest
Stockfish dev-20260718-m-cb702902 by the Stockfish developers (see AUTHORS file)
info string Using 224 threads with NUMA node thread binding: 32/32:32/32:32/32:32/32:32/32:32/32:32/32

closes https://github.com/official-stockfish/Stockfish/pull/6989

No functional change
2026-07-20 10:58:54 +02:00
21 changed files with 542 additions and 221 deletions
+22 -26
View File
@@ -80,17 +80,17 @@ SRCS = attacks.cpp benchmark.cpp bitboard.cpp evaluate.cpp main.cpp \
misc.cpp movegen.cpp movepick.cpp position.cpp \ misc.cpp movegen.cpp movepick.cpp position.cpp \
search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp \ search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp \
nnue/nnue_accumulator.cpp nnue/nnue_misc.cpp nnue/network.cpp \ nnue/nnue_accumulator.cpp nnue/nnue_misc.cpp nnue/network.cpp \
nnue/features/half_ka_v2_hm.cpp nnue/features/full_threats.cpp \ nnue/features/half_ka_v2_hm.cpp nnue/features/full_threats.cpp nnue/features/pp_3wide.cpp \
engine.cpp score.cpp memory.cpp engine.cpp score.cpp memory.cpp
OTHER_SRCS = universal/entry_x86.cpp universal/entry_arm64.cpp universal/entry_riscv64.cpp universal/nnue_embed.cpp OTHER_SRCS = universal/entry_x86.cpp universal/entry_arm64.cpp universal/entry_riscv64.cpp universal/nnue_embed.cpp
HEADERS = attacks.h benchmark.h bitboard.h evaluate.h misc.h movegen.h movepick.h history.h \ HEADERS = attacks.h benchmark.h bitboard.h evaluate.h misc.h movegen.h movepick.h history.h \
nnue/nnue_misc.h nnue/features/half_ka_v2_hm.h nnue/features/full_threats.h \ nnue/nnue_misc.h nnue/features/half_ka_v2_hm.h nnue/features/full_threats.h \
nnue/layers/affine_transform.h nnue/layers/affine_transform_sparse_input.h \ nnue/features/pp_3wide.h nnue/layers/affine_transform.h nnue/layers/affine_transform_sparse_input.h \
nnue/layers/clipped_relu.h nnue/layers/sqr_clipped_relu.h nnue/nnue_accumulator.h \ nnue/layers/clipped_relu.h nnue/layers/sqr_clipped_relu.h nnue/nnue_accumulator.h \
nnue/nnue_architecture.h nnue/nnue_common.h nnue/nnue_feature_transformer.h nnue/simd.h \ nnue/nnue_architecture.h nnue/nnue_common.h nnue/nnue_feature_transformer.h nnue/simd.h \
nnue/nnz_helper.h position.h search.h syzygy/tbprobe.h thread.h thread_win32_osx.h timeman.h \ nnue/nnz_helper.h position.h search.h syzygy/tbprobe.h thread.h thread_native.h timeman.h \
tt.h tune.h types.h uci.h ucioption.h perft.h nnue/network.h engine.h score.h numa.h memory.h shm.h shm_linux.h tt.h tune.h types.h uci.h ucioption.h perft.h nnue/network.h engine.h score.h numa.h memory.h shm.h shm_linux.h
OBJS = $(notdir $(SRCS:.cpp=.o)) OBJS = $(notdir $(SRCS:.cpp=.o))
@@ -685,31 +685,27 @@ else ifeq ($(COMP),mingw)
CXXFLAGS += -fno-ipa-cp-clone CXXFLAGS += -fno-ipa-cp-clone
endif endif
### On mingw use Windows threads, otherwise POSIX # On Android Bionic's C library comes with its own pthread implementation bundled in
ifneq ($(comp),mingw) ifneq ($(OS),Android)
CXXFLAGS += -DUSE_PTHREADS # Haiku has pthreads in its libroot, so only link it in on other platforms
# On Android Bionic's C library comes with its own pthread implementation bundled in ifneq ($(KERNEL),Haiku)
ifneq ($(OS),Android) ifneq ($(COMP),ndk)
# Haiku has pthreads in its libroot, so only link it in on other platforms ifneq ($(arch),wasm32)
ifneq ($(KERNEL),Haiku) LDFLAGS += -lpthread
ifneq ($(COMP),ndk)
ifneq ($(arch),wasm32)
LDFLAGS += -lpthread
add_lrt = yes add_lrt = yes
ifeq ($(target_windows),yes) ifeq ($(target_windows),yes)
add_lrt = no add_lrt = no
endif
ifeq ($(TARGET_KERNEL),Darwin)
add_lrt = no
endif
ifeq ($(add_lrt),yes)
LDFLAGS += -lrt
endif
endif endif
ifeq ($(TARGET_KERNEL),Darwin)
add_lrt = no
endif endif
ifeq ($(add_lrt),yes)
LDFLAGS += -lrt
endif
endif
endif endif
endif endif
endif endif
@@ -903,7 +899,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 -DUSE_COMPTIME_ATTACKS CXXFLAGS += -mbmi2
endif endif
endif endif
+7 -22
View File
@@ -24,15 +24,17 @@
namespace Stockfish::Attacks { namespace Stockfish::Attacks {
namespace { #ifdef USE_DUAL_HYPERBOLA_QUINT
alignas(64) DualMagic DualMagics[SQUARE_NB];
#endif
Bitboard LineBB[SQUARE_NB][SQUARE_NB]; Bitboard LineBB[SQUARE_NB][SQUARE_NB];
Bitboard BetweenBB[SQUARE_NB][SQUARE_NB]; Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
Bitboard RayPassBB[SQUARE_NB][SQUARE_NB]; Bitboard RayPassBB[SQUARE_NB][SQUARE_NB];
#ifdef USE_DUAL_HYPERBOLA_QUINT namespace {
alignas(64) DualMagic DualMagics[SQUARE_NB];
#else #ifndef USE_DUAL_HYPERBOLA_QUINT
alignas(64) Magic Magics[SQUARE_NB][2]; alignas(64) Magic Magics[SQUARE_NB][2];
#endif #endif
@@ -187,28 +189,11 @@ void init() {
} }
} }
#ifdef USE_DUAL_HYPERBOLA_QUINT #ifndef USE_DUAL_HYPERBOLA_QUINT
const DualMagic& dual_magic(Square s) { return DualMagics[s]; }
#else
const Magic& magic(Square s, PieceType pt) { const Magic& magic(Square s, PieceType pt) {
assert((pt == BISHOP || pt == ROOK) && is_ok(s)); assert((pt == BISHOP || pt == ROOK) && is_ok(s));
return Magics[s][pt - BISHOP]; return Magics[s][pt - BISHOP];
} }
#endif #endif
Bitboard line_bb(Square s1, Square s2) {
assert(is_ok(s1) && is_ok(s2));
return LineBB[s1][s2];
}
Bitboard between_bb(Square s1, Square s2) {
assert(is_ok(s1) && is_ok(s2));
return BetweenBB[s1][s2];
}
Bitboard ray_pass_bb(Square s1, Square s2) {
assert(is_ok(s1) && is_ok(s2));
return RayPassBB[s1][s2];
}
} // namespace Stockfish::Attacks } // namespace Stockfish::Attacks
+30 -4
View File
@@ -22,6 +22,7 @@
#include <cassert> #include <cassert>
#include <array> #include <array>
#include <initializer_list> #include <initializer_list>
#include <utility>
#include "types.h" #include "types.h"
#include "bitboard.h" #include "bitboard.h"
@@ -135,7 +136,9 @@ struct alignas(32) DualMagic {
} }
}; };
const DualMagic& dual_magic(Square s); extern DualMagic DualMagics[SQUARE_NB];
inline const DualMagic& dual_magic(Square s) { return DualMagics[s]; }
#else #else
// Magic holds all magic bitboards relevant data for a single square // Magic holds all magic bitboards relevant data for a single square
@@ -164,9 +167,24 @@ const Magic& magic(Square s, PieceType pt);
#endif #endif
Bitboard line_bb(Square s1, Square s2); extern Bitboard LineBB[SQUARE_NB][SQUARE_NB];
Bitboard between_bb(Square s1, Square s2); extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
Bitboard ray_pass_bb(Square s1, Square s2); extern Bitboard RayPassBB[SQUARE_NB][SQUARE_NB];
inline Bitboard line_bb(Square s1, Square s2) {
assert(is_ok(s1) && is_ok(s2));
return LineBB[s1][s2];
}
inline Bitboard between_bb(Square s1, Square s2) {
assert(is_ok(s1) && is_ok(s2));
return BetweenBB[s1][s2];
}
inline Bitboard ray_pass_bb(Square s1, Square s2) {
assert(is_ok(s1) && is_ok(s2));
return RayPassBB[s1][s2];
}
// Returns the bitboard of target square for the given step // Returns the bitboard of target square for the given step
// from the given square. If the step is off the board, returns empty bitboard. // from the given square. If the step is off the board, returns empty bitboard.
@@ -304,6 +322,14 @@ inline Bitboard attacks_bb(Square s, Bitboard occupied) {
#endif #endif
} }
inline std::pair<Bitboard, Bitboard> both_attacks_bb(Square s, Bitboard occupied) {
#ifdef USE_DUAL_HYPERBOLA_QUINT
return dual_magic(s).both_attacks_bb(occupied);
#else
return {attacks_bb<BISHOP>(s, occupied), attacks_bb<ROOK>(s, occupied)};
#endif
}
// Returns the attacks by the given piece // Returns the attacks by the given piece
// assuming the board is occupied according to the passed Bitboard. // assuming the board is occupied according to the passed Bitboard.
// Sliding piece attacks do not continue past an occupied square. // Sliding piece attacks do not continue past an occupied square.
+17 -1
View File
@@ -20,6 +20,7 @@
#define BITBOARD_H_INCLUDED #define BITBOARD_H_INCLUDED
#include <algorithm> #include <algorithm>
#include <array>
#include <cassert> #include <cassert>
#include <string> #include <string>
#include <type_traits> #include <type_traits>
@@ -127,8 +128,23 @@ constexpr Bitboard pawn_single_push_bb(Color c, Bitboard b) {
return c == WHITE ? shift<NORTH>(b) : shift<SOUTH>(b); return c == WHITE ? shift<NORTH>(b) : shift<SOUTH>(b);
} }
constexpr int edge_distance(File f) { return std::min(f, File(FILE_H - f)); } inline constexpr auto PawnPairBB = []() {
std::array<Bitboard, SQUARE_NB> result{};
for (Square s = SQ_A1; s <= SQ_H8; ++s)
{
Bitboard file = file_bb(s);
Bitboard files = file | shift<EAST>(file) | shift<WEST>(file);
result[s] = files & ~(Rank1BB | Rank8BB) & ~square_bb(s);
}
return result;
}();
// Returns the squares that can host a pawn forming a "pawn pair" with a pawn
// on s: own file plus adjacent files, restricted to ranks 2-7, excluding s.
// The geometry is color-independent.
constexpr Bitboard pawn_pair_bb(Square s) { return PawnPairBB[s]; }
constexpr int edge_distance(File f) { return std::min(f, File(FILE_H - f)); }
template<typename T> template<typename T>
constexpr int constexpr_popcount(T v) { constexpr int constexpr_popcount(T v) {
+1 -1
View File
@@ -33,7 +33,7 @@ namespace Eval {
// for the build process (profile-build and fishtest) to work. Do not change the // for the build process (profile-build and fishtest) to work. Do not change the
// name of the macro or the location where this macro is defined, as it is used // name of the macro or the location where this macro is defined, as it is used
// in the Makefile/Fishtest. // in the Makefile/Fishtest.
#define EvalFileDefaultName "nn-0ee0657fb25e.nnue" #define EvalFileDefaultName "nn-89cb98a217f7.nnue"
namespace NNUE { namespace NNUE {
class Network; class Network;
+2 -1
View File
@@ -554,7 +554,8 @@ std::optional<usize> str_to_size_t(const std::string& s) {
errno = 0; errno = 0;
char* endptr = nullptr; char* endptr = nullptr;
const unsigned long long value = std::strtoull(s.c_str(), &endptr, 10); const unsigned long long value = std::strtoull(s.c_str(), &endptr, 10);
if (errno == ERANGE || *endptr != '\0' || value > std::numeric_limits<usize>::max()) if (errno == ERANGE || (*endptr != '\0' && !std::isspace(*endptr))
|| value > std::numeric_limits<usize>::max())
return std::nullopt; return std::nullopt;
return static_cast<usize>(value); return static_cast<usize>(value);
} }
+12 -21
View File
@@ -21,7 +21,6 @@
#include "full_threats.h" #include "full_threats.h"
#include <array> #include <array>
#include <cassert>
#include <cstdint> #include <cstdint>
#include <initializer_list> #include <initializer_list>
#include <utility> #include <utility>
@@ -73,7 +72,7 @@ constexpr auto make_piece_indices_piece() {
for (Square from = SQ_A1; from <= SQ_H8; ++from) for (Square from = SQ_A1; from <= SQ_H8; ++from)
{ {
Bitboard attacks = Attacks::PawnPushOrAttacks[C][from]; Bitboard attacks = Attacks::PseudoAttacks[C][from];
for (Square to = SQ_A1; to <= SQ_H8; ++to) for (Square to = SQ_A1; to <= SQ_H8; ++to)
{ {
@@ -136,8 +135,8 @@ constexpr auto init_threat_offsets() {
else if (from >= SQ_A2 && from <= SQ_H7) else if (from >= SQ_A2 && from <= SQ_H7)
{ {
Bitboard attacks = (pieceIdx < 8) ? Attacks::PawnPushOrAttacks[WHITE][from] Bitboard attacks = (pieceIdx < 8) ? Attacks::PseudoAttacks[WHITE][from]
: Attacks::PawnPushOrAttacks[BLACK][from]; : Attacks::PseudoAttacks[BLACK][from];
cumulativePieceOffset += constexpr_popcount(attacks); cumulativePieceOffset += constexpr_popcount(attacks);
} }
} }
@@ -208,11 +207,9 @@ inline sf_always_inline IndexType FullThreats::make_index(
// Get a list of indices for active features in ascending order // Get a list of indices for active features in ascending order
void FullThreats::append_active_indices(Color perspective, const Position& pos, IndexList& active) { void FullThreats::append_active_indices(Color perspective, const Position& pos, IndexList& active) {
const Square ksq = pos.square<KING>(perspective); const Square ksq = pos.square<KING>(perspective);
const Bitboard occupied = pos.pieces(); const Bitboard occupied = pos.pieces();
const Bitboard pawns = pos.pieces(PAWN); const Bitboard pawnTargets = pos.pieces(KNIGHT, ROOK);
const Bitboard pawnTargets = pos.pieces(PAWN, KNIGHT, ROOK);
const Bitboard minorSliderTargets = pos.pieces(PAWN, KNIGHT, BISHOP, ROOK); const Bitboard minorSliderTargets = pos.pieces(PAWN, KNIGHT, BISHOP, ROOK);
const Bitboard queenTargets = pos.pieces(PAWN, KNIGHT, BISHOP, ROOK, QUEEN); const Bitboard queenTargets = pos.pieces(PAWN, KNIGHT, BISHOP, ROOK, QUEEN);
@@ -221,18 +218,14 @@ void FullThreats::append_active_indices(Color perspective, const Position& pos,
const Color c = Color(perspective ^ color); const Color c = Color(perspective ^ color);
{ {
const Piece attacker = make_piece(c, PAWN); const Piece attacker = make_piece(c, PAWN);
const Bitboard cPawns = pos.pieces(c, PAWN); const Bitboard cPawns = pos.pieces(c, PAWN);
// Set of pawns which are prevented from movement by a pawn in front of them auto process_pawn_attacks = [&](Bitboard attacks, Direction attkDir) {
const Bitboard pushers = pawn_single_push_bb(~c, pawns) & cPawns;
auto process_pawn_attacks = [&](Bitboard attacks, Direction attkDir) {
while (attacks) while (attacks)
{ {
Square to = pop_lsb(attacks); Square to = pop_lsb(attacks);
Square from = to - attkDir; Square from = to - attkDir;
Piece attacked = pos.piece_on(to); Piece attacked = pos.piece_on(to);
assert(file_of(from) != file_of(to) || type_of(attacked) == PAWN);
IndexType index = make_index(perspective, attacker, from, to, attacked, ksq); IndexType index = make_index(perspective, attacker, from, to, attacked, ksq);
active.push_back_if_lt(index, Dimensions); active.push_back_if_lt(index, Dimensions);
} }
@@ -242,13 +235,11 @@ void FullThreats::append_active_indices(Color perspective, const Position& pos,
{ {
process_pawn_attacks(shift<NORTH_EAST>(cPawns) & pawnTargets, NORTH_EAST); process_pawn_attacks(shift<NORTH_EAST>(cPawns) & pawnTargets, NORTH_EAST);
process_pawn_attacks(shift<NORTH_WEST>(cPawns) & pawnTargets, NORTH_WEST); process_pawn_attacks(shift<NORTH_WEST>(cPawns) & pawnTargets, NORTH_WEST);
process_pawn_attacks(shift<NORTH>(pushers), NORTH);
} }
else else
{ {
process_pawn_attacks(shift<SOUTH_WEST>(cPawns) & pawnTargets, SOUTH_WEST); process_pawn_attacks(shift<SOUTH_WEST>(cPawns) & pawnTargets, SOUTH_WEST);
process_pawn_attacks(shift<SOUTH_EAST>(cPawns) & pawnTargets, SOUTH_EAST); process_pawn_attacks(shift<SOUTH_EAST>(cPawns) & pawnTargets, SOUTH_EAST);
process_pawn_attacks(shift<SOUTH>(pushers), SOUTH);
} }
} }
+9 -8
View File
@@ -29,16 +29,18 @@ class Position;
namespace Stockfish::Eval::NNUE::Features { namespace Stockfish::Eval::NNUE::Features {
static constexpr int numValidTargets[PIECE_NB] = {0, 6, 10, 8, 8, 10, 0, 0, // Pawn diagonal threats only target knights and rooks (pawn-pawn relationships
0, 6, 10, 8, 8, 10, 0, 0}; // are handled by the PP_3Wide feature set), so pawns have 4 valid targets.
static constexpr int numValidTargets[PIECE_NB] = {0, 4, 10, 8, 8, 10, 0, 0,
0, 4, 10, 8, 8, 10, 0, 0};
class FullThreats { class FullThreats {
public: public:
// Hash value embedded in the evaluation file // Hash value embedded in the evaluation file
static constexpr u32 HashValue = 0x8f234cb8u; static constexpr u32 HashValue = 0x2e6b9d04u;
// Number of feature dimensions // Number of feature dimensions
static constexpr IndexType Dimensions = 60720; static constexpr IndexType Dimensions = 59808;
// clang-format off // clang-format off
// Orient a square according to perspective (rotates by 180 for black) // Orient a square according to perspective (rotates by 180 for black)
@@ -54,7 +56,7 @@ class FullThreats {
}; };
static constexpr int map[PIECE_TYPE_NB-2][PIECE_TYPE_NB-2] = { static constexpr int map[PIECE_TYPE_NB-2][PIECE_TYPE_NB-2] = {
{ 0, 1, -1, 2, -1, -1}, {-1, 0, -1, 1, -1, -1},
{ 0, 1, 2, 3, 4, -1}, { 0, 1, 2, 3, 4, -1},
{ 0, 1, 2, 3, -1, -1}, { 0, 1, 2, 3, -1, -1},
{ 0, 1, 2, 3, -1, -1}, { 0, 1, 2, 3, -1, -1},
@@ -64,9 +66,8 @@ class FullThreats {
// clang-format on // clang-format on
// Maximum number of simultaneously active features. // Maximum number of simultaneously active features.
static constexpr IndexType MaxActiveDimensions = 128; using IndexList = ValueList<u16, 256>;
using IndexList = ValueList<IndexType, MaxActiveDimensions>; using DiffType = DirtyThreats;
using DiffType = DirtyThreats;
static IndexType static IndexType
make_index(Color perspective, Piece attkr, Square from, Square to, Piece attkd, Square ksq); make_index(Color perspective, Piece attkr, Square from, Square to, Piece attkd, Square ksq);
+171
View File
@@ -0,0 +1,171 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "pp_3wide.h"
#include <algorithm>
#include <cassert>
#include <cstdint>
#include "full_threats.h"
#include "../../bitboard.h"
#include "../../misc.h"
#include "../../position.h"
#include "../../types.h"
#include "../nnue_common.h"
namespace Stockfish::Eval::NNUE::Features {
constexpr IndexType make_pawn_id(Color color, Square square) {
assert(square >= SQ_A2 && square <= SQ_H7);
return 48 * int(color) + square - SQ_A2;
}
#ifdef USE_AVX512ICL
static inline __m256i pp_idx_epi16(__m256i a, __m256i b) {
const __m256i hi = _mm256_max_epu16(a, b);
const __m256i lo = _mm256_min_epu16(a, b);
const __m256i prod = _mm256_mullo_epi16(hi, _mm256_sub_epi16(hi, _mm256_set1_epi16(1)));
return _mm256_add_epi16(_mm256_add_epi16(_mm256_srli_epi16(prod, 1), lo),
_mm256_set1_epi16(i16(PP_3Wide::IndexBase)));
}
#endif
inline sf_always_inline IndexType PP_3Wide::make_index(
Color perspective, Color color, Square from, Square to, Color pairedColor, Square ksq) {
const i8 orientation = FullThreats::OrientTBL[ksq] ^ (56 * perspective);
unsigned from_oriented = u8(from) ^ orientation;
unsigned to_oriented = u8(to) ^ orientation;
Color color_oriented = Color(color ^ perspective);
Color pairedColor_oriented = Color(pairedColor ^ perspective);
assert(from_oriented >= SQ_A2 && from_oriented <= SQ_H7);
assert(to_oriented >= SQ_A2 && to_oriented <= SQ_H7);
const IndexType idA = make_pawn_id(color_oriented, Square(from_oriented));
const IndexType idB = make_pawn_id(pairedColor_oriented, Square(to_oriented));
const IndexType hi = std::max(idA, idB);
const IndexType lo = std::min(idA, idB);
return hi * (hi - 1) / 2 + lo + IndexBase;
}
void PP_3Wide::append_active_indices(Color perspective, const Position& pos, IndexList& active) {
const Square ksq = pos.square<KING>(perspective);
const Bitboard white = pos.pieces(WHITE, PAWN);
const Bitboard black = pos.pieces(BLACK, PAWN);
Bitboard bb = white;
while (bb)
{
Square from = pop_lsb(bb);
const Bitboard band = pawn_pair_bb(from);
for (Bitboard ww = band & bb; ww;)
active.push_back(make_index(perspective, WHITE, from, pop_lsb(ww), WHITE, ksq));
for (Bitboard wb = band & black; wb;)
active.push_back(make_index(perspective, WHITE, from, pop_lsb(wb), BLACK, ksq));
}
bb = black;
while (bb)
{
Square from = pop_lsb(bb);
const Bitboard band = pawn_pair_bb(from);
for (Bitboard bbk = band & bb; bbk;)
active.push_back(make_index(perspective, BLACK, from, pop_lsb(bbk), BLACK, ksq));
}
}
void PP_3Wide::append_changed_indices(Color perspective,
Square ksq,
const DiffType& diff,
IndexList& removed,
IndexList& added,
[[maybe_unused]] const ThreatWeightType* prefetchBase,
[[maybe_unused]] IndexType prefetchStride) {
const Bitboard whiteBefore = diff.before[WHITE];
const Bitboard blackBefore = diff.before[BLACK];
const Bitboard whiteAfter = diff.after[WHITE];
const Bitboard blackAfter = diff.after[BLACK];
if (whiteBefore == whiteAfter && blackBefore == blackAfter)
return;
#ifdef USE_AVX512ICL
const u8 orientation = u8(FullThreats::OrientTBL[ksq]) ^ u8(56 * perspective);
const __m512i iota = AllSquares;
const __m512i adjusted =
_mm512_sub_epi8(_mm512_xor_si512(iota, _mm512_set1_epi8(orientation)), _mm512_set1_epi8(8));
auto generate = [&](Bitboard updatedW, Bitboard updatedB, Bitboard pawnsW, Bitboard pawnsB,
IndexList& out) {
const Bitboard friendly = perspective == WHITE ? pawnsW : pawnsB;
const Bitboard enemy = perspective == WHITE ? pawnsB : pawnsW;
const __m512i ids = _mm512_mask_blend_epi8(
friendly, _mm512_add_epi8(adjusted, _mm512_set1_epi8(48)), adjusted);
const Bitboard unchanged = (pawnsW | pawnsB) & ~(updatedW | updatedB);
for (Bitboard u = updatedW | updatedB; u;)
{
const Square a = pop_lsb(u);
const Bitboard partners = pawn_pair_bb(a) & (unchanged | u);
const int n = popcount(partners);
if (!n)
continue;
const u16 colorOff = (enemy & a) ? 48 : 0;
const u16 aId = u16(((u8(a) ^ orientation) - 8) + colorOff);
const __m256i pids = _mm256_cvtepu8_epi16(
_mm512_castsi512_si128(_mm512_maskz_compress_epi8(partners, ids)));
const __m256i feats = pp_idx_epi16(_mm256_set1_epi16(aId), pids);
u16* w = out.make_space(n);
_mm256_storeu_epi16(w, feats);
}
};
#else
auto generate = [&](Bitboard updatedW, Bitboard updatedB, Bitboard pawnsW, Bitboard pawnsB,
IndexList& out) {
auto push = [&](IndexType index) {
if (prefetchBase)
prefetch<PrefetchRw::READ, PrefetchLoc::LOW>(reinterpret_cast<const void*>(
reinterpret_cast<uintptr_t>(prefetchBase) + index * prefetchStride));
out.push_back(index);
};
const Bitboard unchanged = (pawnsW | pawnsB) & ~(updatedW | updatedB);
for (Bitboard u = updatedW | updatedB; u;)
{
const Square a = pop_lsb(u);
const Bitboard mask = pawn_pair_bb(a) & (unchanged | u);
const Color aCol = (pawnsB & a) ? BLACK : WHITE;
for (Bitboard pb = pawnsB & mask; pb;)
push(make_index(perspective, aCol, a, pop_lsb(pb), BLACK, ksq));
for (Bitboard pw = pawnsW & mask; pw;)
push(make_index(perspective, aCol, a, pop_lsb(pw), WHITE, ksq));
}
};
#endif
generate(whiteAfter & ~whiteBefore, blackAfter & ~blackBefore, whiteAfter, blackAfter, added);
generate(whiteBefore & ~whiteAfter, blackBefore & ~blackAfter, whiteBefore, blackBefore,
removed);
}
} // namespace Stockfish::Eval::NNUE::Features
+61
View File
@@ -0,0 +1,61 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef NNUE_FEATURES_PP_3WIDE_INCLUDED
#define NNUE_FEATURES_PP_3WIDE_INCLUDED
#include "../../misc.h"
#include "../../types.h"
#include "../nnue_common.h"
namespace Stockfish {
class Position;
}
namespace Stockfish::Eval::NNUE::Features {
class PP_3Wide {
public:
static constexpr u32 HashValue = 0x86f2b1ddu;
static constexpr IndexType PawnIds = COLOR_NB * 48;
static constexpr IndexType Dimensions = PawnIds * (PawnIds - 1) / 2;
// Pawn pair feature indices are concatenated to threats, so this must equal ThreatFeatureSet::Dimensions;
// see nnue_feature_transformer.h
static constexpr IndexType IndexBase = 59808;
using IndexList = ValueList<u16, 256>;
using DiffType = DirtyPawnPairs;
static IndexType make_index(
Color perspective, Color color, Square from, Square to, Color pairedColor, Square ksq);
static void append_active_indices(Color perspective, const Position& pos, IndexList& active);
static void append_changed_indices(Color perspective,
Square ksq,
const DiffType& diff,
IndexList& removed,
IndexList& added,
const ThreatWeightType* prefetchBase = nullptr,
IndexType prefetchStride = 0);
};
} // namespace Stockfish::Eval::NNUE::Features
#endif // #ifndef NNUE_FEATURES_PP_3WIDE_INCLUDED
+40 -30
View File
@@ -57,17 +57,19 @@ AccumulatorState& AccumulatorStack::mut_latest() noexcept { return accumulators[
void AccumulatorStack::reset() noexcept { void AccumulatorStack::reset() noexcept {
accumulators[0].dirtyPiece = {}; accumulators[0].dirtyPiece = {};
new (&accumulators[0].dirtyThreats) DirtyThreats; new (&accumulators[0].dirtyThreats) DirtyThreats;
new (&accumulators[0].dirtyPawnPairs) DirtyPawnPairs;
accumulators[0].computed.fill(false); accumulators[0].computed.fill(false);
size = 1; size = 1;
} }
std::pair<DirtyPiece&, DirtyThreats&> AccumulatorStack::push() noexcept { Dirties& AccumulatorStack::push() noexcept {
assert(size < MaxSize); assert(size < MaxSize);
auto& st = accumulators[size]; auto& st = accumulators[size];
st.computed.fill(false); st.computed.fill(false);
new (&st.dirtyThreats) DirtyThreats; new (&st.dirtyThreats) DirtyThreats;
new (&st.dirtyPawnPairs) DirtyPawnPairs;
size++; size++;
return {st.dirtyPiece, st.dirtyThreats}; return st;
} }
void AccumulatorStack::pop() noexcept { void AccumulatorStack::pop() noexcept {
@@ -177,8 +179,8 @@ void apply_combined(Color perspective,
vec_t acc[Tiling::NumRegs]; vec_t acc[Tiling::NumRegs];
psqt_vec_t psqt[Tiling::NumPsqtRegs]; psqt_vec_t psqt[Tiling::NumPsqtRegs];
const auto* psqWeights = &featureTransformer.weights[0]; const auto* psqWeights = &featureTransformer.weights[0];
const auto* threatWeights = &featureTransformer.threatWeights[0]; const auto* threatAndPpWeights = &featureTransformer.threatAndPpWeights[0];
for (IndexType j = 0; j < Dimensions / Tiling::TileHeight; ++j) for (IndexType j = 0; j < Dimensions / Tiling::TileHeight; ++j)
{ {
@@ -208,7 +210,7 @@ void apply_combined(Color perspective,
for (int i = 0; i < thrRemoved.ssize(); ++i) for (int i = 0; i < thrRemoved.ssize(); ++i)
{ {
auto* column = reinterpret_cast<const vec_i8_t*>( auto* column = reinterpret_cast<const vec_i8_t*>(
&threatWeights[thrRemoved[i] * Dimensions + tileOff]); &threatAndPpWeights[thrRemoved[i] * Dimensions + tileOff]);
#ifdef USE_NEON #ifdef USE_NEON
for (IndexType k = 0; k < Tiling::NumRegs; k += 2) for (IndexType k = 0; k < Tiling::NumRegs; k += 2)
@@ -224,8 +226,8 @@ void apply_combined(Color perspective,
for (int i = 0; i < thrAdded.ssize(); ++i) for (int i = 0; i < thrAdded.ssize(); ++i)
{ {
auto* column = auto* column = reinterpret_cast<const vec_i8_t*>(
reinterpret_cast<const vec_i8_t*>(&threatWeights[thrAdded[i] * Dimensions + tileOff]); &threatAndPpWeights[thrAdded[i] * Dimensions + tileOff]);
#ifdef USE_NEON #ifdef USE_NEON
for (IndexType k = 0; k < Tiling::NumRegs; k += 2) for (IndexType k = 0; k < Tiling::NumRegs; k += 2)
@@ -271,7 +273,8 @@ void apply_combined(Color perspective,
for (int i = 0; i < thrRemoved.ssize(); ++i) for (int i = 0; i < thrRemoved.ssize(); ++i)
{ {
auto* columnPsqt = reinterpret_cast<const psqt_vec_t*>( auto* columnPsqt = reinterpret_cast<const psqt_vec_t*>(
&featureTransformer.threatPsqtWeights[thrRemoved[i] * PSQTBuckets + psqtTileOff]); &featureTransformer
.threatAndPpPsqtWeights[thrRemoved[i] * PSQTBuckets + psqtTileOff]);
for (usize k = 0; k < Tiling::NumPsqtRegs; ++k) for (usize k = 0; k < Tiling::NumPsqtRegs; ++k)
psqt[k] = vec_sub_psqt_32(psqt[k], columnPsqt[k]); psqt[k] = vec_sub_psqt_32(psqt[k], columnPsqt[k]);
} }
@@ -279,7 +282,7 @@ void apply_combined(Color perspective,
for (int i = 0; i < thrAdded.ssize(); ++i) for (int i = 0; i < thrAdded.ssize(); ++i)
{ {
auto* columnPsqt = reinterpret_cast<const psqt_vec_t*>( auto* columnPsqt = reinterpret_cast<const psqt_vec_t*>(
&featureTransformer.threatPsqtWeights[thrAdded[i] * PSQTBuckets + psqtTileOff]); &featureTransformer.threatAndPpPsqtWeights[thrAdded[i] * PSQTBuckets + psqtTileOff]);
for (usize k = 0; k < Tiling::NumPsqtRegs; ++k) for (usize k = 0; k < Tiling::NumPsqtRegs; ++k)
psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]); psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]);
} }
@@ -293,9 +296,9 @@ void apply_combined(Color perspective,
usize tileOffset = 0; usize tileOffset = 0;
const auto* psqWeights = &featureTransformer.weights[0]; const auto* psqWeights = &featureTransformer.weights[0];
const auto* threatWeights = &featureTransformer.threatWeights[0]; const auto* threatWeights = &featureTransformer.threatAndPpWeights[0];
const auto* psqtWeights = &featureTransformer.psqtWeights[0]; const auto* psqtWeights = &featureTransformer.psqtWeights[0];
const auto* threatPsqtWeights = &featureTransformer.threatPsqtWeights[0]; const auto* threatPsqtWeights = &featureTransformer.threatAndPpPsqtWeights[0];
while (tileOffset < Dimensions) while (tileOffset < Dimensions)
{ {
@@ -373,18 +376,18 @@ void apply_combined(Color perspective,
{ {
const IndexType offset = Dimensions * index; const IndexType offset = Dimensions * index;
for (IndexType j = 0; j < Dimensions; ++j) for (IndexType j = 0; j < Dimensions; ++j)
toAcc[j] -= featureTransformer.threatWeights[offset + j]; toAcc[j] -= featureTransformer.threatAndPpWeights[offset + j];
for (usize k = 0; k < PSQTBuckets; ++k) for (usize k = 0; k < PSQTBuckets; ++k)
toPsqtAcc[k] -= featureTransformer.threatPsqtWeights[index * PSQTBuckets + k]; toPsqtAcc[k] -= featureTransformer.threatAndPpPsqtWeights[index * PSQTBuckets + k];
} }
for (const auto index : thrAdded) for (const auto index : thrAdded)
{ {
const IndexType offset = Dimensions * index; const IndexType offset = Dimensions * index;
for (IndexType j = 0; j < Dimensions; ++j) for (IndexType j = 0; j < Dimensions; ++j)
toAcc[j] += featureTransformer.threatWeights[offset + j]; toAcc[j] += featureTransformer.threatAndPpWeights[offset + j];
for (usize k = 0; k < PSQTBuckets; ++k) for (usize k = 0; k < PSQTBuckets; ++k)
toPsqtAcc[k] += featureTransformer.threatPsqtWeights[index * PSQTBuckets + k]; toPsqtAcc[k] += featureTransformer.threatAndPpPsqtWeights[index * PSQTBuckets + k];
} }
#endif #endif
@@ -407,22 +410,28 @@ void update_accumulator_incremental(Color perspective,
PSQFeatureSet::IndexList psqRemoved, psqAdded; PSQFeatureSet::IndexList psqRemoved, psqAdded;
ThreatFeatureSet::IndexList thrRemoved, thrAdded; ThreatFeatureSet::IndexList thrRemoved, thrAdded;
const auto& dirtyPiece = Forward ? target_state.dirtyPiece : computed.dirtyPiece; const auto& dirtyPiece = Forward ? target_state.dirtyPiece : computed.dirtyPiece;
const auto& dirtyThreats = Forward ? target_state.dirtyThreats : computed.dirtyThreats; const auto& dirtyThreats = Forward ? target_state.dirtyThreats : computed.dirtyThreats;
const auto& dirtyPawnPairs = Forward ? target_state.dirtyPawnPairs : computed.dirtyPawnPairs;
const auto* pfBase = &featureTransformer.threatWeights[0]; // Used solely for prefetching
IndexType pfStride = FeatureTransformer::OutputDimensions; const auto* threatPpBase = &featureTransformer.threatAndPpWeights[0];
IndexType pfStride = FeatureTransformer::OutputDimensions;
if constexpr (Forward) if constexpr (Forward)
{ {
ThreatFeatureSet::append_changed_indices(perspective, ksq, dirtyThreats, thrRemoved, ThreatFeatureSet::append_changed_indices(perspective, ksq, dirtyThreats, thrRemoved,
thrAdded, pfBase, pfStride); thrAdded, threatPpBase, pfStride);
PairFeatureSet::append_changed_indices(perspective, ksq, dirtyPawnPairs, thrRemoved,
thrAdded, threatPpBase, pfStride);
PSQFeatureSet::append_changed_indices(perspective, ksq, dirtyPiece, psqRemoved, psqAdded); PSQFeatureSet::append_changed_indices(perspective, ksq, dirtyPiece, psqRemoved, psqAdded);
} }
else else
{ {
ThreatFeatureSet::append_changed_indices(perspective, ksq, dirtyThreats, thrAdded, ThreatFeatureSet::append_changed_indices(perspective, ksq, dirtyThreats, thrAdded,
thrRemoved, pfBase, pfStride); thrRemoved, threatPpBase, pfStride);
PairFeatureSet::append_changed_indices(perspective, ksq, dirtyPawnPairs, thrAdded,
thrRemoved, threatPpBase, pfStride);
PSQFeatureSet::append_changed_indices(perspective, ksq, dirtyPiece, psqAdded, psqRemoved); PSQFeatureSet::append_changed_indices(perspective, ksq, dirtyPiece, psqAdded, psqRemoved);
} }
@@ -575,6 +584,7 @@ void update_accumulator_refresh_cache(Color perspective,
ThreatFeatureSet::IndexList active; ThreatFeatureSet::IndexList active;
ThreatFeatureSet::append_active_indices(perspective, pos, active); ThreatFeatureSet::append_active_indices(perspective, pos, active);
PairFeatureSet::append_active_indices(perspective, pos, active);
accumulator.computed[perspective] = true; accumulator.computed[perspective] = true;
@@ -582,8 +592,8 @@ void update_accumulator_refresh_cache(Color perspective,
vec_t acc[Tiling::NumRegs]; vec_t acc[Tiling::NumRegs];
psqt_vec_t psqt[Tiling::NumPsqtRegs]; psqt_vec_t psqt[Tiling::NumPsqtRegs];
const auto* weights = &featureTransformer.weights[0]; const auto* weights = &featureTransformer.weights[0];
const auto* threatWeights = &featureTransformer.threatWeights[0]; const auto* threatAndPpWeights = &featureTransformer.threatAndPpWeights[0];
for (IndexType j = 0; j < Dimensions / Tiling::TileHeight; ++j) for (IndexType j = 0; j < Dimensions / Tiling::TileHeight; ++j)
{ {
@@ -614,8 +624,8 @@ void update_accumulator_refresh_cache(Color perspective,
for (int i = 0; i < active.ssize(); ++i) for (int i = 0; i < active.ssize(); ++i)
{ {
auto* column = auto* column = reinterpret_cast<const vec_i8_t*>(
reinterpret_cast<const vec_i8_t*>(&threatWeights[active[i] * Dimensions + tileOff]); &threatAndPpWeights[active[i] * Dimensions + tileOff]);
#ifdef USE_NEON #ifdef USE_NEON
for (IndexType k = 0; k < Tiling::NumRegs; k += 2) for (IndexType k = 0; k < Tiling::NumRegs; k += 2)
@@ -664,7 +674,7 @@ void update_accumulator_refresh_cache(Color perspective,
for (int i = 0; i < active.ssize(); ++i) for (int i = 0; i < active.ssize(); ++i)
{ {
auto* columnPsqt = reinterpret_cast<const psqt_vec_t*>( auto* columnPsqt = reinterpret_cast<const psqt_vec_t*>(
&featureTransformer.threatPsqtWeights[active[i] * PSQTBuckets + psqtTileOff]); &featureTransformer.threatAndPpPsqtWeights[active[i] * PSQTBuckets + psqtTileOff]);
for (usize k = 0; k < Tiling::NumPsqtRegs; ++k) for (usize k = 0; k < Tiling::NumPsqtRegs; ++k)
psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]); psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]);
} }
@@ -676,9 +686,9 @@ void update_accumulator_refresh_cache(Color perspective,
#elif defined(USE_RVV) #elif defined(USE_RVV)
const auto* weights = &featureTransformer.weights[0]; const auto* weights = &featureTransformer.weights[0];
const auto* threatWeights = &featureTransformer.threatWeights[0]; const auto* threatWeights = &featureTransformer.threatAndPpWeights[0];
const auto* psqtWeights = &featureTransformer.psqtWeights[0]; const auto* psqtWeights = &featureTransformer.psqtWeights[0];
const auto* threatPsqtWeights = &featureTransformer.threatPsqtWeights[0]; const auto* threatPsqtWeights = &featureTransformer.threatAndPpPsqtWeights[0];
usize tileOffset = 0; usize tileOffset = 0;
@@ -763,11 +773,11 @@ void update_accumulator_refresh_cache(Color perspective,
for (IndexType j = 0; j < Dimensions; ++j) for (IndexType j = 0; j < Dimensions; ++j)
accumulator.accumulation[perspective][j] += accumulator.accumulation[perspective][j] +=
featureTransformer.threatWeights[offset + j]; featureTransformer.threatAndPpWeights[offset + j];
for (usize k = 0; k < PSQTBuckets; ++k) for (usize k = 0; k < PSQTBuckets; ++k)
accumulator.psqtAccumulation[perspective][k] += accumulator.psqtAccumulation[perspective][k] +=
featureTransformer.threatPsqtWeights[index * PSQTBuckets + k]; featureTransformer.threatAndPpPsqtWeights[index * PSQTBuckets + k];
} }
#endif #endif
+4 -8
View File
@@ -24,7 +24,6 @@
#include <array> #include <array>
#include <cstddef> #include <cstddef>
#include <cstring> #include <cstring>
#include <utility>
#include "../types.h" #include "../types.h"
#include "../misc.h" #include "../misc.h"
@@ -90,10 +89,7 @@ struct AccumulatorCaches {
}; };
struct AccumulatorState: public Accumulator { struct AccumulatorState: public Accumulator, Dirties {};
DirtyPiece dirtyPiece;
DirtyThreats dirtyThreats;
};
class AccumulatorStack { class AccumulatorStack {
public: public:
@@ -101,9 +97,9 @@ class AccumulatorStack {
[[nodiscard]] const AccumulatorState& latest() const noexcept; [[nodiscard]] const AccumulatorState& latest() const noexcept;
void reset() noexcept; void reset() noexcept;
std::pair<DirtyPiece&, DirtyThreats&> push() noexcept; Dirties& push() noexcept;
void pop() noexcept; void pop() noexcept;
void evaluate(const Position& pos, void evaluate(const Position& pos,
const FeatureTransformer& featureTransformer, const FeatureTransformer& featureTransformer,
+2
View File
@@ -26,6 +26,7 @@
#include "features/half_ka_v2_hm.h" #include "features/half_ka_v2_hm.h"
#include "features/full_threats.h" #include "features/full_threats.h"
#include "features/pp_3wide.h"
#include "layers/affine_transform.h" #include "layers/affine_transform.h"
#include "layers/affine_transform_sparse_input.h" #include "layers/affine_transform_sparse_input.h"
#include "layers/clipped_relu.h" #include "layers/clipped_relu.h"
@@ -37,6 +38,7 @@ namespace Stockfish::Eval::NNUE {
// Input features used in evaluation function // Input features used in evaluation function
using ThreatFeatureSet = Features::FullThreats; using ThreatFeatureSet = Features::FullThreats;
using PairFeatureSet = Features::PP_3Wide;
using PSQFeatureSet = Features::HalfKAv2_hm; using PSQFeatureSet = Features::HalfKAv2_hm;
// Number of input feature dimensions after conversion // Number of input feature dimensions after conversion
+28 -9
View File
@@ -187,12 +187,9 @@ inline void write_little_endian(std::ostream& stream, const IntType* values, usi
// Read N signed integers from the stream s, putting them in the array out. // Read N signed integers from the stream s, putting them in the array out.
// The stream is assumed to be compressed using the signed LEB128 format. // The stream is assumed to be compressed using the signed LEB128 format.
// See https://en.wikipedia.org/wiki/LEB128 for a description of the compression scheme. // See https://en.wikipedia.org/wiki/LEB128 for a description of the compression scheme.
template<typename BufType, typename IntType, usize Count> template<typename BufType, typename IntType>
inline void read_leb_128_detail(std::istream& stream, inline void read_leb_128_detail(
std::array<IntType, Count>& out, std::istream& stream, IntType* out, usize Count, u32& bytes_left, BufType& buf, u32& buf_pos) {
u32& bytes_left,
BufType& buf,
u32& buf_pos) {
static_assert(std::is_signed_v<IntType>, "Not implemented for unsigned types"); static_assert(std::is_signed_v<IntType>, "Not implemented for unsigned types");
static_assert(sizeof(IntType) <= 4, "Not implemented for types larger than 32 bit"); static_assert(sizeof(IntType) <= 4, "Not implemented for types larger than 32 bit");
@@ -233,7 +230,24 @@ inline void read_leb_128(std::istream& stream, Arrays&... outs) {
std::array<u8, 8192> buf; std::array<u8, 8192> buf;
u32 buf_pos = u32(buf.size()); u32 buf_pos = u32(buf.size());
(read_leb_128_detail(stream, outs, bytes_left, buf, buf_pos), ...); (read_leb_128_detail(stream, outs.data(), outs.size(), bytes_left, buf, buf_pos), ...);
assert(bytes_left == 0);
}
template<typename IntType>
inline void read_leb_128(std::istream& stream, IntType* out, usize expected) {
// Check the presence of our LEB128 magic string
char leb128MagicString[Leb128MagicStringSize];
stream.read(leb128MagicString, Leb128MagicStringSize);
assert(strncmp(Leb128MagicString, leb128MagicString, Leb128MagicStringSize) == 0);
auto bytes_left = read_little_endian<u32>(stream);
std::array<u8, 8192> buf;
u32 buf_pos = u32(buf.size());
read_leb_128_detail(stream, out, expected, bytes_left, buf, buf_pos);
assert(bytes_left == 0); assert(bytes_left == 0);
} }
@@ -243,8 +257,8 @@ inline void read_leb_128(std::istream& stream, Arrays&... outs) {
// This takes N integers from array values, compresses them with // This takes N integers from array values, compresses them with
// the LEB128 algorithm and writes the result on the stream s. // the LEB128 algorithm and writes the result on the stream s.
// See https://en.wikipedia.org/wiki/LEB128 for a description of the compression scheme. // See https://en.wikipedia.org/wiki/LEB128 for a description of the compression scheme.
template<typename IntType, usize Count> template<typename IntType>
inline void write_leb_128(std::ostream& stream, const std::array<IntType, Count>& values) { inline void write_leb_128(std::ostream& stream, const IntType* values, usize Count) {
// Write our LEB128 magic string // Write our LEB128 magic string
stream.write(Leb128MagicString, Leb128MagicStringSize); stream.write(Leb128MagicString, Leb128MagicStringSize);
@@ -303,6 +317,11 @@ inline void write_leb_128(std::ostream& stream, const std::array<IntType, Count>
flush(); flush();
} }
template<typename IntType, usize Count>
inline void write_leb_128(std::ostream& stream, const std::array<IntType, Count>& values) {
write_leb_128(stream, values.data(), Count);
}
} // namespace Stockfish::Eval::NNUE } // namespace Stockfish::Eval::NNUE
#endif // #ifndef NNUE_COMMON_H_INCLUDED #endif // #ifndef NNUE_COMMON_H_INCLUDED
+51 -15
View File
@@ -87,7 +87,9 @@ class FeatureTransformer {
// Number of input/output dimensions // Number of input/output dimensions
static constexpr IndexType ThreatInputDimensions = ThreatFeatureSet::Dimensions; static constexpr IndexType ThreatInputDimensions = ThreatFeatureSet::Dimensions;
static constexpr IndexType InputDimensions = PSQFeatureSet::Dimensions + ThreatInputDimensions; static constexpr IndexType PairInputDimensions = PairFeatureSet::Dimensions;
static constexpr IndexType InputDimensions =
PSQFeatureSet::Dimensions + ThreatInputDimensions + PairInputDimensions;
static constexpr IndexType OutputDimensions = HalfDimensions; static constexpr IndexType OutputDimensions = HalfDimensions;
// Size of forward propagation buffer // Size of forward propagation buffer
@@ -128,7 +130,8 @@ class FeatureTransformer {
// Hash value embedded in the evaluation file // Hash value embedded in the evaluation file
static constexpr u32 get_hash_value() { static constexpr u32 get_hash_value() {
return combine_hash({ThreatFeatureSet::HashValue, PSQFeatureSet::HashValue}) return combine_hash(
{ThreatFeatureSet::HashValue, PairFeatureSet::HashValue, PSQFeatureSet::HashValue})
^ (OutputDimensions * 2); ^ (OutputDimensions * 2);
} }
@@ -136,22 +139,42 @@ class FeatureTransformer {
permute<16>(biases, PackusEpi16Order); permute<16>(biases, PackusEpi16Order);
permute<16>(weights, PackusEpi16Order); permute<16>(weights, PackusEpi16Order);
permute<8>(threatWeights, PackusEpi16Order); permute<8>(threatAndPpWeights, PackusEpi16Order);
} }
void unpermute_weights() { void unpermute_weights() {
permute<16>(biases, InversePackusEpi16Order); permute<16>(biases, InversePackusEpi16Order);
permute<16>(weights, InversePackusEpi16Order); permute<16>(weights, InversePackusEpi16Order);
permute<8>(threatWeights, InversePackusEpi16Order); permute<8>(threatAndPpWeights, InversePackusEpi16Order);
} }
auto threatWeights() { return threatAndPpWeights.data(); }
auto threatWeights() const { return threatAndPpWeights.data(); }
auto ppWeights() { return &threatAndPpWeights[ThreatFeatureSet::Dimensions * HalfDimensions]; }
auto ppWeights() const {
return &threatAndPpWeights[ThreatFeatureSet::Dimensions * HalfDimensions];
}
auto threatPsqtWeights() { return threatAndPpPsqtWeights.data(); }
auto threatPsqtWeights() const { return threatAndPpPsqtWeights.data(); }
auto ppPsqtWeights() {
return &threatAndPpPsqtWeights[ThreatFeatureSet::Dimensions * PSQTBuckets];
}
auto ppPsqtWeights() const {
return &threatAndPpPsqtWeights[ThreatFeatureSet::Dimensions * PSQTBuckets];
}
// Read network parameters // Read network parameters
bool read_parameters(std::istream& stream) { bool read_parameters(std::istream& stream) {
read_leb_128(stream, biases); read_leb_128(stream, biases);
read_little_endian<ThreatWeightType>(stream, threatWeights.data(), read_little_endian<ThreatWeightType>(stream, threatWeights(),
ThreatInputDimensions * HalfDimensions); ThreatInputDimensions * HalfDimensions);
read_leb_128(stream, threatPsqtWeights); read_leb_128(stream, threatPsqtWeights(), ThreatFeatureSet::Dimensions * PSQTBuckets);
read_little_endian<ThreatWeightType>(stream, ppWeights(),
PairInputDimensions * HalfDimensions);
read_leb_128(stream, ppPsqtWeights(), PairFeatureSet::Dimensions * PSQTBuckets);
read_leb_128(stream, weights); read_leb_128(stream, weights);
read_leb_128(stream, psqtWeights); read_leb_128(stream, psqtWeights);
@@ -170,9 +193,14 @@ class FeatureTransformer {
write_leb_128<BiasType>(stream, copy->biases); write_leb_128<BiasType>(stream, copy->biases);
write_little_endian<ThreatWeightType>(stream, copy->threatWeights.data(), write_little_endian<ThreatWeightType>(stream, copy->threatWeights(),
ThreatInputDimensions * HalfDimensions); ThreatInputDimensions * HalfDimensions);
write_leb_128<PSQTWeightType>(stream, copy->threatPsqtWeights); write_leb_128<PSQTWeightType>(stream, copy->threatPsqtWeights(),
ThreatFeatureSet::Dimensions * PSQTBuckets);
write_little_endian<ThreatWeightType>(stream, copy->ppWeights(),
PairInputDimensions * HalfDimensions);
write_leb_128<PSQTWeightType>(stream, copy->ppPsqtWeights(),
PairFeatureSet::Dimensions * PSQTBuckets);
write_leb_128<WeightType>(stream, copy->weights); write_leb_128<WeightType>(stream, copy->weights);
write_leb_128<PSQTWeightType>(stream, copy->psqtWeights); write_leb_128<PSQTWeightType>(stream, copy->psqtWeights);
@@ -187,8 +215,8 @@ class FeatureTransformer {
hash_combine(h, get_raw_data_hash(weights)); hash_combine(h, get_raw_data_hash(weights));
hash_combine(h, get_raw_data_hash(psqtWeights)); hash_combine(h, get_raw_data_hash(psqtWeights));
hash_combine(h, get_raw_data_hash(threatWeights)); hash_combine(h, get_raw_data_hash(threatAndPpWeights));
hash_combine(h, get_raw_data_hash(threatPsqtWeights)); hash_combine(h, get_raw_data_hash(threatAndPpPsqtWeights));
hash_combine(h, get_hash_value()); hash_combine(h, get_hash_value());
@@ -399,12 +427,20 @@ class FeatureTransformer {
alignas(CacheLineSize) std::array<BiasType, HalfDimensions> biases; alignas(CacheLineSize) std::array<BiasType, HalfDimensions> biases;
alignas( alignas(
CacheLineSize) std::array<WeightType, HalfDimensions * PSQFeatureSet::Dimensions> weights; CacheLineSize) std::array<WeightType, HalfDimensions * PSQFeatureSet::Dimensions> weights;
// Threats and pawn-pair features are concatenated into one array to allow for a single index to address either.
// The first pawn-pair feature is at index ThreatFeatureSet::Dimensions.
static_assert(PairFeatureSet::IndexBase == ThreatFeatureSet::Dimensions);
alignas(CacheLineSize) std::array<ThreatWeightType,
(ThreatFeatureSet::Dimensions + PairFeatureSet::Dimensions)
* HalfDimensions> threatAndPpWeights;
alignas(CacheLineSize) alignas(CacheLineSize)
std::array<ThreatWeightType, HalfDimensions * ThreatFeatureSet::Dimensions> threatWeights; std::array<PSQTWeightType, PSQTBuckets * PSQFeatureSet::Dimensions> psqtWeights;
alignas(CacheLineSize) // As above
std::array<PSQTWeightType, PSQFeatureSet::Dimensions * PSQTBuckets> psqtWeights; alignas(CacheLineSize) std::array<PSQTWeightType,
alignas(CacheLineSize) (ThreatFeatureSet::Dimensions + PairFeatureSet::Dimensions)
std::array<PSQTWeightType, ThreatFeatureSet::Dimensions * PSQTBuckets> threatPsqtWeights; * PSQTBuckets> threatAndPpPsqtWeights;
}; };
} // namespace Stockfish::Eval::NNUE } // namespace Stockfish::Eval::NNUE
+32 -37
View File
@@ -469,12 +469,13 @@ void Position::set_check_info() const {
update_slider_blockers(WHITE); update_slider_blockers(WHITE);
update_slider_blockers(BLACK); update_slider_blockers(BLACK);
Square ksq = square<KING>(~sideToMove); Square ksq = square<KING>(~sideToMove);
const auto [bishopAttacks, rookAttacks] = both_attacks_bb(ksq, pieces());
st->checkSquares[PAWN] = attacks_bb<PAWN>(ksq, ~sideToMove); st->checkSquares[PAWN] = attacks_bb<PAWN>(ksq, ~sideToMove);
st->checkSquares[KNIGHT] = attacks_bb<KNIGHT>(ksq); st->checkSquares[KNIGHT] = attacks_bb<KNIGHT>(ksq);
st->checkSquares[BISHOP] = attacks_bb<BISHOP>(ksq, pieces()); st->checkSquares[BISHOP] = bishopAttacks;
st->checkSquares[ROOK] = attacks_bb<ROOK>(ksq, pieces()); st->checkSquares[ROOK] = rookAttacks;
st->checkSquares[QUEEN] = st->checkSquares[BISHOP] | st->checkSquares[ROOK]; st->checkSquares[QUEEN] = st->checkSquares[BISHOP] | st->checkSquares[ROOK];
st->checkSquares[KING] = 0; st->checkSquares[KING] = 0;
} }
@@ -641,8 +642,9 @@ void Position::update_slider_blockers(Color c) const {
// Slider attacks use the occupied bitboard to indicate occupancy. // Slider attacks use the occupied bitboard to indicate occupancy.
Bitboard Position::attackers_to(Square s, Bitboard occupied) const { Bitboard Position::attackers_to(Square s, Bitboard occupied) const {
return (attacks_bb<ROOK>(s, occupied) & pieces(ROOK, QUEEN)) const auto [bishopAttacks, rookAttacks] = both_attacks_bb(s, occupied);
| (attacks_bb<BISHOP>(s, occupied) & pieces(BISHOP, QUEEN))
return (rookAttacks & pieces(ROOK, QUEEN)) | (bishopAttacks & pieces(BISHOP, QUEEN))
| (attacks_bb<PAWN>(s, BLACK) & pieces(WHITE, PAWN)) | (attacks_bb<PAWN>(s, BLACK) & pieces(WHITE, PAWN))
| (attacks_bb<PAWN>(s, WHITE) & pieces(BLACK, PAWN)) | (attacks_bb<PAWN>(s, WHITE) & pieces(BLACK, PAWN))
| (attacks_bb<KNIGHT>(s) & pieces(KNIGHT)) | (attacks_bb<KING>(s) & pieces(KING)); | (attacks_bb<KNIGHT>(s) & pieces(KNIGHT)) | (attacks_bb<KING>(s) & pieces(KING));
@@ -789,12 +791,12 @@ bool Position::gives_check(Move m) const {
// checks and ordinary discovered check, so the only case we need to handle // checks and ordinary discovered check, so the only case we need to handle
// is the unusual case of a discovered check through the captured pawn. // is the unusual case of a discovered check through the captured pawn.
case EN_PASSANT : { case EN_PASSANT : {
Square capsq = make_square(file_of(to), rank_of(from)); Square capsq = make_square(file_of(to), rank_of(from));
Bitboard b = (pieces() ^ from ^ capsq) | to; Bitboard b = (pieces() ^ from ^ capsq) | to;
const auto [bishopAttacks, rookAttacks] = both_attacks_bb(square<KING>(~sideToMove), b);
return (attacks_bb<ROOK>(square<KING>(~sideToMove), b) & pieces(sideToMove, QUEEN, ROOK)) return (rookAttacks & pieces(sideToMove, QUEEN, ROOK))
| (attacks_bb<BISHOP>(square<KING>(~sideToMove), b) | (bishopAttacks & pieces(sideToMove, QUEEN, BISHOP));
& pieces(sideToMove, QUEEN, BISHOP));
} }
default : //CASTLING default : //CASTLING
{ {
@@ -815,8 +817,7 @@ bool Position::gives_check(Move m) const {
void Position::do_move(Move m, void Position::do_move(Move m,
StateInfo& newSt, StateInfo& newSt,
bool givesCheck, bool givesCheck,
DirtyPiece& dp, Dirties& dirties,
DirtyThreats& dts,
const TranspositionTable* tt = nullptr, const TranspositionTable* tt = nullptr,
const SharedHistories* history = nullptr) { const SharedHistories* history = nullptr) {
@@ -838,6 +839,13 @@ void Position::do_move(Move m,
++st->rule50; ++st->rule50;
++st->pliesFromNull; ++st->pliesFromNull;
auto& dpps = dirties.dirtyPawnPairs;
auto& dts = dirties.dirtyThreats;
auto& dp = dirties.dirtyPiece;
dpps.before[WHITE] = pieces(WHITE, PAWN);
dpps.before[BLACK] = pieces(BLACK, PAWN);
Color us = sideToMove; Color us = sideToMove;
Color them = ~us; Color them = ~us;
Square from = m.from_sq(); Square from = m.from_sq();
@@ -1063,6 +1071,9 @@ void Position::do_move(Move m,
assert(pos_is_ok()); assert(pos_is_ok());
dpps.after[WHITE] = pieces(WHITE, PAWN);
dpps.after[BLACK] = pieces(BLACK, PAWN);
assert(dp.pc != NO_PIECE); assert(dp.pc != NO_PIECE);
assert(!(bool(captured) || m.type_of() == CASTLING) ^ (dp.remove_sq != SQ_NONE)); assert(!(bool(captured) || m.type_of() == CASTLING) ^ (dp.remove_sq != SQ_NONE));
assert(dp.from != SQ_NONE); assert(dp.from != SQ_NONE);
@@ -1189,8 +1200,9 @@ void Position::update_piece_threats(Piece pc,
const Bitboard occupied = pieces(); const Bitboard occupied = pieces();
const Bitboard rookQueens = pieces(ROOK, QUEEN); const Bitboard rookQueens = pieces(ROOK, QUEEN);
const Bitboard bishopQueens = pieces(BISHOP, QUEEN); const Bitboard bishopQueens = pieces(BISHOP, QUEEN);
const Bitboard rAttacks = attacks_bb<ROOK>(s, occupied); const auto attacks = both_attacks_bb(s, occupied);
const Bitboard bAttacks = attacks_bb<BISHOP>(s, occupied); const Bitboard bAttacks = attacks.first;
const Bitboard rAttacks = attacks.second;
const Bitboard occupiedNoK = occupied ^ pieces(KING); const Bitboard occupiedNoK = occupied ^ pieces(KING);
Bitboard sliders = (rookQueens & rAttacks) | (bishopQueens & bAttacks); Bitboard sliders = (rookQueens & rAttacks) | (bishopQueens & bAttacks);
@@ -1235,32 +1247,14 @@ void Position::update_piece_threats(Piece pc,
Bitboard threatened = attacks_bb(pc, s, occupied) & occupiedNoK; Bitboard threatened = attacks_bb(pc, s, occupied) & occupiedNoK;
Bitboard incoming_threats = PseudoAttacks[KNIGHT][s] & knights; Bitboard incoming_threats = PseudoAttacks[KNIGHT][s] & knights;
// Compute both incoming and outgoing pawn threats. Incoming pawn pushers are only if (type_of(pc) == KNIGHT || type_of(pc) == ROOK)
// added if 'pc' is a pawn. incoming_threats |=
Bitboard pawnThreats = 0;
if (type_of(pc) == PAWN)
{
Bitboard whiteAttacks = PawnPushOrAttacks[WHITE][s];
Bitboard blackAttacks = PawnPushOrAttacks[BLACK][s];
threatened |= (color_of(pc) == WHITE ? whiteAttacks : blackAttacks) & pieces(PAWN);
pawnThreats = whiteAttacks & blackPawns;
pawnThreats |= blackAttacks & whitePawns;
}
else
{
pawnThreats =
(attacks_bb<PAWN>(s, WHITE) & blackPawns) | (attacks_bb<PAWN>(s, BLACK) & whitePawns); (attacks_bb<PAWN>(s, WHITE) & blackPawns) | (attacks_bb<PAWN>(s, BLACK) & whitePawns);
}
if (type_of(pc) == PAWN || type_of(pc) == KNIGHT || type_of(pc) == ROOK)
incoming_threats |= pawnThreats;
switch (type_of(pc)) switch (type_of(pc))
{ {
case PAWN : case PAWN :
threatened &= pieces(PAWN, KNIGHT, ROOK); threatened &= pieces(KNIGHT, ROOK);
break; break;
case BISHOP : case BISHOP :
case ROOK : case ROOK :
@@ -1508,8 +1502,9 @@ bool Position::see_ge(Move m, int threshold) const {
assert(swap >= res); assert(swap >= res);
occupied ^= least_significant_square_bb(bb); occupied ^= least_significant_square_bb(bb);
attackers |= (attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN)) const auto [bishopAttacks, rookAttacks] = both_attacks_bb(to, occupied);
| (attacks_bb<ROOK>(to, occupied) & pieces(ROOK, QUEEN)); attackers |=
(bishopAttacks & pieces(BISHOP, QUEEN)) | (rookAttacks & pieces(ROOK, QUEEN));
} }
else // KING else // KING
+13 -14
View File
@@ -147,8 +147,7 @@ class Position {
void do_move(Move m, void do_move(Move m,
StateInfo& newSt, StateInfo& newSt,
bool givesCheck, bool givesCheck,
DirtyPiece& dp, Dirties& dirties,
DirtyThreats& dts,
const TranspositionTable* tt, const TranspositionTable* tt,
const SharedHistories* worker); const SharedHistories* worker);
void undo_move(Move m); void undo_move(Move m);
@@ -221,16 +220,15 @@ class Position {
std::array<Bitboard, PIECE_TYPE_NB> byTypeBB; std::array<Bitboard, PIECE_TYPE_NB> byTypeBB;
std::array<Bitboard, COLOR_NB> byColorBB; std::array<Bitboard, COLOR_NB> byColorBB;
int pieceCount[PIECE_NB]; int pieceCount[PIECE_NB];
int castlingRightsMask[SQUARE_NB]; int castlingRightsMask[SQUARE_NB];
Square castlingRookSquare[CASTLING_RIGHT_NB]; Square castlingRookSquare[CASTLING_RIGHT_NB];
Bitboard castlingPath[CASTLING_RIGHT_NB]; Bitboard castlingPath[CASTLING_RIGHT_NB];
StateInfo* st; StateInfo* st;
int gamePly; int gamePly;
Color sideToMove; Color sideToMove;
bool chess960; bool chess960;
DirtyPiece scratch_dp; Dirties scratchDirties;
DirtyThreats scratch_dts;
}; };
std::ostream& operator<<(std::ostream& os, const Position& pos); std::ostream& operator<<(std::ostream& os, const Position& pos);
@@ -437,8 +435,9 @@ inline void Position::swap_piece(Square s, Piece pc, DirtyThreats* const dts) {
} }
inline void Position::do_move(Move m, StateInfo& newSt, const TranspositionTable* tt = nullptr) { inline void Position::do_move(Move m, StateInfo& newSt, const TranspositionTable* tt = nullptr) {
new (&scratch_dts) DirtyThreats; new (&scratchDirties.dirtyThreats) DirtyThreats;
do_move(m, newSt, gives_check(m), scratch_dp, scratch_dts, tt, nullptr); new (&scratchDirties.dirtyPawnPairs) DirtyPawnPairs;
do_move(m, newSt, gives_check(m), scratchDirties, tt, nullptr);
} }
inline StateInfo* Position::state() const { return st; } inline StateInfo* Position::state() const { return st; }
+4 -3
View File
@@ -644,12 +644,13 @@ void Search::Worker::do_move(
bool capture = pos.capture_stage(move); bool capture = pos.capture_stage(move);
++nodes; ++nodes;
auto [dirtyPiece, dirtyThreats] = accumulatorStack.push(); Dirties& dirties = accumulatorStack.push();
pos.do_move(move, st, givesCheck, dirtyPiece, dirtyThreats, &tt, &sharedHistory); pos.do_move(move, st, givesCheck, dirties, &tt, &sharedHistory);
if (ss != nullptr) if (ss != nullptr)
{ {
ss->currentMove = move; auto& dirtyPiece = dirties.dirtyPiece;
ss->currentMove = move;
ss->continuationHistory = ss->continuationHistory =
&continuationHistory[ss->inCheck][capture][dirtyPiece.pc][move.to_sq()]; &continuationHistory[ss->inCheck][capture][dirtyPiece.pc][move.to_sq()];
ss->continuationCorrectionHistory = ss->continuationCorrectionHistory =
+1 -1
View File
@@ -31,7 +31,7 @@
#include "numa.h" #include "numa.h"
#include "position.h" #include "position.h"
#include "search.h" #include "search.h"
#include "thread_win32_osx.h" #include "thread_native.h"
namespace Stockfish { namespace Stockfish {
+24 -20
View File
@@ -16,10 +16,29 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef THREAD_WIN32_OSX_H_INCLUDED #ifndef THREAD_NATIVE_H_INCLUDED
#define THREAD_WIN32_OSX_H_INCLUDED #define THREAD_NATIVE_H_INCLUDED
#include <thread> #ifdef _MSC_VER
#include <thread>
#else
#include <pthread.h>
#include <functional>
#include <utility>
#include "misc.h"
#endif
namespace Stockfish {
#ifdef _MSC_VER
// MSVC-compatible toolchains use std::thread because they do not provide
// pthreads by default. On all other platforms, pthreads is required and used.
using NativeThread = std::thread;
#else
// On OSX threads other than the main thread are created with a reduced stack // On OSX threads other than the main thread are created with a reduced stack
// size of 512KB by default, this is too low for deep searches, which require // size of 512KB by default, this is too low for deep searches, which require
@@ -27,13 +46,6 @@
// The implementation calls pthread_create() with the stack size parameter // The implementation calls pthread_create() with the stack size parameter
// equal to the Linux 8MB default, on platforms that support it. // equal to the Linux 8MB default, on platforms that support it.
#if defined(__APPLE__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(USE_PTHREADS)
#include <pthread.h>
#include <functional>
namespace Stockfish {
class NativeThread { class NativeThread {
pthread_t thread; pthread_t thread;
@@ -63,16 +75,8 @@ class NativeThread {
void join() { pthread_join(thread, nullptr); } void join() { pthread_join(thread, nullptr); }
}; };
} // namespace Stockfish #endif // _MSC_VER
#else // Default case: use STL classes
namespace Stockfish {
using NativeThread = std::thread;
} // namespace Stockfish } // namespace Stockfish
#endif #endif // #ifndef THREAD_NATIVE_H_INCLUDED
#endif // #ifndef THREAD_WIN32_OSX_H_INCLUDED
+11
View File
@@ -344,6 +344,17 @@ struct DirtyThreats {
DirtyThreatList list; DirtyThreatList list;
}; };
struct DirtyPawnPairs {
Bitboard before[COLOR_NB];
Bitboard after[COLOR_NB];
};
struct Dirties {
DirtyPiece dirtyPiece;
DirtyThreats dirtyThreats;
DirtyPawnPairs dirtyPawnPairs;
};
#define ENABLE_INCR_OPERATORS_ON(T) \ #define ENABLE_INCR_OPERATORS_ON(T) \
constexpr T& operator++(T& d) { return d = T(int(d) + 1); } \ constexpr T& operator++(T& d) { return d = T(int(d) + 1); } \
constexpr T& operator--(T& d) { return d = T(int(d) - 1); } constexpr T& operator--(T& d) { return d = T(int(d) - 1); }