mirror of
https://github.com/official-stockfish/Stockfish.git
synced 2026-07-22 20:57:10 +00:00
Compare commits
18
Commits
@@ -0,0 +1,85 @@
|
|||||||
|
name: AVX2 Compiler Matrix
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
avx2-compiler-matrix:
|
||||||
|
name: avx2 (${{ matrix.name }})
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: ${{ matrix.image }}
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- { name: gcc-10, comp: gcc, cxx: g++, image: "gcc:10" }
|
||||||
|
- { name: gcc-11, comp: gcc, cxx: g++, image: "gcc:11" }
|
||||||
|
- { name: gcc-12, comp: gcc, cxx: g++, image: "gcc:12" }
|
||||||
|
- { name: gcc-13, comp: gcc, cxx: g++, image: "gcc:13" }
|
||||||
|
- { name: gcc-14, comp: gcc, cxx: g++, image: "gcc:14" }
|
||||||
|
- { name: gcc-15, comp: gcc, cxx: g++, image: "gcc:15" }
|
||||||
|
|
||||||
|
# Using silkeh/clang for older versions
|
||||||
|
- { name: clang-10, comp: clang, cxx: clang++, image: "silkeh/clang:10", is_clang: true, ver: "10" }
|
||||||
|
- { name: clang-11, comp: clang, cxx: clang++, image: "silkeh/clang:11", is_clang: true, ver: "11" }
|
||||||
|
- { name: clang-12, comp: clang, cxx: clang++, image: "silkeh/clang:12", is_clang: true, ver: "12" }
|
||||||
|
- { name: clang-13, comp: clang, cxx: clang++, image: "silkeh/clang:13", is_clang: true, ver: "13" }
|
||||||
|
- { name: clang-14, comp: clang, cxx: clang++, image: "silkeh/clang:14", is_clang: true, ver: "14" }
|
||||||
|
- { name: clang-15, comp: clang, cxx: clang++, image: "silkeh/clang:15", is_clang: true, ver: "15" }
|
||||||
|
- { name: clang-16, comp: clang, cxx: clang++, image: "silkeh/clang:16", is_clang: true, ver: "16" }
|
||||||
|
- { name: clang-17, comp: clang, cxx: clang++, image: "silkeh/clang:17", is_clang: true, ver: "17" }
|
||||||
|
|
||||||
|
- { name: clang-18, comp: clang, cxx: clang++-18, image: "ubuntu:rolling", is_clang: true, ver: "18" }
|
||||||
|
- { name: clang-19, comp: clang, cxx: clang++-19, image: "ubuntu:rolling", is_clang: true, ver: "19" }
|
||||||
|
- { name: clang-20, comp: clang, cxx: clang++-20, image: "ubuntu:rolling", is_clang: true, ver: "20" }
|
||||||
|
- { name: clang-21, comp: clang, cxx: clang++-21, image: "ubuntu:rolling", is_clang: true, ver: "21" }
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
if grep -q "buster" /etc/os-release; then
|
||||||
|
echo "Debian Buster detected. Switching to archive repositories..."
|
||||||
|
echo "deb http://archive.debian.org/debian buster main contrib non-free" > /etc/apt/sources.list
|
||||||
|
echo "deb http://archive.debian.org/debian-security buster/updates main contrib non-free" >> /etc/apt/sources.list
|
||||||
|
echo 'Acquire::Check-Valid-Until "false";' > /etc/apt/apt.conf.d/99-ignore-valid-until
|
||||||
|
fi
|
||||||
|
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y curl git make
|
||||||
|
|
||||||
|
- name: Set up Clang
|
||||||
|
if: ${{ matrix.is_clang && matrix.image == 'ubuntu:rolling' }}
|
||||||
|
run: |
|
||||||
|
if [ "${{ matrix.ver }}" -le 20 ]; then
|
||||||
|
apt-get install -y clang-${{ matrix.ver }}
|
||||||
|
else
|
||||||
|
apt-get install -y \
|
||||||
|
clang-${{ matrix.ver }} \
|
||||||
|
llvm-${{ matrix.ver }}-dev \
|
||||||
|
llvm-${{ matrix.ver }}-linker-tools \
|
||||||
|
lld-${{ matrix.ver }}
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Download network
|
||||||
|
working-directory: src
|
||||||
|
run: make net
|
||||||
|
|
||||||
|
- name: Build avx2 binary
|
||||||
|
working-directory: src
|
||||||
|
run: |
|
||||||
|
export CXXFLAGS="-Werror"
|
||||||
|
if [ "${{ matrix.ver }}" -ge 20 ]; then
|
||||||
|
export LDFLAGS="-fuse-ld=lld"
|
||||||
|
apt install -y lld
|
||||||
|
fi
|
||||||
|
make clean
|
||||||
|
make -j build ARCH=x86-64-avx2 COMP=${{ matrix.comp }} COMPCXX=${{ matrix.cxx }}
|
||||||
|
|
||||||
|
- name: Smoke test
|
||||||
|
working-directory: src
|
||||||
|
run: ./stockfish bench 16 1 6
|
||||||
@@ -120,3 +120,5 @@ jobs:
|
|||||||
contents: write # For deleting/creating a (pre)release
|
contents: write # For deleting/creating a (pre)release
|
||||||
secrets:
|
secrets:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
CompilerCheck:
|
||||||
|
uses: ./.github/workflows/avx2_compilers.yml
|
||||||
|
|||||||
@@ -111,6 +111,7 @@ Hongzhi Cheng
|
|||||||
Ivan Ivec (IIvec)
|
Ivan Ivec (IIvec)
|
||||||
Jacques B. (Timshel)
|
Jacques B. (Timshel)
|
||||||
Jake Senne (w1wwwwww)
|
Jake Senne (w1wwwwww)
|
||||||
|
Jakub Ciolek (jake-ciolek)
|
||||||
Jan Ondruš (hxim)
|
Jan Ondruš (hxim)
|
||||||
Jared Kish (Kurtbusch, kurt22i)
|
Jared Kish (Kurtbusch, kurt22i)
|
||||||
Jarrod Torriero (DU-jdto)
|
Jarrod Torriero (DU-jdto)
|
||||||
|
|||||||
+4
-1
@@ -49,12 +49,15 @@ std::string Bitboards::pretty(Bitboard b) {
|
|||||||
|
|
||||||
std::string s = "+---+---+---+---+---+---+---+---+\n";
|
std::string s = "+---+---+---+---+---+---+---+---+\n";
|
||||||
|
|
||||||
for (Rank r = RANK_8; r >= RANK_1; --r)
|
for (Rank r = RANK_8;; --r)
|
||||||
{
|
{
|
||||||
for (File f = FILE_A; f <= FILE_H; ++f)
|
for (File f = FILE_A; f <= FILE_H; ++f)
|
||||||
s += b & make_square(f, r) ? "| X " : "| ";
|
s += b & make_square(f, r) ? "| X " : "| ";
|
||||||
|
|
||||||
s += "| " + std::to_string(1 + r) + "\n+---+---+---+---+---+---+---+---+\n";
|
s += "| " + std::to_string(1 + r) + "\n+---+---+---+---+---+---+---+---+\n";
|
||||||
|
|
||||||
|
if (r == RANK_1)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
s += " a b c d e f g h\n";
|
s += " a b c d e f g h\n";
|
||||||
|
|
||||||
|
|||||||
+13
-11
@@ -52,19 +52,21 @@ constexpr auto StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq
|
|||||||
constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048;
|
constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048;
|
||||||
int MaxThreads = std::max(1024, 4 * int(get_hardware_concurrency()));
|
int MaxThreads = std::max(1024, 4 * int(get_hardware_concurrency()));
|
||||||
|
|
||||||
|
// The default configuration will attempt to group L3 domains up to 32 threads.
|
||||||
|
// This size was found to be a good balance between the Elo gain of increased
|
||||||
|
// history sharing and the speed loss from more cross-cache accesses (see
|
||||||
|
// PR#6526). The user can always explicitly override this behavior.
|
||||||
|
constexpr NumaAutoPolicy DefaultNumaPolicy = BundledL3Policy{32};
|
||||||
|
|
||||||
Engine::Engine(std::optional<std::string> path) :
|
Engine::Engine(std::optional<std::string> path) :
|
||||||
binaryDirectory(path ? CommandLine::get_binary_directory(*path) : ""),
|
binaryDirectory(path ? CommandLine::get_binary_directory(*path) : ""),
|
||||||
numaContext(NumaConfig::from_system()),
|
numaContext(NumaConfig::from_system(DefaultNumaPolicy)),
|
||||||
states(new std::deque<StateInfo>(1)),
|
states(new std::deque<StateInfo>(1)),
|
||||||
threads(),
|
threads(),
|
||||||
networks(
|
networks(numaContext,
|
||||||
numaContext,
|
// Heap-allocate because sizeof(NN::Networks) is large
|
||||||
// Heap-allocate because sizeof(NN::Networks) is large
|
std::make_unique<NN::Networks>(NN::EvalFile{EvalFileDefaultNameBig, "None", ""},
|
||||||
std::make_unique<NN::Networks>(
|
NN::EvalFile{EvalFileDefaultNameSmall, "None", ""})) {
|
||||||
std::make_unique<NN::NetworkBig>(NN::EvalFile{EvalFileDefaultNameBig, "None", ""},
|
|
||||||
NN::EmbeddedNNUEType::BIG),
|
|
||||||
std::make_unique<NN::NetworkSmall>(NN::EvalFile{EvalFileDefaultNameSmall, "None", ""},
|
|
||||||
NN::EmbeddedNNUEType::SMALL))) {
|
|
||||||
|
|
||||||
pos.set(StartFEN, false, &states->back());
|
pos.set(StartFEN, false, &states->back());
|
||||||
|
|
||||||
@@ -217,12 +219,12 @@ void Engine::set_position(const std::string& fen, const std::vector<std::string>
|
|||||||
void Engine::set_numa_config_from_option(const std::string& o) {
|
void Engine::set_numa_config_from_option(const std::string& o) {
|
||||||
if (o == "auto" || o == "system")
|
if (o == "auto" || o == "system")
|
||||||
{
|
{
|
||||||
numaContext.set_numa_config(NumaConfig::from_system());
|
numaContext.set_numa_config(NumaConfig::from_system(DefaultNumaPolicy));
|
||||||
}
|
}
|
||||||
else if (o == "hardware")
|
else if (o == "hardware")
|
||||||
{
|
{
|
||||||
// Don't respect affinity set in the system.
|
// Don't respect affinity set in the system.
|
||||||
numaContext.set_numa_config(NumaConfig::from_system(false));
|
numaContext.set_numa_config(NumaConfig::from_system(DefaultNumaPolicy, false));
|
||||||
}
|
}
|
||||||
else if (o == "none")
|
else if (o == "none")
|
||||||
{
|
{
|
||||||
|
|||||||
+1
-1
@@ -40,7 +40,7 @@ namespace Stockfish {
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// Version number or dev.
|
// Version number or dev.
|
||||||
constexpr std::string_view version = "dev";
|
constexpr std::string_view version = "18";
|
||||||
|
|
||||||
// Our fancy logging facility. The trick here is to replace cin.rdbuf() and
|
// Our fancy logging facility. The trick here is to replace cin.rdbuf() and
|
||||||
// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We
|
// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We
|
||||||
|
|||||||
+28
-13
@@ -34,6 +34,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
#include <type_traits>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#define stringify2(x) #x
|
#define stringify2(x) #x
|
||||||
@@ -305,24 +306,38 @@ inline uint64_t mul_hi64(uint64_t a, uint64_t b) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline std::uint64_t hash_bytes(const char* data, std::size_t size) {
|
||||||
template<typename T>
|
// FNV-1a 64-bit
|
||||||
inline void hash_combine(std::size_t& seed, const T& v) {
|
const char* p = data;
|
||||||
std::hash<T> hasher;
|
std::uint64_t h = 14695981039346656037ull;
|
||||||
seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
for (std::size_t i = 0; i < size; ++i)
|
||||||
}
|
h = (h ^ p[i]) * 1099511628211ull;
|
||||||
|
return h;
|
||||||
template<>
|
|
||||||
inline void hash_combine(std::size_t& seed, const std::size_t& v) {
|
|
||||||
seed ^= v + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline std::size_t get_raw_data_hash(const T& value) {
|
inline std::size_t get_raw_data_hash(const T& value) {
|
||||||
return std::hash<std::string_view>{}(
|
// We must have no padding bytes because we're reinterpreting as char
|
||||||
std::string_view(reinterpret_cast<const char*>(&value), sizeof(value)));
|
static_assert(std::has_unique_object_representations<T>());
|
||||||
|
|
||||||
|
return static_cast<std::size_t>(
|
||||||
|
hash_bytes(reinterpret_cast<const char*>(&value), sizeof(value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void hash_combine(std::size_t& seed, const T& v) {
|
||||||
|
std::size_t x;
|
||||||
|
// For primitive types we avoid using the default hasher, which may be
|
||||||
|
// nondeterministic across program invocations
|
||||||
|
if constexpr (std::is_integral<T>())
|
||||||
|
x = v;
|
||||||
|
else
|
||||||
|
x = std::hash<T>{}(v);
|
||||||
|
seed ^= x + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::uint64_t hash_string(const std::string& sv) { return hash_bytes(sv.data(), sv.size()); }
|
||||||
|
|
||||||
template<std::size_t Capacity>
|
template<std::size_t Capacity>
|
||||||
class FixedString {
|
class FixedString {
|
||||||
public:
|
public:
|
||||||
@@ -450,7 +465,7 @@ void move_to_front(std::vector<T>& vec, Predicate pred) {
|
|||||||
template<std::size_t N>
|
template<std::size_t N>
|
||||||
struct std::hash<Stockfish::FixedString<N>> {
|
struct std::hash<Stockfish::FixedString<N>> {
|
||||||
std::size_t operator()(const Stockfish::FixedString<N>& fstr) const noexcept {
|
std::size_t operator()(const Stockfish::FixedString<N>& fstr) const noexcept {
|
||||||
return std::hash<std::string_view>{}((std::string_view) fstr);
|
return Stockfish::hash_bytes(fstr.data(), fstr.size());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -47,10 +47,10 @@ template<Direction offset>
|
|||||||
inline Move* splat_pawn_moves(Move* moveList, Bitboard to_bb) {
|
inline Move* splat_pawn_moves(Move* moveList, Bitboard to_bb) {
|
||||||
alignas(64) static constexpr auto SPLAT_TABLE = [] {
|
alignas(64) static constexpr auto SPLAT_TABLE = [] {
|
||||||
std::array<Move, 64> table{};
|
std::array<Move, 64> table{};
|
||||||
for (int8_t i = 0; i < 64; i++)
|
for (int i = 0; i < 64; i++)
|
||||||
{
|
{
|
||||||
Square from{std::clamp<int8_t>(i - offset, 0, 63)};
|
Square from{uint8_t(std::clamp(i - offset, 0, 63))};
|
||||||
table[i] = {Move(from, Square{i})};
|
table[i] = {Move(from, Square{uint8_t(i)})};
|
||||||
}
|
}
|
||||||
return table;
|
return table;
|
||||||
}();
|
}();
|
||||||
@@ -68,7 +68,7 @@ inline Move* splat_pawn_moves(Move* moveList, Bitboard to_bb) {
|
|||||||
inline Move* splat_moves(Move* moveList, Square from, Bitboard to_bb) {
|
inline Move* splat_moves(Move* moveList, Square from, Bitboard to_bb) {
|
||||||
alignas(64) static constexpr auto SPLAT_TABLE = [] {
|
alignas(64) static constexpr auto SPLAT_TABLE = [] {
|
||||||
std::array<Move, 64> table{};
|
std::array<Move, 64> table{};
|
||||||
for (int8_t i = 0; i < 64; i++)
|
for (uint8_t i = 0; i < 64; i++)
|
||||||
table[i] = {Move(SQUARE_ZERO, Square{i})};
|
table[i] = {Move(SQUARE_ZERO, Square{i})};
|
||||||
return table;
|
return table;
|
||||||
}();
|
}();
|
||||||
|
|||||||
@@ -37,26 +37,6 @@ struct HelperOffsets {
|
|||||||
int cumulativePieceOffset, cumulativeOffset;
|
int cumulativePieceOffset, cumulativeOffset;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Information on a particular pair of pieces and whether they should be excluded
|
|
||||||
struct PiecePairData {
|
|
||||||
// Layout: bits 8..31 are the index contribution of this piece pair, bits 0 and 1 are exclusion info
|
|
||||||
uint32_t data;
|
|
||||||
|
|
||||||
constexpr PiecePairData() :
|
|
||||||
data(0) {}
|
|
||||||
|
|
||||||
constexpr PiecePairData(bool excluded_pair,
|
|
||||||
bool semi_excluded_pair,
|
|
||||||
IndexType feature_index_base) :
|
|
||||||
data((uint32_t(excluded_pair) << 1) | (uint32_t(semi_excluded_pair && !excluded_pair))
|
|
||||||
| (uint32_t(feature_index_base) << 8)) {}
|
|
||||||
|
|
||||||
// lsb: excluded if from < to; 2nd lsb: always excluded
|
|
||||||
constexpr uint8_t excluded_pair_info() const { return static_cast<uint8_t>(data); }
|
|
||||||
|
|
||||||
constexpr IndexType feature_index_base() const { return static_cast<IndexType>(data >> 8); }
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr std::array<Piece, 12> AllPieces = {
|
constexpr std::array<Piece, 12> AllPieces = {
|
||||||
W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
|
W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
|
||||||
B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING,
|
B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING,
|
||||||
@@ -173,7 +153,7 @@ constexpr auto helper_offsets = init_threat_offsets().first;
|
|||||||
constexpr auto offsets = init_threat_offsets().second;
|
constexpr auto offsets = init_threat_offsets().second;
|
||||||
|
|
||||||
constexpr auto init_index_luts() {
|
constexpr auto init_index_luts() {
|
||||||
std::array<std::array<PiecePairData, PIECE_NB>, PIECE_NB> indices{};
|
std::array<std::array<std::array<uint32_t, 2>, PIECE_NB>, PIECE_NB> indices{};
|
||||||
|
|
||||||
for (Piece attacker : AllPieces)
|
for (Piece attacker : AllPieces)
|
||||||
{
|
{
|
||||||
@@ -189,8 +169,10 @@ constexpr auto init_index_luts() {
|
|||||||
+ (color_of(attacked) * (numValidTargets[attacker] / 2) + map)
|
+ (color_of(attacked) * (numValidTargets[attacker] / 2) + map)
|
||||||
* helper_offsets[attacker].cumulativePieceOffset;
|
* helper_offsets[attacker].cumulativePieceOffset;
|
||||||
|
|
||||||
bool excluded = map < 0;
|
bool excluded = map < 0;
|
||||||
indices[attacker][attacked] = PiecePairData(excluded, semi_excluded, feature);
|
indices[attacker][attacked][0] = excluded ? FullThreats::Dimensions : feature;
|
||||||
|
indices[attacker][attacked][1] =
|
||||||
|
excluded || semi_excluded ? FullThreats::Dimensions : feature;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,7 +182,7 @@ constexpr auto init_index_luts() {
|
|||||||
// The final index is calculated from summing data found in these two LUTs, as well
|
// The final index is calculated from summing data found in these two LUTs, as well
|
||||||
// as offsets[attacker][from]
|
// as offsets[attacker][from]
|
||||||
|
|
||||||
// [attacker][attacked]
|
// [attacker][attacked][from < to]
|
||||||
constexpr auto index_lut1 = init_index_luts();
|
constexpr auto index_lut1 = init_index_luts();
|
||||||
// [attacker][from][to]
|
// [attacker][from][to]
|
||||||
constexpr auto index_lut2 = index_lut2_array();
|
constexpr auto index_lut2 = index_lut2_array();
|
||||||
@@ -216,17 +198,9 @@ inline sf_always_inline IndexType FullThreats::make_index(
|
|||||||
unsigned attacker_oriented = attacker ^ swap;
|
unsigned attacker_oriented = attacker ^ swap;
|
||||||
unsigned attacked_oriented = attacked ^ swap;
|
unsigned attacked_oriented = attacked ^ swap;
|
||||||
|
|
||||||
const auto piecePairData = index_lut1[attacker_oriented][attacked_oriented];
|
return index_lut1[attacker_oriented][attacked_oriented][from_oriented < to_oriented]
|
||||||
|
+ offsets[attacker_oriented][from_oriented]
|
||||||
const bool less_than = from_oriented < to_oriented;
|
+ index_lut2[attacker_oriented][from_oriented][to_oriented];
|
||||||
if ((piecePairData.excluded_pair_info() + less_than) & 2)
|
|
||||||
return FullThreats::Dimensions;
|
|
||||||
|
|
||||||
const IndexType index = piecePairData.feature_index_base()
|
|
||||||
+ offsets[attacker_oriented][from_oriented]
|
|
||||||
+ index_lut2[attacker_oriented][from_oriented][to_oriented];
|
|
||||||
sf_assume(index < Dimensions);
|
|
||||||
return index;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a list of indices for active features in ascending order
|
// Get a list of indices for active features in ascending order
|
||||||
|
|||||||
+3
-4
@@ -28,7 +28,6 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include "../misc.h"
|
#include "../misc.h"
|
||||||
#include "../types.h"
|
#include "../types.h"
|
||||||
@@ -130,9 +129,9 @@ using NetworkSmall = Network<SmallNetworkArchitecture, SmallFeatureTransformer>;
|
|||||||
|
|
||||||
|
|
||||||
struct Networks {
|
struct Networks {
|
||||||
Networks(std::unique_ptr<NetworkBig>&& nB, std::unique_ptr<NetworkSmall>&& nS) :
|
Networks(EvalFile bigFile, EvalFile smallFile) :
|
||||||
big(std::move(*nB)),
|
big(bigFile, EmbeddedNNUEType::BIG),
|
||||||
small(std::move(*nS)) {}
|
small(smallFile, EmbeddedNNUEType::SMALL) {}
|
||||||
|
|
||||||
NetworkBig big;
|
NetworkBig big;
|
||||||
NetworkSmall small;
|
NetworkSmall small;
|
||||||
|
|||||||
+40
-36
@@ -169,52 +169,56 @@ inline void write_little_endian(std::ostream& stream, const IntType* values, std
|
|||||||
write_little_endian<IntType>(stream, values[i]);
|
write_little_endian<IntType>(stream, values[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 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 IntType, std::size_t Count>
|
template<typename BufType, typename IntType, std::size_t Count>
|
||||||
inline void read_leb_128(std::istream& stream, std::array<IntType, Count>& out) {
|
inline void read_leb_128_detail(std::istream& stream,
|
||||||
|
std::array<IntType, Count>& out,
|
||||||
|
std::uint32_t& bytes_left,
|
||||||
|
BufType& buf,
|
||||||
|
std::uint32_t& buf_pos) {
|
||||||
|
|
||||||
|
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");
|
||||||
|
|
||||||
|
IntType result = 0;
|
||||||
|
size_t shift = 0, i = 0;
|
||||||
|
while (i < Count)
|
||||||
|
{
|
||||||
|
if (buf_pos == buf.size())
|
||||||
|
{
|
||||||
|
stream.read(reinterpret_cast<char*>(buf.data()),
|
||||||
|
std::min(std::size_t(bytes_left), buf.size()));
|
||||||
|
buf_pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint8_t byte = buf[buf_pos++];
|
||||||
|
--bytes_left;
|
||||||
|
result |= (byte & 0x7f) << (shift % 32);
|
||||||
|
shift += 7;
|
||||||
|
|
||||||
|
if ((byte & 0x80) == 0)
|
||||||
|
{
|
||||||
|
out[i++] = (shift >= 32 || (byte & 0x40) == 0) ? result : result | ~((1 << shift) - 1);
|
||||||
|
result = 0;
|
||||||
|
shift = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Arrays>
|
||||||
|
inline void read_leb_128(std::istream& stream, Arrays&... outs) {
|
||||||
// Check the presence of our LEB128 magic string
|
// Check the presence of our LEB128 magic string
|
||||||
char leb128MagicString[Leb128MagicStringSize];
|
char leb128MagicString[Leb128MagicStringSize];
|
||||||
stream.read(leb128MagicString, Leb128MagicStringSize);
|
stream.read(leb128MagicString, Leb128MagicStringSize);
|
||||||
assert(strncmp(Leb128MagicString, leb128MagicString, Leb128MagicStringSize) == 0);
|
assert(strncmp(Leb128MagicString, leb128MagicString, Leb128MagicStringSize) == 0);
|
||||||
|
|
||||||
static_assert(std::is_signed_v<IntType>, "Not implemented for unsigned types");
|
auto bytes_left = read_little_endian<std::uint32_t>(stream);
|
||||||
|
std::array<std::uint8_t, 8192> buf;
|
||||||
|
std::uint32_t buf_pos = buf.size();
|
||||||
|
|
||||||
const std::uint32_t BUF_SIZE = 4096;
|
(read_leb_128_detail(stream, outs, bytes_left, buf, buf_pos), ...);
|
||||||
std::uint8_t buf[BUF_SIZE];
|
|
||||||
|
|
||||||
auto bytes_left = read_little_endian<std::uint32_t>(stream);
|
|
||||||
|
|
||||||
std::uint32_t buf_pos = BUF_SIZE;
|
|
||||||
for (std::size_t i = 0; i < Count; ++i)
|
|
||||||
{
|
|
||||||
IntType result = 0;
|
|
||||||
size_t shift = 0;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (buf_pos == BUF_SIZE)
|
|
||||||
{
|
|
||||||
stream.read(reinterpret_cast<char*>(buf), std::min(bytes_left, BUF_SIZE));
|
|
||||||
buf_pos = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint8_t byte = buf[buf_pos++];
|
|
||||||
--bytes_left;
|
|
||||||
result |= (byte & 0x7f) << shift;
|
|
||||||
shift += 7;
|
|
||||||
|
|
||||||
if ((byte & 0x80) == 0)
|
|
||||||
{
|
|
||||||
out[i] = (sizeof(IntType) * 8 <= shift || (byte & 0x40) == 0)
|
|
||||||
? result
|
|
||||||
: result | ~((1 << shift) - 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} while (shift < sizeof(IntType) * 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(bytes_left == 0);
|
assert(bytes_left == 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ class FeatureTransformer {
|
|||||||
permute<16>(biases, PackusEpi16Order);
|
permute<16>(biases, PackusEpi16Order);
|
||||||
permute<16>(weights, PackusEpi16Order);
|
permute<16>(weights, PackusEpi16Order);
|
||||||
|
|
||||||
if (UseThreats)
|
if constexpr (UseThreats)
|
||||||
permute<8>(threatWeights, PackusEpi16Order);
|
permute<8>(threatWeights, PackusEpi16Order);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,7 +140,7 @@ class FeatureTransformer {
|
|||||||
permute<16>(biases, InversePackusEpi16Order);
|
permute<16>(biases, InversePackusEpi16Order);
|
||||||
permute<16>(weights, InversePackusEpi16Order);
|
permute<16>(weights, InversePackusEpi16Order);
|
||||||
|
|
||||||
if (UseThreats)
|
if constexpr (UseThreats)
|
||||||
permute<8>(threatWeights, InversePackusEpi16Order);
|
permute<8>(threatWeights, InversePackusEpi16Order);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,40 +152,26 @@ class FeatureTransformer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read network parameters
|
// Read network parameters
|
||||||
// TODO: This is ugly. Currently LEB128 on the entire L1 necessitates
|
|
||||||
// reading the weights into a combined array, and then splitting.
|
|
||||||
bool read_parameters(std::istream& stream) {
|
bool read_parameters(std::istream& stream) {
|
||||||
read_leb_128<BiasType>(stream, biases);
|
read_leb_128(stream, biases);
|
||||||
|
|
||||||
if (UseThreats)
|
if constexpr (UseThreats)
|
||||||
{
|
{
|
||||||
read_little_endian<ThreatWeightType>(stream, threatWeights.data(),
|
read_little_endian<ThreatWeightType>(stream, threatWeights.data(),
|
||||||
ThreatInputDimensions * HalfDimensions);
|
ThreatInputDimensions * HalfDimensions);
|
||||||
read_leb_128<WeightType>(stream, weights);
|
read_leb_128(stream, weights);
|
||||||
|
|
||||||
auto combinedPsqtWeights =
|
read_leb_128(stream, threatPsqtWeights, psqtWeights);
|
||||||
std::make_unique<std::array<PSQTWeightType, TotalInputDimensions * PSQTBuckets>>();
|
|
||||||
|
|
||||||
read_leb_128<PSQTWeightType>(stream, *combinedPsqtWeights);
|
|
||||||
|
|
||||||
std::copy(combinedPsqtWeights->begin(),
|
|
||||||
combinedPsqtWeights->begin() + ThreatInputDimensions * PSQTBuckets,
|
|
||||||
std::begin(threatPsqtWeights));
|
|
||||||
|
|
||||||
std::copy(combinedPsqtWeights->begin() + ThreatInputDimensions * PSQTBuckets,
|
|
||||||
combinedPsqtWeights->begin()
|
|
||||||
+ (ThreatInputDimensions + InputDimensions) * PSQTBuckets,
|
|
||||||
std::begin(psqtWeights));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
read_leb_128<WeightType>(stream, weights);
|
read_leb_128(stream, weights);
|
||||||
read_leb_128<PSQTWeightType>(stream, psqtWeights);
|
read_leb_128(stream, psqtWeights);
|
||||||
}
|
}
|
||||||
|
|
||||||
permute_weights();
|
permute_weights();
|
||||||
|
|
||||||
if (!UseThreats)
|
if constexpr (!UseThreats)
|
||||||
scale_weights(true);
|
scale_weights(true);
|
||||||
|
|
||||||
return !stream.fail();
|
return !stream.fail();
|
||||||
@@ -197,12 +183,12 @@ class FeatureTransformer {
|
|||||||
|
|
||||||
copy->unpermute_weights();
|
copy->unpermute_weights();
|
||||||
|
|
||||||
if (!UseThreats)
|
if constexpr (!UseThreats)
|
||||||
copy->scale_weights(false);
|
copy->scale_weights(false);
|
||||||
|
|
||||||
write_leb_128<BiasType>(stream, copy->biases);
|
write_leb_128<BiasType>(stream, copy->biases);
|
||||||
|
|
||||||
if (UseThreats)
|
if constexpr (UseThreats)
|
||||||
{
|
{
|
||||||
write_little_endian<ThreatWeightType>(stream, copy->threatWeights.data(),
|
write_little_endian<ThreatWeightType>(stream, copy->threatWeights.data(),
|
||||||
ThreatInputDimensions * HalfDimensions);
|
ThreatInputDimensions * HalfDimensions);
|
||||||
@@ -256,7 +242,7 @@ class FeatureTransformer {
|
|||||||
auto psqt =
|
auto psqt =
|
||||||
(psqtAccumulation[perspectives[0]][bucket] - psqtAccumulation[perspectives[1]][bucket]);
|
(psqtAccumulation[perspectives[0]][bucket] - psqtAccumulation[perspectives[1]][bucket]);
|
||||||
|
|
||||||
if (UseThreats)
|
if constexpr (UseThreats)
|
||||||
{
|
{
|
||||||
const auto& threatPsqtAccumulation =
|
const auto& threatPsqtAccumulation =
|
||||||
(threatAccumulatorState.acc<HalfDimensions>()).psqtAccumulation;
|
(threatAccumulatorState.acc<HalfDimensions>()).psqtAccumulation;
|
||||||
@@ -348,7 +334,7 @@ class FeatureTransformer {
|
|||||||
#else
|
#else
|
||||||
6;
|
6;
|
||||||
#endif
|
#endif
|
||||||
if (UseThreats)
|
if constexpr (UseThreats)
|
||||||
{
|
{
|
||||||
const vec_t* tin0 =
|
const vec_t* tin0 =
|
||||||
reinterpret_cast<const vec_t*>(&(threatAccumulation[perspectives[p]][0]));
|
reinterpret_cast<const vec_t*>(&(threatAccumulation[perspectives[p]][0]));
|
||||||
@@ -400,7 +386,7 @@ class FeatureTransformer {
|
|||||||
BiasType sum1 =
|
BiasType sum1 =
|
||||||
accumulation[static_cast<int>(perspectives[p])][j + HalfDimensions / 2];
|
accumulation[static_cast<int>(perspectives[p])][j + HalfDimensions / 2];
|
||||||
|
|
||||||
if (UseThreats)
|
if constexpr (UseThreats)
|
||||||
{
|
{
|
||||||
BiasType sum0t = threatAccumulation[static_cast<int>(perspectives[p])][j + 0];
|
BiasType sum0t = threatAccumulation[static_cast<int>(perspectives[p])][j + 0];
|
||||||
BiasType sum1t =
|
BiasType sum1t =
|
||||||
|
|||||||
+334
-98
@@ -371,6 +371,50 @@ inline WindowsAffinity get_process_affinity() {
|
|||||||
return affinity;
|
return affinity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Type machinery used to emulate Cache->GroupCount
|
||||||
|
|
||||||
|
template<typename T, typename = void>
|
||||||
|
struct HasGroupCount: std::false_type {};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct HasGroupCount<T, std::void_t<decltype(std::declval<T>().Cache.GroupCount)>>: std::true_type {
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, typename Pred, std::enable_if_t<HasGroupCount<T>::value, bool> = true>
|
||||||
|
std::set<CpuIndex> readCacheMembers(const T* info, Pred&& is_cpu_allowed) {
|
||||||
|
std::set<CpuIndex> cpus;
|
||||||
|
// On Windows 10 this will read a 0 because GroupCount doesn't exist
|
||||||
|
int groupCount = std::max(info->Cache.GroupCount, WORD(1));
|
||||||
|
for (WORD procGroup = 0; procGroup < groupCount; ++procGroup)
|
||||||
|
{
|
||||||
|
for (BYTE number = 0; number < WIN_PROCESSOR_GROUP_SIZE; ++number)
|
||||||
|
{
|
||||||
|
WORD groupNumber = info->Cache.GroupMasks[procGroup].Group;
|
||||||
|
const CpuIndex c = static_cast<CpuIndex>(groupNumber) * WIN_PROCESSOR_GROUP_SIZE
|
||||||
|
+ static_cast<CpuIndex>(number);
|
||||||
|
if (!(info->Cache.GroupMasks[procGroup].Mask & (1ULL << number)) || !is_cpu_allowed(c))
|
||||||
|
continue;
|
||||||
|
cpus.insert(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cpus;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename Pred, std::enable_if_t<!HasGroupCount<T>::value, bool> = true>
|
||||||
|
std::set<CpuIndex> readCacheMembers(const T* info, Pred&& is_cpu_allowed) {
|
||||||
|
std::set<CpuIndex> cpus;
|
||||||
|
for (BYTE number = 0; number < WIN_PROCESSOR_GROUP_SIZE; ++number)
|
||||||
|
{
|
||||||
|
WORD groupNumber = info->Cache.GroupMask.Group;
|
||||||
|
const CpuIndex c = static_cast<CpuIndex>(groupNumber) * WIN_PROCESSOR_GROUP_SIZE
|
||||||
|
+ static_cast<CpuIndex>(number);
|
||||||
|
if (!(info->Cache.GroupMask.Mask & (1ULL << number)) || !is_cpu_allowed(c))
|
||||||
|
continue;
|
||||||
|
cpus.insert(c);
|
||||||
|
}
|
||||||
|
return cpus;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__linux__) && !defined(__ANDROID__)
|
#if defined(__linux__) && !defined(__ANDROID__)
|
||||||
@@ -447,14 +491,39 @@ class NumaReplicatedAccessToken {
|
|||||||
NumaIndex n;
|
NumaIndex n;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct L3Domain {
|
||||||
|
NumaIndex systemNumaIndex{};
|
||||||
|
std::set<CpuIndex> cpus{};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Use system NUMA nodes
|
||||||
|
struct SystemNumaPolicy {};
|
||||||
|
// Use system-reported L3 domains
|
||||||
|
struct L3DomainsPolicy {};
|
||||||
|
// Group system-reported L3 domains until they reach bundleSize
|
||||||
|
struct BundledL3Policy {
|
||||||
|
size_t bundleSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
using NumaAutoPolicy = std::variant<SystemNumaPolicy, L3DomainsPolicy, BundledL3Policy>;
|
||||||
|
|
||||||
// Designed as immutable, because there is no good reason to alter an already
|
// Designed as immutable, because there is no good reason to alter an already
|
||||||
// existing config in a way that doesn't require recreating it completely, and
|
// existing config in a way that doesn't require recreating it completely, and
|
||||||
// it would be complex and expensive to maintain class invariants.
|
// it would be complex and expensive to maintain class invariants.
|
||||||
// The CPU (processor) numbers always correspond to the actual numbering used
|
// The CPU (processor) numbers always correspond to the actual numbering used
|
||||||
// by the system. The NUMA node numbers MAY NOT correspond to the system's
|
// by the system. The NUMA node numbers MAY NOT correspond to the system's
|
||||||
// numbering of the NUMA nodes. In particular, empty nodes may be removed, or
|
// numbering of the NUMA nodes. In particular, by default, if the processor has
|
||||||
// the user may create custom nodes. It is guaranteed that NUMA nodes are NOT
|
// non-uniform cache access within a NUMA node (i.e., a non-unified L3 cache structure),
|
||||||
// empty: every node exposed by NumaConfig has at least one processor assigned.
|
// then L3 domains within a system NUMA node will be used to subdivide it
|
||||||
|
// into multiple logical NUMA nodes in the config. Additionally, empty nodes may
|
||||||
|
// be removed, or the user may create custom nodes.
|
||||||
|
//
|
||||||
|
// As a special case, when performing system-wide replication of read-only data
|
||||||
|
// (i.e., LazyNumaReplicatedSystemWide), the system NUMA node is used, rather than
|
||||||
|
// custom or L3-aware nodes. See that class's get_discriminator() function.
|
||||||
|
//
|
||||||
|
// It is guaranteed that NUMA nodes are NOT empty: every node exposed by NumaConfig
|
||||||
|
// has at least one processor assigned.
|
||||||
//
|
//
|
||||||
// We use startup affinities so as not to modify its own behaviour in time.
|
// We use startup affinities so as not to modify its own behaviour in time.
|
||||||
//
|
//
|
||||||
@@ -469,78 +538,19 @@ class NumaConfig {
|
|||||||
add_cpu_range_to_node(NumaIndex{0}, CpuIndex{0}, numCpus - 1);
|
add_cpu_range_to_node(NumaIndex{0}, CpuIndex{0}, numCpus - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function queries the system for the mapping of processors to NUMA nodes.
|
// This function gets a NumaConfig based on the system's provided information.
|
||||||
// On Linux we read from standardized kernel sysfs, with a fallback to single NUMA
|
// The available policies are documented above.
|
||||||
// node. On Windows we utilize GetNumaProcessorNodeEx, which has its quirks, see
|
static NumaConfig from_system([[maybe_unused]] const NumaAutoPolicy& policy,
|
||||||
// comment for Windows implementation of get_process_affinity.
|
bool respectProcessAffinity = true) {
|
||||||
static NumaConfig from_system([[maybe_unused]] bool respectProcessAffinity = true) {
|
|
||||||
NumaConfig cfg = empty();
|
NumaConfig cfg = empty();
|
||||||
|
|
||||||
#if defined(__linux__) && !defined(__ANDROID__)
|
#if !((defined(__linux__) && !defined(__ANDROID__)) || defined(_WIN64))
|
||||||
|
// Fallback for unsupported systems.
|
||||||
|
for (CpuIndex c = 0; c < SYSTEM_THREADS_NB; ++c)
|
||||||
|
cfg.add_cpu_to_node(NumaIndex{0}, c);
|
||||||
|
#else
|
||||||
|
|
||||||
std::set<CpuIndex> allowedCpus;
|
#if defined(_WIN64)
|
||||||
|
|
||||||
if (respectProcessAffinity)
|
|
||||||
allowedCpus = STARTUP_PROCESSOR_AFFINITY;
|
|
||||||
|
|
||||||
auto is_cpu_allowed = [respectProcessAffinity, &allowedCpus](CpuIndex c) {
|
|
||||||
return !respectProcessAffinity || allowedCpus.count(c) == 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
// On Linux things are straightforward, since there's no processor groups and
|
|
||||||
// any thread can be scheduled on all processors.
|
|
||||||
// We try to gather this information from the sysfs first
|
|
||||||
// https://www.kernel.org/doc/Documentation/ABI/stable/sysfs-devices-node
|
|
||||||
|
|
||||||
bool useFallback = false;
|
|
||||||
auto fallback = [&]() {
|
|
||||||
useFallback = true;
|
|
||||||
cfg = empty();
|
|
||||||
};
|
|
||||||
|
|
||||||
// /sys/devices/system/node/online contains information about active NUMA nodes
|
|
||||||
auto nodeIdsStr = read_file_to_string("/sys/devices/system/node/online");
|
|
||||||
if (!nodeIdsStr.has_value() || nodeIdsStr->empty())
|
|
||||||
{
|
|
||||||
fallback();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
remove_whitespace(*nodeIdsStr);
|
|
||||||
for (size_t n : indices_from_shortened_string(*nodeIdsStr))
|
|
||||||
{
|
|
||||||
// /sys/devices/system/node/node.../cpulist
|
|
||||||
std::string path =
|
|
||||||
std::string("/sys/devices/system/node/node") + std::to_string(n) + "/cpulist";
|
|
||||||
auto cpuIdsStr = read_file_to_string(path);
|
|
||||||
// Now, we only bail if the file does not exist. Some nodes may be
|
|
||||||
// empty, that's fine. An empty node still has a file that appears
|
|
||||||
// to have some whitespace, so we need to handle that.
|
|
||||||
if (!cpuIdsStr.has_value())
|
|
||||||
{
|
|
||||||
fallback();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
remove_whitespace(*cpuIdsStr);
|
|
||||||
for (size_t c : indices_from_shortened_string(*cpuIdsStr))
|
|
||||||
{
|
|
||||||
if (is_cpu_allowed(c))
|
|
||||||
cfg.add_cpu_to_node(n, c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (useFallback)
|
|
||||||
{
|
|
||||||
for (CpuIndex c = 0; c < SYSTEM_THREADS_NB; ++c)
|
|
||||||
if (is_cpu_allowed(c))
|
|
||||||
cfg.add_cpu_to_node(NumaIndex{0}, c);
|
|
||||||
}
|
|
||||||
|
|
||||||
#elif defined(_WIN64)
|
|
||||||
|
|
||||||
std::optional<std::set<CpuIndex>> allowedCpus;
|
std::optional<std::set<CpuIndex>> allowedCpus;
|
||||||
|
|
||||||
@@ -555,28 +565,38 @@ class NumaConfig {
|
|||||||
return !allowedCpus.has_value() || allowedCpus->count(c) == 1;
|
return !allowedCpus.has_value() || allowedCpus->count(c) == 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
WORD numProcGroups = GetActiveProcessorGroupCount();
|
#elif defined(__linux__) && !defined(__ANDROID__)
|
||||||
for (WORD procGroup = 0; procGroup < numProcGroups; ++procGroup)
|
|
||||||
{
|
|
||||||
for (BYTE number = 0; number < WIN_PROCESSOR_GROUP_SIZE; ++number)
|
|
||||||
{
|
|
||||||
PROCESSOR_NUMBER procnum;
|
|
||||||
procnum.Group = procGroup;
|
|
||||||
procnum.Number = number;
|
|
||||||
procnum.Reserved = 0;
|
|
||||||
USHORT nodeNumber;
|
|
||||||
|
|
||||||
const BOOL status = GetNumaProcessorNodeEx(&procnum, &nodeNumber);
|
std::set<CpuIndex> allowedCpus;
|
||||||
const CpuIndex c = static_cast<CpuIndex>(procGroup) * WIN_PROCESSOR_GROUP_SIZE
|
|
||||||
+ static_cast<CpuIndex>(number);
|
if (respectProcessAffinity)
|
||||||
if (status != 0 && nodeNumber != std::numeric_limits<USHORT>::max()
|
allowedCpus = STARTUP_PROCESSOR_AFFINITY;
|
||||||
&& is_cpu_allowed(c))
|
|
||||||
{
|
auto is_cpu_allowed = [respectProcessAffinity, &allowedCpus](CpuIndex c) {
|
||||||
cfg.add_cpu_to_node(nodeNumber, c);
|
return !respectProcessAffinity || allowedCpus.count(c) == 1;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool l3Success = false;
|
||||||
|
if (!std::holds_alternative<SystemNumaPolicy>(policy))
|
||||||
|
{
|
||||||
|
size_t l3BundleSize = 0;
|
||||||
|
if (const auto* v = std::get_if<BundledL3Policy>(&policy))
|
||||||
|
{
|
||||||
|
l3BundleSize = v->bundleSize;
|
||||||
|
}
|
||||||
|
if (auto l3Cfg =
|
||||||
|
try_get_l3_aware_config(respectProcessAffinity, l3BundleSize, is_cpu_allowed))
|
||||||
|
{
|
||||||
|
cfg = std::move(*l3Cfg);
|
||||||
|
l3Success = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!l3Success)
|
||||||
|
cfg = from_system_numa(respectProcessAffinity, is_cpu_allowed);
|
||||||
|
|
||||||
|
#if defined(_WIN64)
|
||||||
// Split the NUMA nodes to be contained within a group if necessary.
|
// Split the NUMA nodes to be contained within a group if necessary.
|
||||||
// This is needed between Windows 10 Build 20348 and Windows 11, because
|
// This is needed between Windows 10 Build 20348 and Windows 11, because
|
||||||
// the new NUMA allocation behaviour was introduced while there was
|
// the new NUMA allocation behaviour was introduced while there was
|
||||||
@@ -623,12 +643,7 @@ class NumaConfig {
|
|||||||
|
|
||||||
cfg = std::move(splitCfg);
|
cfg = std::move(splitCfg);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
#else
|
|
||||||
|
|
||||||
// Fallback for unsupported systems.
|
|
||||||
for (CpuIndex c = 0; c < SYSTEM_THREADS_NB; ++c)
|
|
||||||
cfg.add_cpu_to_node(NumaIndex{0}, c);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -1041,6 +1056,227 @@ class NumaConfig {
|
|||||||
|
|
||||||
return indices;
|
return indices;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This function queries the system for the mapping of processors to NUMA nodes.
|
||||||
|
// On Linux we read from standardized kernel sysfs, with a fallback to single NUMA
|
||||||
|
// node. On Windows we utilize GetNumaProcessorNodeEx, which has its quirks, see
|
||||||
|
// comment for Windows implementation of get_process_affinity.
|
||||||
|
template<typename Pred>
|
||||||
|
static NumaConfig from_system_numa([[maybe_unused]] bool respectProcessAffinity,
|
||||||
|
[[maybe_unused]] Pred&& is_cpu_allowed) {
|
||||||
|
NumaConfig cfg = empty();
|
||||||
|
|
||||||
|
#if defined(__linux__) && !defined(__ANDROID__)
|
||||||
|
|
||||||
|
// On Linux things are straightforward, since there's no processor groups and
|
||||||
|
// any thread can be scheduled on all processors.
|
||||||
|
// We try to gather this information from the sysfs first
|
||||||
|
// https://www.kernel.org/doc/Documentation/ABI/stable/sysfs-devices-node
|
||||||
|
|
||||||
|
bool useFallback = false;
|
||||||
|
auto fallback = [&]() {
|
||||||
|
useFallback = true;
|
||||||
|
cfg = empty();
|
||||||
|
};
|
||||||
|
|
||||||
|
// /sys/devices/system/node/online contains information about active NUMA nodes
|
||||||
|
auto nodeIdsStr = read_file_to_string("/sys/devices/system/node/online");
|
||||||
|
if (!nodeIdsStr.has_value() || nodeIdsStr->empty())
|
||||||
|
{
|
||||||
|
fallback();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove_whitespace(*nodeIdsStr);
|
||||||
|
for (size_t n : indices_from_shortened_string(*nodeIdsStr))
|
||||||
|
{
|
||||||
|
// /sys/devices/system/node/node.../cpulist
|
||||||
|
std::string path =
|
||||||
|
std::string("/sys/devices/system/node/node") + std::to_string(n) + "/cpulist";
|
||||||
|
auto cpuIdsStr = read_file_to_string(path);
|
||||||
|
// Now, we only bail if the file does not exist. Some nodes may be
|
||||||
|
// empty, that's fine. An empty node still has a file that appears
|
||||||
|
// to have some whitespace, so we need to handle that.
|
||||||
|
if (!cpuIdsStr.has_value())
|
||||||
|
{
|
||||||
|
fallback();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove_whitespace(*cpuIdsStr);
|
||||||
|
for (size_t c : indices_from_shortened_string(*cpuIdsStr))
|
||||||
|
{
|
||||||
|
if (is_cpu_allowed(c))
|
||||||
|
cfg.add_cpu_to_node(n, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useFallback)
|
||||||
|
{
|
||||||
|
for (CpuIndex c = 0; c < SYSTEM_THREADS_NB; ++c)
|
||||||
|
if (is_cpu_allowed(c))
|
||||||
|
cfg.add_cpu_to_node(NumaIndex{0}, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(_WIN64)
|
||||||
|
|
||||||
|
WORD numProcGroups = GetActiveProcessorGroupCount();
|
||||||
|
for (WORD procGroup = 0; procGroup < numProcGroups; ++procGroup)
|
||||||
|
{
|
||||||
|
for (BYTE number = 0; number < WIN_PROCESSOR_GROUP_SIZE; ++number)
|
||||||
|
{
|
||||||
|
PROCESSOR_NUMBER procnum;
|
||||||
|
procnum.Group = procGroup;
|
||||||
|
procnum.Number = number;
|
||||||
|
procnum.Reserved = 0;
|
||||||
|
USHORT nodeNumber;
|
||||||
|
|
||||||
|
const BOOL status = GetNumaProcessorNodeEx(&procnum, &nodeNumber);
|
||||||
|
const CpuIndex c = static_cast<CpuIndex>(procGroup) * WIN_PROCESSOR_GROUP_SIZE
|
||||||
|
+ static_cast<CpuIndex>(number);
|
||||||
|
if (status != 0 && nodeNumber != std::numeric_limits<USHORT>::max()
|
||||||
|
&& is_cpu_allowed(c))
|
||||||
|
{
|
||||||
|
cfg.add_cpu_to_node(nodeNumber, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
abort(); // should not reach here
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return cfg;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Pred>
|
||||||
|
static std::optional<NumaConfig> try_get_l3_aware_config(
|
||||||
|
bool respectProcessAffinity, size_t bundleSize, [[maybe_unused]] Pred&& is_cpu_allowed) {
|
||||||
|
// Get the normal system configuration so we know to which NUMA node
|
||||||
|
// each L3 domain belongs.
|
||||||
|
NumaConfig systemConfig =
|
||||||
|
NumaConfig::from_system(SystemNumaPolicy{}, respectProcessAffinity);
|
||||||
|
std::vector<L3Domain> l3Domains;
|
||||||
|
|
||||||
|
#if defined(__linux__) && !defined(__ANDROID__)
|
||||||
|
|
||||||
|
std::set<CpuIndex> seenCpus;
|
||||||
|
auto nextUnseenCpu = [&seenCpus]() {
|
||||||
|
for (CpuIndex i = 0;; ++i)
|
||||||
|
if (!seenCpus.count(i))
|
||||||
|
return i;
|
||||||
|
};
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
CpuIndex next = nextUnseenCpu();
|
||||||
|
auto siblingsStr =
|
||||||
|
read_file_to_string("/sys/devices/system/cpu/cpu" + std::to_string(next)
|
||||||
|
+ "/cache/index3/shared_cpu_list");
|
||||||
|
|
||||||
|
if (!siblingsStr.has_value() || siblingsStr->empty())
|
||||||
|
{
|
||||||
|
break; // we have read all available CPUs
|
||||||
|
}
|
||||||
|
|
||||||
|
L3Domain domain;
|
||||||
|
for (size_t c : indices_from_shortened_string(*siblingsStr))
|
||||||
|
{
|
||||||
|
if (is_cpu_allowed(c))
|
||||||
|
{
|
||||||
|
domain.systemNumaIndex = systemConfig.nodeByCpu.at(c);
|
||||||
|
domain.cpus.insert(c);
|
||||||
|
}
|
||||||
|
seenCpus.insert(c);
|
||||||
|
}
|
||||||
|
if (!domain.cpus.empty())
|
||||||
|
{
|
||||||
|
l3Domains.emplace_back(std::move(domain));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(_WIN64)
|
||||||
|
|
||||||
|
DWORD bufSize = 0;
|
||||||
|
GetLogicalProcessorInformationEx(RelationCache, nullptr, &bufSize);
|
||||||
|
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
std::vector<char> buffer(bufSize);
|
||||||
|
auto info = reinterpret_cast<PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX>(buffer.data());
|
||||||
|
if (!GetLogicalProcessorInformationEx(RelationCache, info, &bufSize))
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
while (reinterpret_cast<char*>(info) < buffer.data() + bufSize)
|
||||||
|
{
|
||||||
|
info = std::launder(info);
|
||||||
|
if (info->Relationship == RelationCache && info->Cache.Level == 3)
|
||||||
|
{
|
||||||
|
L3Domain domain{};
|
||||||
|
domain.cpus = readCacheMembers(info, is_cpu_allowed);
|
||||||
|
if (!domain.cpus.empty())
|
||||||
|
{
|
||||||
|
domain.systemNumaIndex = systemConfig.nodeByCpu.at(*domain.cpus.begin());
|
||||||
|
l3Domains.push_back(std::move(domain));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Variable length data structure, advance to next
|
||||||
|
info = reinterpret_cast<PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX>(
|
||||||
|
reinterpret_cast<char*>(info) + info->Size);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!l3Domains.empty())
|
||||||
|
return {NumaConfig::from_l3_info(std::move(l3Domains), bundleSize)};
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static NumaConfig from_l3_info(std::vector<L3Domain>&& domains, size_t bundleSize) {
|
||||||
|
assert(!domains.empty());
|
||||||
|
|
||||||
|
std::map<NumaIndex, std::vector<L3Domain>> list;
|
||||||
|
for (auto& d : domains)
|
||||||
|
list[d.systemNumaIndex].emplace_back(std::move(d));
|
||||||
|
|
||||||
|
NumaConfig cfg = empty();
|
||||||
|
NumaIndex n = 0;
|
||||||
|
for (auto& [_, ds] : list)
|
||||||
|
{
|
||||||
|
bool changed;
|
||||||
|
// Scan through pairs and merge them. With roughly equal L3 sizes, should give
|
||||||
|
// a decent distribution.
|
||||||
|
do
|
||||||
|
{
|
||||||
|
changed = false;
|
||||||
|
for (size_t j = 0; j + 1 < ds.size(); ++j)
|
||||||
|
{
|
||||||
|
if (ds[j].cpus.size() + ds[j + 1].cpus.size() <= bundleSize)
|
||||||
|
{
|
||||||
|
changed = true;
|
||||||
|
ds[j].cpus.merge(ds[j + 1].cpus);
|
||||||
|
ds.erase(ds.begin() + j + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ds.size() has decreased if changed is true, so this loop will terminate
|
||||||
|
} while (changed);
|
||||||
|
for (const L3Domain& d : ds)
|
||||||
|
{
|
||||||
|
const NumaIndex dn = n++;
|
||||||
|
for (CpuIndex cpu : d.cpus)
|
||||||
|
{
|
||||||
|
cfg.add_cpu_to_node(dn, cpu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cfg;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class NumaReplicationContext;
|
class NumaReplicationContext;
|
||||||
@@ -1345,12 +1581,12 @@ class LazyNumaReplicatedSystemWide: public NumaReplicatedBase {
|
|||||||
|
|
||||||
std::size_t get_discriminator(NumaIndex idx) const {
|
std::size_t get_discriminator(NumaIndex idx) const {
|
||||||
const NumaConfig& cfg = get_numa_config();
|
const NumaConfig& cfg = get_numa_config();
|
||||||
const NumaConfig& cfg_sys = NumaConfig::from_system(false);
|
const NumaConfig& cfg_sys = NumaConfig::from_system(SystemNumaPolicy{}, false);
|
||||||
// as a discriminator, locate the hardware/system numadomain this cpuindex belongs to
|
// as a discriminator, locate the hardware/system numadomain this cpuindex belongs to
|
||||||
CpuIndex cpu = *cfg.nodes[idx].begin(); // get a CpuIndex from NumaIndex
|
CpuIndex cpu = *cfg.nodes[idx].begin(); // get a CpuIndex from NumaIndex
|
||||||
NumaIndex sys_idx = cfg_sys.is_cpu_assigned(cpu) ? cfg_sys.nodeByCpu.at(cpu) : 0;
|
NumaIndex sys_idx = cfg_sys.is_cpu_assigned(cpu) ? cfg_sys.nodeByCpu.at(cpu) : 0;
|
||||||
std::string s = cfg_sys.to_string() + "$" + std::to_string(sys_idx);
|
std::string s = cfg_sys.to_string() + "$" + std::to_string(sys_idx);
|
||||||
return std::hash<std::string>{}(s);
|
return static_cast<std::size_t>(hash_string(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ensure_present(NumaIndex idx) const {
|
void ensure_present(NumaIndex idx) const {
|
||||||
|
|||||||
+12
-5
@@ -66,12 +66,15 @@ std::ostream& operator<<(std::ostream& os, const Position& pos) {
|
|||||||
|
|
||||||
os << "\n +---+---+---+---+---+---+---+---+\n";
|
os << "\n +---+---+---+---+---+---+---+---+\n";
|
||||||
|
|
||||||
for (Rank r = RANK_8; r >= RANK_1; --r)
|
for (Rank r = RANK_8;; --r)
|
||||||
{
|
{
|
||||||
for (File f = FILE_A; f <= FILE_H; ++f)
|
for (File f = FILE_A; f <= FILE_H; ++f)
|
||||||
os << " | " << PieceToChar[pos.piece_on(make_square(f, r))];
|
os << " | " << PieceToChar[pos.piece_on(make_square(f, r))];
|
||||||
|
|
||||||
os << " | " << (1 + r) << "\n +---+---+---+---+---+---+---+---+\n";
|
os << " | " << (1 + r) << "\n +---+---+---+---+---+---+---+---+\n";
|
||||||
|
|
||||||
|
if (r == RANK_1)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
os << " a b c d e f g h\n"
|
os << " a b c d e f g h\n"
|
||||||
@@ -416,7 +419,7 @@ string Position::fen() const {
|
|||||||
int emptyCnt;
|
int emptyCnt;
|
||||||
std::ostringstream ss;
|
std::ostringstream ss;
|
||||||
|
|
||||||
for (Rank r = RANK_8; r >= RANK_1; --r)
|
for (Rank r = RANK_8;; --r)
|
||||||
{
|
{
|
||||||
for (File f = FILE_A; f <= FILE_H; ++f)
|
for (File f = FILE_A; f <= FILE_H; ++f)
|
||||||
{
|
{
|
||||||
@@ -430,8 +433,9 @@ string Position::fen() const {
|
|||||||
ss << PieceToChar[piece_on(make_square(f, r))];
|
ss << PieceToChar[piece_on(make_square(f, r))];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (r > RANK_1)
|
if (r == RANK_1)
|
||||||
ss << '/';
|
break;
|
||||||
|
ss << '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
ss << (sideToMove == WHITE ? " w " : " b ");
|
ss << (sideToMove == WHITE ? " w " : " b ");
|
||||||
@@ -1477,10 +1481,13 @@ void Position::flip() {
|
|||||||
string f, token;
|
string f, token;
|
||||||
std::stringstream ss(fen());
|
std::stringstream ss(fen());
|
||||||
|
|
||||||
for (Rank r = RANK_8; r >= RANK_1; --r) // Piece placement
|
for (Rank r = RANK_8;; --r) // Piece placement
|
||||||
{
|
{
|
||||||
std::getline(ss, token, r > RANK_1 ? '/' : ' ');
|
std::getline(ss, token, r > RANK_1 ? '/' : ' ');
|
||||||
f.insert(0, token + (f.empty() ? " " : "/"));
|
f.insert(0, token + (f.empty() ? " " : "/"));
|
||||||
|
|
||||||
|
if (r == RANK_1)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ss >> token; // Active color
|
ss >> token; // Active color
|
||||||
|
|||||||
+4
-5
@@ -1206,8 +1206,7 @@ moves_loop: // When in check, search starts here
|
|||||||
|
|
||||||
// Increase reduction if next ply has a lot of fail high
|
// Increase reduction if next ply has a lot of fail high
|
||||||
if ((ss + 1)->cutoffCnt > 1)
|
if ((ss + 1)->cutoffCnt > 1)
|
||||||
r += 120 + 1024 * ((ss + 1)->cutoffCnt > 2) + 100 * ((ss + 1)->cutoffCnt > 3)
|
r += 256 + 1024 * ((ss + 1)->cutoffCnt > 2) + 1024 * allNode;
|
||||||
+ 1024 * allNode;
|
|
||||||
|
|
||||||
// For first picked move (ttMove) reduce reduction
|
// For first picked move (ttMove) reduce reduction
|
||||||
if (move == ttData.move)
|
if (move == ttData.move)
|
||||||
@@ -1433,6 +1432,7 @@ moves_loop: // When in check, search starts here
|
|||||||
|
|
||||||
bonusScale = std::max(bonusScale, 0);
|
bonusScale = std::max(bonusScale, 0);
|
||||||
|
|
||||||
|
// scaledBonus ranges from 0 to roughly 2.3M, overflows happen for multipliers larger than 900
|
||||||
const int scaledBonus = std::min(141 * depth - 87, 1351) * bonusScale;
|
const int scaledBonus = std::min(141 * depth - 87, 1351) * bonusScale;
|
||||||
|
|
||||||
update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq,
|
update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq,
|
||||||
@@ -1441,8 +1441,7 @@ moves_loop: // When in check, search starts here
|
|||||||
mainHistory[~us][((ss - 1)->currentMove).raw()] << scaledBonus * 243 / 32768;
|
mainHistory[~us][((ss - 1)->currentMove).raw()] << scaledBonus * 243 / 32768;
|
||||||
|
|
||||||
if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION)
|
if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION)
|
||||||
sharedHistory.pawn_entry(pos)[pos.piece_on(prevSq)][prevSq]
|
sharedHistory.pawn_entry(pos)[pos.piece_on(prevSq)][prevSq] << scaledBonus * 290 / 8192;
|
||||||
<< scaledBonus * 1160 / 32768;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bonus for prior capture countermove that caused the fail low
|
// Bonus for prior capture countermove that caused the fail low
|
||||||
@@ -1653,7 +1652,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta)
|
|||||||
// we can prune this move.
|
// we can prune this move.
|
||||||
if (!pos.see_ge(move, alpha - futilityBase))
|
if (!pos.see_ge(move, alpha - futilityBase))
|
||||||
{
|
{
|
||||||
bestValue = std::min(alpha, futilityBase);
|
bestValue = std::max(bestValue, std::min(alpha, futilityBase));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#define SHM_H_INCLUDED
|
#define SHM_H_INCLUDED
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cinttypes>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@@ -35,7 +36,7 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
|
||||||
#if !defined(_WIN32) && !defined(__ANDROID__)
|
#if defined(__linux__) && !defined(__ANDROID__)
|
||||||
#include "shm_linux.h"
|
#include "shm_linux.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -59,7 +60,7 @@
|
|||||||
#define NOMINMAX
|
#define NOMINMAX
|
||||||
#endif
|
#endif
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#else
|
#elif defined(__linux__)
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
@@ -406,7 +407,7 @@ class SharedMemoryBackend {
|
|||||||
std::string last_error_message;
|
std::string last_error_message;
|
||||||
};
|
};
|
||||||
|
|
||||||
#elif !defined(__ANDROID__)
|
#elif defined(__linux__) && !defined(__ANDROID__)
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class SharedMemoryBackend {
|
class SharedMemoryBackend {
|
||||||
@@ -512,12 +513,9 @@ template<typename T>
|
|||||||
struct SystemWideSharedConstant {
|
struct SystemWideSharedConstant {
|
||||||
private:
|
private:
|
||||||
static std::string createHashString(const std::string& input) {
|
static std::string createHashString(const std::string& input) {
|
||||||
size_t hash = std::hash<std::string>{}(input);
|
char buf[1024];
|
||||||
|
std::snprintf(buf, sizeof(buf), "%016" PRIx64, hash_string(input));
|
||||||
std::stringstream ss;
|
return buf;
|
||||||
ss << std::hex << std::setfill('0') << hash;
|
|
||||||
|
|
||||||
return ss.str();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -534,13 +532,13 @@ struct SystemWideSharedConstant {
|
|||||||
// that are not present in the content, for example NUMA node allocation.
|
// that are not present in the content, for example NUMA node allocation.
|
||||||
SystemWideSharedConstant(const T& value, std::size_t discriminator = 0) {
|
SystemWideSharedConstant(const T& value, std::size_t discriminator = 0) {
|
||||||
std::size_t content_hash = std::hash<T>{}(value);
|
std::size_t content_hash = std::hash<T>{}(value);
|
||||||
std::size_t executable_hash = std::hash<std::string>{}(getExecutablePathHash());
|
std::size_t executable_hash = hash_string(getExecutablePathHash());
|
||||||
|
|
||||||
std::string shm_name = std::string("Local\\sf_") + std::to_string(content_hash) + "$"
|
char buf[1024];
|
||||||
+ std::to_string(executable_hash) + "$"
|
std::snprintf(buf, sizeof(buf), "Local\\sf_%zu$%zu$%zu", content_hash, executable_hash, discriminator);
|
||||||
+ std::to_string(discriminator);
|
std::string shm_name = buf;
|
||||||
|
|
||||||
#if !defined(_WIN32)
|
#if defined(__linux__) && !defined(__ANDROID__)
|
||||||
// POSIX shared memory names must start with a slash
|
// POSIX shared memory names must start with a slash
|
||||||
shm_name = "/sf_" + createHashString(shm_name);
|
shm_name = "/sf_" + createHashString(shm_name);
|
||||||
|
|
||||||
|
|||||||
+43
-30
@@ -19,6 +19,10 @@
|
|||||||
#ifndef SHM_LINUX_H_INCLUDED
|
#ifndef SHM_LINUX_H_INCLUDED
|
||||||
#define SHM_LINUX_H_INCLUDED
|
#define SHM_LINUX_H_INCLUDED
|
||||||
|
|
||||||
|
#if !defined(__linux__) || defined(__ANDROID__)
|
||||||
|
#error shm_linux.h should not be included on this platform.
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
@@ -33,7 +37,6 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <unordered_set>
|
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
@@ -41,16 +44,10 @@
|
|||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#define SF_MAX_SEM_NAME_LEN NAME_MAX
|
||||||
|
|
||||||
#if defined(__NetBSD__) || defined(__DragonFly__) || defined(__linux__)
|
#include "misc.h"
|
||||||
#include <limits.h>
|
|
||||||
#define SF_MAX_SEM_NAME_LEN NAME_MAX
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
#define SF_MAX_SEM_NAME_LEN 31
|
|
||||||
#else
|
|
||||||
#define SF_MAX_SEM_NAME_LEN 255
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
namespace Stockfish::shm {
|
namespace Stockfish::shm {
|
||||||
|
|
||||||
@@ -67,48 +64,60 @@ struct ShmHeader {
|
|||||||
class SharedMemoryBase {
|
class SharedMemoryBase {
|
||||||
public:
|
public:
|
||||||
virtual ~SharedMemoryBase() = default;
|
virtual ~SharedMemoryBase() = default;
|
||||||
virtual void close() noexcept = 0;
|
virtual void close(bool skip_unmap = false) noexcept = 0;
|
||||||
virtual const std::string& name() const noexcept = 0;
|
virtual const std::string& name() const noexcept = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SharedMemoryRegistry {
|
class SharedMemoryRegistry {
|
||||||
private:
|
private:
|
||||||
static std::mutex registry_mutex_;
|
static std::mutex registry_mutex_;
|
||||||
static std::unordered_set<SharedMemoryBase*> active_instances_;
|
static std::vector<SharedMemoryBase*> active_instances_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void register_instance(SharedMemoryBase* instance) {
|
static void register_instance(SharedMemoryBase* instance) {
|
||||||
std::scoped_lock lock(registry_mutex_);
|
std::scoped_lock lock(registry_mutex_);
|
||||||
active_instances_.insert(instance);
|
active_instances_.push_back(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void unregister_instance(SharedMemoryBase* instance) {
|
static void unregister_instance(SharedMemoryBase* instance) {
|
||||||
std::scoped_lock lock(registry_mutex_);
|
std::scoped_lock lock(registry_mutex_);
|
||||||
active_instances_.erase(instance);
|
active_instances_.erase(
|
||||||
|
std::remove(active_instances_.begin(), active_instances_.end(), instance), active_instances_.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cleanup_all() noexcept {
|
static void cleanup_all(bool skip_unmap = false) noexcept {
|
||||||
std::scoped_lock lock(registry_mutex_);
|
std::scoped_lock lock(registry_mutex_);
|
||||||
for (auto* instance : active_instances_)
|
for (auto* instance : active_instances_)
|
||||||
instance->close();
|
instance->close(skip_unmap);
|
||||||
active_instances_.clear();
|
active_instances_.clear();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
inline std::mutex SharedMemoryRegistry::registry_mutex_;
|
inline std::mutex SharedMemoryRegistry::registry_mutex_;
|
||||||
inline std::unordered_set<SharedMemoryBase*> SharedMemoryRegistry::active_instances_;
|
inline std::vector<SharedMemoryBase*> SharedMemoryRegistry::active_instances_;
|
||||||
|
|
||||||
class CleanupHooks {
|
class CleanupHooks {
|
||||||
private:
|
private:
|
||||||
static std::once_flag register_once_;
|
static std::once_flag register_once_;
|
||||||
|
|
||||||
static void handle_signal(int sig) noexcept {
|
static void handle_signal(int sig) noexcept {
|
||||||
SharedMemoryRegistry::cleanup_all();
|
// Search threads may still be running, so skip munmap (but still perform
|
||||||
_Exit(128 + sig);
|
// other cleanup actions). The memory mappings will be released on exit.
|
||||||
|
SharedMemoryRegistry::cleanup_all(true);
|
||||||
|
|
||||||
|
// Invoke the default handler, which will exit
|
||||||
|
struct sigaction sa;
|
||||||
|
sa.sa_handler = SIG_DFL;
|
||||||
|
sigemptyset(&sa.sa_mask);
|
||||||
|
sa.sa_flags = 0;
|
||||||
|
if (sigaction(sig, &sa, nullptr) == -1)
|
||||||
|
_Exit(128 + sig);
|
||||||
|
|
||||||
|
raise(sig);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void register_signal_handlers() noexcept {
|
static void register_signal_handlers() noexcept {
|
||||||
std::atexit([]() { SharedMemoryRegistry::cleanup_all(); });
|
std::atexit([]() { SharedMemoryRegistry::cleanup_all(true); });
|
||||||
|
|
||||||
constexpr int signals[] = {SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGFPE,
|
constexpr int signals[] = {SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGFPE,
|
||||||
SIGSEGV, SIGTERM, SIGBUS, SIGSYS, SIGXCPU, SIGXFSZ};
|
SIGSEGV, SIGTERM, SIGBUS, SIGSYS, SIGXCPU, SIGXFSZ};
|
||||||
@@ -170,9 +179,10 @@ class SharedMemory: public detail::SharedMemoryBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static std::string make_sentinel_base(const std::string& name) {
|
static std::string make_sentinel_base(const std::string& name) {
|
||||||
uint64_t hash = std::hash<std::string>{}(name);
|
|
||||||
char buf[32];
|
char buf[32];
|
||||||
std::snprintf(buf, sizeof(buf), "sfshm_%016" PRIx64, static_cast<uint64_t>(hash));
|
// Using std::to_string here causes non-deterministic PGO builds.
|
||||||
|
// snprintf, being part of libc, is insensitive to the formatted values.
|
||||||
|
std::snprintf(buf, sizeof(buf), "sfshm_%016" PRIu64, hash_string(name));
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,7 +328,7 @@ class SharedMemory: public detail::SharedMemoryBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void close() noexcept override {
|
void close(bool skip_unmap = false) noexcept override {
|
||||||
if (fd_ == -1 && mapped_ptr_ == nullptr)
|
if (fd_ == -1 && mapped_ptr_ == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -345,7 +355,10 @@ class SharedMemory: public detail::SharedMemoryBase {
|
|||||||
decrement_refcount_relaxed();
|
decrement_refcount_relaxed();
|
||||||
}
|
}
|
||||||
|
|
||||||
unmap_region();
|
if (skip_unmap)
|
||||||
|
mapped_ptr_ = nullptr;
|
||||||
|
else
|
||||||
|
unmap_region();
|
||||||
|
|
||||||
if (remove_region)
|
if (remove_region)
|
||||||
shm_unlink(name_.c_str());
|
shm_unlink(name_.c_str());
|
||||||
@@ -359,7 +372,8 @@ class SharedMemory: public detail::SharedMemoryBase {
|
|||||||
fd_ = -1;
|
fd_ = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
reset();
|
if (!skip_unmap)
|
||||||
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string& name() const noexcept override { return name_; }
|
const std::string& name() const noexcept override { return name_; }
|
||||||
@@ -427,11 +441,10 @@ class SharedMemory: public detail::SharedMemoryBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string sentinel_full_path(pid_t pid) const {
|
std::string sentinel_full_path(pid_t pid) const {
|
||||||
std::string path = "/dev/shm/";
|
char buf[1024];
|
||||||
path += sentinel_base_;
|
// See above snprintf comment
|
||||||
path.push_back('.');
|
std::snprintf(buf, sizeof(buf), "/dev/shm/%s.%ld", sentinel_base_.c_str(), long(pid));
|
||||||
path += std::to_string(pid);
|
return buf;
|
||||||
return path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void decrement_refcount_relaxed() noexcept {
|
void decrement_refcount_relaxed() noexcept {
|
||||||
|
|||||||
+16
-11
@@ -709,15 +709,11 @@ int map_score(TBTable<DTZ>* entry, File f, int value, WDLScore wdl) {
|
|||||||
return value + 1;
|
return value + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A temporary fix for the compiler bug with AVX-512. (#4450)
|
// A temporary fix for the compiler bug with vectorization. (#4450)
|
||||||
#ifdef USE_AVX512
|
#if defined(__clang__) && defined(__clang_major__) && __clang_major__ >= 15
|
||||||
#if defined(__clang__) && defined(__clang_major__) && __clang_major__ >= 15
|
#define DISABLE_CLANG_LOOP_VEC _Pragma("clang loop vectorize(disable)")
|
||||||
#define CLANG_AVX512_BUG_FIX __attribute__((optnone))
|
#else
|
||||||
#endif
|
#define DISABLE_CLANG_LOOP_VEC
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef CLANG_AVX512_BUG_FIX
|
|
||||||
#define CLANG_AVX512_BUG_FIX
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Compute a unique index out of a position and use it to probe the TB file. To
|
// Compute a unique index out of a position and use it to probe the TB file. To
|
||||||
@@ -727,8 +723,7 @@ int map_score(TBTable<DTZ>* entry, File f, int value, WDLScore wdl) {
|
|||||||
// idx = Binomial[1][s1] + Binomial[2][s2] + ... + Binomial[k][sk]
|
// idx = Binomial[1][s1] + Binomial[2][s2] + ... + Binomial[k][sk]
|
||||||
//
|
//
|
||||||
template<typename T, typename Ret = typename T::Ret>
|
template<typename T, typename Ret = typename T::Ret>
|
||||||
CLANG_AVX512_BUG_FIX Ret
|
Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* result) {
|
||||||
do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* result) {
|
|
||||||
|
|
||||||
Square squares[TBPIECES];
|
Square squares[TBPIECES];
|
||||||
Piece pieces[TBPIECES];
|
Piece pieces[TBPIECES];
|
||||||
@@ -812,8 +807,11 @@ do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* result)
|
|||||||
// Now we map again the squares so that the square of the lead piece is in
|
// Now we map again the squares so that the square of the lead piece is in
|
||||||
// the triangle A1-D1-D4.
|
// the triangle A1-D1-D4.
|
||||||
if (file_of(squares[0]) > FILE_D)
|
if (file_of(squares[0]) > FILE_D)
|
||||||
|
{
|
||||||
|
DISABLE_CLANG_LOOP_VEC
|
||||||
for (int i = 0; i < size; ++i)
|
for (int i = 0; i < size; ++i)
|
||||||
squares[i] = flip_file(squares[i]);
|
squares[i] = flip_file(squares[i]);
|
||||||
|
}
|
||||||
|
|
||||||
// Encode leading pawns starting with the one with minimum MapPawns[] and
|
// Encode leading pawns starting with the one with minimum MapPawns[] and
|
||||||
// proceeding in ascending order.
|
// proceeding in ascending order.
|
||||||
@@ -832,19 +830,26 @@ do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* result)
|
|||||||
// In positions without pawns, we further flip the squares to ensure leading
|
// In positions without pawns, we further flip the squares to ensure leading
|
||||||
// piece is below RANK_5.
|
// piece is below RANK_5.
|
||||||
if (rank_of(squares[0]) > RANK_4)
|
if (rank_of(squares[0]) > RANK_4)
|
||||||
|
{
|
||||||
|
DISABLE_CLANG_LOOP_VEC
|
||||||
for (int i = 0; i < size; ++i)
|
for (int i = 0; i < size; ++i)
|
||||||
squares[i] = flip_rank(squares[i]);
|
squares[i] = flip_rank(squares[i]);
|
||||||
|
}
|
||||||
|
|
||||||
// Look for the first piece of the leading group not on the A1-D4 diagonal
|
// Look for the first piece of the leading group not on the A1-D4 diagonal
|
||||||
// and ensure it is mapped below the diagonal.
|
// and ensure it is mapped below the diagonal.
|
||||||
|
DISABLE_CLANG_LOOP_VEC
|
||||||
for (int i = 0; i < d->groupLen[0]; ++i)
|
for (int i = 0; i < d->groupLen[0]; ++i)
|
||||||
{
|
{
|
||||||
if (!off_A1H8(squares[i]))
|
if (!off_A1H8(squares[i]))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (off_A1H8(squares[i]) > 0) // A1-H8 diagonal flip: SQ_A3 -> SQ_C1
|
if (off_A1H8(squares[i]) > 0) // A1-H8 diagonal flip: SQ_A3 -> SQ_C1
|
||||||
|
{
|
||||||
|
DISABLE_CLANG_LOOP_VEC
|
||||||
for (int j = i; j < size; ++j)
|
for (int j = i; j < size; ++j)
|
||||||
squares[j] = Square(((squares[j] >> 3) | (squares[j] << 3)) & 63);
|
squares[j] = Square(((squares[j] >> 3) | (squares[j] << 3)) & 63);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+23
-14
@@ -213,22 +213,31 @@ void ThreadPool::set(const NumaConfig& numaConfig,
|
|||||||
|
|
||||||
while (threads.size() < requested)
|
while (threads.size() < requested)
|
||||||
{
|
{
|
||||||
const size_t threadId = threads.size();
|
const size_t threadId = threads.size();
|
||||||
const NumaIndex numaId = doBindThreads ? boundThreadToNumaNode[threadId] : 0;
|
const NumaIndex numaId = doBindThreads ? boundThreadToNumaNode[threadId] : 0;
|
||||||
auto manager = threadId == 0 ? std::unique_ptr<Search::ISearchManager>(
|
auto create_thread = [&]() {
|
||||||
std::make_unique<Search::SearchManager>(updateContext))
|
auto manager = threadId == 0
|
||||||
: std::make_unique<Search::NullSearchManager>();
|
? std::unique_ptr<Search::ISearchManager>(
|
||||||
|
std::make_unique<Search::SearchManager>(updateContext))
|
||||||
|
: std::make_unique<Search::NullSearchManager>();
|
||||||
|
|
||||||
// When not binding threads we want to force all access to happen
|
// When not binding threads we want to force all access to happen
|
||||||
// from the same NUMA node, because in case of NUMA replicated memory
|
// from the same NUMA node, because in case of NUMA replicated memory
|
||||||
// accesses we don't want to trash cache in case the threads get scheduled
|
// accesses we don't want to trash cache in case the threads get scheduled
|
||||||
// on the same NUMA node.
|
// on the same NUMA node.
|
||||||
auto binder = doBindThreads ? OptionalThreadToNumaNodeBinder(numaConfig, numaId)
|
auto binder = doBindThreads ? OptionalThreadToNumaNodeBinder(numaConfig, numaId)
|
||||||
: OptionalThreadToNumaNodeBinder(numaId);
|
: OptionalThreadToNumaNodeBinder(numaId);
|
||||||
|
|
||||||
threads.emplace_back(std::make_unique<Thread>(sharedState, std::move(manager), threadId,
|
threads.emplace_back(std::make_unique<Thread>(sharedState, std::move(manager),
|
||||||
counts[numaId]++, threadsPerNode[numaId],
|
threadId, counts[numaId]++,
|
||||||
binder));
|
threadsPerNode[numaId], binder));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ensure the worker thread inherits the intended NUMA affinity at creation.
|
||||||
|
if (doBindThreads)
|
||||||
|
numaConfig.execute_on_numa_node(numaId, create_thread);
|
||||||
|
else
|
||||||
|
create_thread();
|
||||||
}
|
}
|
||||||
|
|
||||||
clear();
|
clear();
|
||||||
|
|||||||
+1
-1
@@ -26,7 +26,7 @@
|
|||||||
namespace Stockfish {
|
namespace Stockfish {
|
||||||
|
|
||||||
class OptionsMap;
|
class OptionsMap;
|
||||||
enum Color : int8_t;
|
enum Color : uint8_t;
|
||||||
|
|
||||||
namespace Search {
|
namespace Search {
|
||||||
struct LimitsType;
|
struct LimitsType;
|
||||||
|
|||||||
+9
-9
@@ -117,13 +117,13 @@ using Bitboard = uint64_t;
|
|||||||
constexpr int MAX_MOVES = 256;
|
constexpr int MAX_MOVES = 256;
|
||||||
constexpr int MAX_PLY = 246;
|
constexpr int MAX_PLY = 246;
|
||||||
|
|
||||||
enum Color : int8_t {
|
enum Color : uint8_t {
|
||||||
WHITE,
|
WHITE,
|
||||||
BLACK,
|
BLACK,
|
||||||
COLOR_NB = 2
|
COLOR_NB = 2
|
||||||
};
|
};
|
||||||
|
|
||||||
enum CastlingRights : int8_t {
|
enum CastlingRights : uint8_t {
|
||||||
NO_CASTLING,
|
NO_CASTLING,
|
||||||
WHITE_OO,
|
WHITE_OO,
|
||||||
WHITE_OOO = WHITE_OO << 1,
|
WHITE_OOO = WHITE_OO << 1,
|
||||||
@@ -139,7 +139,7 @@ enum CastlingRights : int8_t {
|
|||||||
CASTLING_RIGHT_NB = 16
|
CASTLING_RIGHT_NB = 16
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Bound : int8_t {
|
enum Bound : uint8_t {
|
||||||
BOUND_NONE,
|
BOUND_NONE,
|
||||||
BOUND_UPPER,
|
BOUND_UPPER,
|
||||||
BOUND_LOWER,
|
BOUND_LOWER,
|
||||||
@@ -190,13 +190,13 @@ constexpr Value QueenValue = 2538;
|
|||||||
|
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
enum PieceType : std::int8_t {
|
enum PieceType : std::uint8_t {
|
||||||
NO_PIECE_TYPE, PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING,
|
NO_PIECE_TYPE, PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING,
|
||||||
ALL_PIECES = 0,
|
ALL_PIECES = 0,
|
||||||
PIECE_TYPE_NB = 8
|
PIECE_TYPE_NB = 8
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Piece : std::int8_t {
|
enum Piece : std::uint8_t {
|
||||||
NO_PIECE,
|
NO_PIECE,
|
||||||
W_PAWN = PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
|
W_PAWN = PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
|
||||||
B_PAWN = PAWN + 8, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING,
|
B_PAWN = PAWN + 8, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING,
|
||||||
@@ -227,7 +227,7 @@ constexpr Depth DEPTH_UNSEARCHED = -2;
|
|||||||
constexpr Depth DEPTH_ENTRY_OFFSET = -3;
|
constexpr Depth DEPTH_ENTRY_OFFSET = -3;
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
enum Square : int8_t {
|
enum Square : uint8_t {
|
||||||
SQ_A1, SQ_B1, SQ_C1, SQ_D1, SQ_E1, SQ_F1, SQ_G1, SQ_H1,
|
SQ_A1, SQ_B1, SQ_C1, SQ_D1, SQ_E1, SQ_F1, SQ_G1, SQ_H1,
|
||||||
SQ_A2, SQ_B2, SQ_C2, SQ_D2, SQ_E2, SQ_F2, SQ_G2, SQ_H2,
|
SQ_A2, SQ_B2, SQ_C2, SQ_D2, SQ_E2, SQ_F2, SQ_G2, SQ_H2,
|
||||||
SQ_A3, SQ_B3, SQ_C3, SQ_D3, SQ_E3, SQ_F3, SQ_G3, SQ_H3,
|
SQ_A3, SQ_B3, SQ_C3, SQ_D3, SQ_E3, SQ_F3, SQ_G3, SQ_H3,
|
||||||
@@ -255,7 +255,7 @@ enum Direction : int8_t {
|
|||||||
NORTH_WEST = NORTH + WEST
|
NORTH_WEST = NORTH + WEST
|
||||||
};
|
};
|
||||||
|
|
||||||
enum File : int8_t {
|
enum File : uint8_t {
|
||||||
FILE_A,
|
FILE_A,
|
||||||
FILE_B,
|
FILE_B,
|
||||||
FILE_C,
|
FILE_C,
|
||||||
@@ -267,7 +267,7 @@ enum File : int8_t {
|
|||||||
FILE_NB
|
FILE_NB
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Rank : int8_t {
|
enum Rank : uint8_t {
|
||||||
RANK_1,
|
RANK_1,
|
||||||
RANK_2,
|
RANK_2,
|
||||||
RANK_3,
|
RANK_3,
|
||||||
@@ -406,7 +406,7 @@ constexpr Key make_key(uint64_t seed) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
enum MoveType {
|
enum MoveType : uint16_t {
|
||||||
NORMAL,
|
NORMAL,
|
||||||
PROMOTION = 1 << 14,
|
PROMOTION = 1 << 14,
|
||||||
EN_PASSANT = 2 << 14,
|
EN_PASSANT = 2 << 14,
|
||||||
|
|||||||
+2
-2
@@ -507,8 +507,8 @@ WinRateParams win_rate_params(const Position& pos) {
|
|||||||
double m = std::clamp(material, 17, 78) / 58.0;
|
double m = std::clamp(material, 17, 78) / 58.0;
|
||||||
|
|
||||||
// Return a = p_a(material) and b = p_b(material), see github.com/official-stockfish/WDL_model
|
// Return a = p_a(material) and b = p_b(material), see github.com/official-stockfish/WDL_model
|
||||||
constexpr double as[] = {-13.50030198, 40.92780883, -36.82753545, 386.83004070};
|
constexpr double as[] = {-72.32565836, 185.93832038, -144.58862193, 416.44950446};
|
||||||
constexpr double bs[] = {96.53354896, -165.79058388, 90.89679019, 49.29561889};
|
constexpr double bs[] = {83.86794042, -136.06112997, 69.98820887, 47.62901433};
|
||||||
|
|
||||||
double a = (((as[0] * m + as[1]) * m + as[2]) * m) + as[3];
|
double a = (((as[0] * m + as[1]) * m + as[2]) * m) + as[3];
|
||||||
double b = (((bs[0] * m + bs[1]) * m + bs[2]) * m) + bs[3];
|
double b = (((bs[0] * m + bs[1]) * m + bs[2]) * m) + bs[3];
|
||||||
|
|||||||
Reference in New Issue
Block a user