From db64aa4b6d0a2505c3224b2104c2e7c94a623d1c Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Sun, 5 Jan 2025 17:11:22 -0800 Subject: [PATCH 1/4] refactor history bench 999324 --- src/history.h | 97 +++++++++++++++++++++++++++----------------------- src/search.cpp | 4 +-- 2 files changed, 55 insertions(+), 46 deletions(-) diff --git a/src/history.h b/src/history.h index edf7fdc6716..6c365164eac 100644 --- a/src/history.h +++ b/src/history.h @@ -66,13 +66,17 @@ inline int non_pawn_index(const Position& pos) { return pos.non_pawn_key(c) & (CORRECTION_HISTORY_SIZE - 1); } -// StatsEntry stores the stat table value. It is usually a number but could -// be a move or even a nested history. We use a class instead of a naked value -// to directly call history update operator<<() on the entry so to use stats -// tables at caller sites as simple multi-dim arrays. +// StatsEntry is the container of various numerical statistics. We use a class +// instead of a naked value to directly call history update operator<<() on +// the entry. The first template parameter T is the base type of the array, +// and the second template parameter D limits the range of updates in [-D, D] +// when we update values with the << operator template class StatsEntry { + static_assert(std::is_arithmetic::value, "Not an arithmetic type"); + static_assert(D <= std::numeric_limits::max(), "D overflows T"); + T entry; public: @@ -80,13 +84,9 @@ class StatsEntry { entry = v; return *this; } - T* operator&() { return &entry; } - T* operator->() { return &entry; } operator const T&() const { return entry; } void operator<<(int bonus) { - static_assert(D <= std::numeric_limits::max(), "D overflows T"); - // Make sure that bonus is in range [-D, D] int clampedBonus = std::clamp(bonus, -D, D); entry += clampedBonus - entry * std::abs(clampedBonus) / D; @@ -95,17 +95,28 @@ class StatsEntry { } }; -template -struct StatsHelper; - -// Stats is a generic N-dimensional array used to store various statistics. -// The first template parameter T is the base type of the array, and the second -// template parameter D limits the range of updates in [-D, D] when we update -// values with the << operator, while the last parameters (Size and Sizes) -// encode the dimensions of the array. -template -class Stats { - using child_type = typename StatsHelper::child_type; +template +class MultiArray; + +namespace Detail { + +template +struct MultiArrayHelper { + using child_type = MultiArray; +}; + +template +struct MultiArrayHelper { + using child_type = T; +}; + +} + +// MultiArray is a generic N-dimensional array used by various statistics. +// The template parameters (Size and Sizes) encode the dimensions of the array. +template +class MultiArray { + using child_type = typename Detail::MultiArrayHelper::child_type; using array_type = std::array; array_type data; @@ -122,7 +133,9 @@ class Stats { auto cbegin() const { return data.cbegin(); } auto cend() const { return data.cend(); } - void fill(const T& v) { + template + void fill(const U& v) { + static_assert(std::is_assignable_v, "Cannot assign fill value to entry type"); for (auto& ele : data) { if constexpr (sizeof...(Sizes) == 0) @@ -133,49 +146,39 @@ class Stats { } }; -template -struct StatsHelper { - using child_type = Stats; -}; - -template -struct StatsHelper { - using child_type = StatsEntry; -}; - -// In stats table, D=0 means that the template parameter is not used -enum StatsParams { - NOT_USED = 0 -}; enum StatsType { NoCaptures, Captures }; +template +using HistoryEntry = StatsEntry; + // ButterflyHistory records how often quiet moves have been successful or unsuccessful // during the current search, and is used for reduction and move ordering decisions. // It uses 2 tables (one for each color) indexed by the move's from and to squares, // see https://www.chessprogramming.org/Butterfly_Boards (~11 elo) -using ButterflyHistory = Stats; +using ButterflyHistory = MultiArray, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)>; // LowPlyHistory is adressed by play and move's from and to squares, used // to improve move ordering near the root -using LowPlyHistory = Stats; +using LowPlyHistory = + MultiArray, LOW_PLY_HISTORY_SIZE, int(SQUARE_NB) * int(SQUARE_NB)>; // CapturePieceToHistory is addressed by a move's [piece][to][captured piece type] -using CapturePieceToHistory = Stats; +using CapturePieceToHistory = MultiArray, PIECE_NB, SQUARE_NB, PIECE_TYPE_NB>; // PieceToHistory is like ButterflyHistory but is addressed by a move's [piece][to] -using PieceToHistory = Stats; +using PieceToHistory = MultiArray, PIECE_NB, SQUARE_NB>; // ContinuationHistory is the combined history of a given pair of moves, usually // the current one given a previous one. The nested history table is based on // PieceToHistory instead of ButterflyBoards. // (~63 elo) -using ContinuationHistory = Stats; +using ContinuationHistory = MultiArray; // PawnHistory is addressed by the pawn structure and a move's [piece][to] -using PawnHistory = Stats; +using PawnHistory = MultiArray, PAWN_HISTORY_SIZE, PIECE_NB, SQUARE_NB>; // Correction histories record differences between the static evaluation of // positions and their search score. It is used to improve the static evaluation @@ -190,23 +193,29 @@ enum CorrHistType { Continuation, // Combined history of move pairs }; +using CorrHistEntry = StatsEntry; + +namespace Detail { + template struct CorrHistTypedef { - using type = Stats; + using type = MultiArray; }; template<> struct CorrHistTypedef { - using type = Stats; + using type = MultiArray; }; template<> struct CorrHistTypedef { - using type = Stats::type, NOT_USED, PIECE_NB, SQUARE_NB>; + using type = MultiArray::type, PIECE_NB, SQUARE_NB>; }; +} + template -using CorrectionHistory = typename CorrHistTypedef::type; +using CorrectionHistory = typename Detail::CorrHistTypedef::type; } // namespace Stockfish diff --git a/src/search.cpp b/src/search.cpp index d6748c7696e..cc3e4708f2b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -510,13 +510,13 @@ void Search::Worker::clear() { for (auto& to : continuationCorrectionHistory) for (auto& h : to) - h->fill(0); + h.fill(0); for (bool inCheck : {false, true}) for (StatsType c : {NoCaptures, Captures}) for (auto& to : continuationHistory[inCheck][c]) for (auto& h : to) - h->fill(-427); + h.fill(-427); for (size_t i = 1; i < reductions.size(); ++i) reductions[i] = int(19.43 * std::log(i)); From 60618bb14d35c4bc71a7c6f29f2f8e934a39ac9d Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Sun, 12 Jan 2025 01:53:35 -0800 Subject: [PATCH 2/4] move multiarray to misc.h bench 999324 --- src/history.h | 52 +------------------------------- src/misc.h | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 51 deletions(-) diff --git a/src/history.h b/src/history.h index 6c365164eac..908e122b8e1 100644 --- a/src/history.h +++ b/src/history.h @@ -28,6 +28,7 @@ #include #include // IWYU pragma: keep +#include "misc.h" #include "position.h" namespace Stockfish { @@ -95,57 +96,6 @@ class StatsEntry { } }; -template -class MultiArray; - -namespace Detail { - -template -struct MultiArrayHelper { - using child_type = MultiArray; -}; - -template -struct MultiArrayHelper { - using child_type = T; -}; - -} - -// MultiArray is a generic N-dimensional array used by various statistics. -// The template parameters (Size and Sizes) encode the dimensions of the array. -template -class MultiArray { - using child_type = typename Detail::MultiArrayHelper::child_type; - using array_type = std::array; - array_type data; - - public: - using size_type = typename array_type::size_type; - - auto& operator[](size_type index) { return data[index]; } - const auto& operator[](size_type index) const { return data[index]; } - - auto begin() { return data.begin(); } - auto end() { return data.end(); } - auto begin() const { return data.cbegin(); } - auto end() const { return data.cend(); } - auto cbegin() const { return data.cbegin(); } - auto cend() const { return data.cend(); } - - template - void fill(const U& v) { - static_assert(std::is_assignable_v, "Cannot assign fill value to entry type"); - for (auto& ele : data) - { - if constexpr (sizeof...(Sizes) == 0) - ele = v; - else - ele.fill(v); - } - } -}; - enum StatsType { NoCaptures, Captures diff --git a/src/misc.h b/src/misc.h index 48c49bafb34..4019addf559 100644 --- a/src/misc.h +++ b/src/misc.h @@ -142,6 +142,89 @@ class ValueList { }; +template +class MultiArray; + +namespace Detail { + +template +struct MultiArrayHelper { + using ChildType = MultiArray; +}; + +template +struct MultiArrayHelper { + using ChildType = T; +}; + +} + +// MultiArray is a generic N-dimensional array. +// The template parameters (Size and Sizes) encode the dimensions of the array. +template +class MultiArray { + using ChildType = typename Detail::MultiArrayHelper::ChildType; + using ArrayType = std::array; + ArrayType data; + + public: + using value_type = typename ArrayType::value_type; + using size_type = typename ArrayType::size_type; + using difference_type = typename ArrayType::difference_type; + using reference = typename ArrayType::reference; + using const_reference = typename ArrayType::const_reference; + using pointer = typename ArrayType::pointer; + using const_pointer = typename ArrayType::const_pointer; + using iterator = typename ArrayType::iterator; + using const_iterator = typename ArrayType::const_iterator; + using reverse_iterator = typename ArrayType::reverse_iterator; + using const_reverse_iterator = typename ArrayType::const_reverse_iterator; + + constexpr auto& at(size_type index) noexcept { return data.at(index); } + constexpr const auto& at(size_type index) const noexcept { return data.at(index); } + + constexpr auto& operator[](size_type index) noexcept { return data[index]; } + constexpr const auto& operator[](size_type index) const noexcept { return data[index]; } + + constexpr auto& front() noexcept { return data.front(); } + constexpr const auto& front() const noexcept { return data.front(); } + constexpr auto& back() noexcept { return data.back(); } + constexpr const auto& back() const noexcept { return data.back(); } + + constexpr auto begin() noexcept { return data.begin(); } + constexpr auto end() noexcept { return data.end(); } + constexpr auto begin() const noexcept { return data.begin(); } + constexpr auto end() const noexcept { return data.end(); } + constexpr auto cbegin() const noexcept { return data.cbegin(); } + constexpr auto cend() const noexcept { return data.cend(); } + + constexpr auto rbegin() noexcept { return data.rbegin(); } + constexpr auto rend() noexcept { return data.rend(); } + constexpr auto rbegin() const noexcept { return data.rbegin(); } + constexpr auto rend() const noexcept { return data.rend(); } + constexpr auto crbegin() const noexcept { return data.crbegin(); } + constexpr auto crend() const noexcept { return data.crend(); } + + constexpr bool empty() const noexcept { return data.empty(); } + constexpr size_type size() const noexcept { return data.size(); } + constexpr size_type max_size() const noexcept { return data.max_size(); } + + template + void fill(const U& v) { + static_assert(std::is_assignable_v, "Cannot assign fill value to entry type"); + for (auto& ele : data) + { + if constexpr (sizeof...(Sizes) == 0) + ele = v; + else + ele.fill(v); + } + } + + constexpr void swap(MultiArray& other) noexcept { data.swap(other.data); } +}; + + // xorshift64star Pseudo-Random Number Generator // This class is based on original code written and dedicated // to the public domain by Sebastiano Vigna (2014). From 1aa948e793c7a2b6f5032f4108941b4853af1023 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Sun, 12 Jan 2025 11:45:44 -0800 Subject: [PATCH 3/4] clean up history.h bench 999324 --- src/history.h | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/history.h b/src/history.h index 908e122b8e1..15095cd0bbc 100644 --- a/src/history.h +++ b/src/history.h @@ -101,25 +101,25 @@ enum StatsType { Captures }; -template -using HistoryEntry = StatsEntry; +template +using Stats = MultiArray, Sizes...>; // ButterflyHistory records how often quiet moves have been successful or unsuccessful // during the current search, and is used for reduction and move ordering decisions. // It uses 2 tables (one for each color) indexed by the move's from and to squares, // see https://www.chessprogramming.org/Butterfly_Boards (~11 elo) -using ButterflyHistory = MultiArray, COLOR_NB, int(SQUARE_NB) * int(SQUARE_NB)>; +using ButterflyHistory = Stats; // LowPlyHistory is adressed by play and move's from and to squares, used // to improve move ordering near the root using LowPlyHistory = - MultiArray, LOW_PLY_HISTORY_SIZE, int(SQUARE_NB) * int(SQUARE_NB)>; + Stats; // CapturePieceToHistory is addressed by a move's [piece][to][captured piece type] -using CapturePieceToHistory = MultiArray, PIECE_NB, SQUARE_NB, PIECE_TYPE_NB>; +using CapturePieceToHistory = Stats; // PieceToHistory is like ButterflyHistory but is addressed by a move's [piece][to] -using PieceToHistory = MultiArray, PIECE_NB, SQUARE_NB>; +using PieceToHistory = Stats; // ContinuationHistory is the combined history of a given pair of moves, usually // the current one given a previous one. The nested history table is based on @@ -128,7 +128,7 @@ using PieceToHistory = MultiArray, PIECE_NB, SQUARE_NB>; using ContinuationHistory = MultiArray; // PawnHistory is addressed by the pawn structure and a move's [piece][to] -using PawnHistory = MultiArray, PAWN_HISTORY_SIZE, PIECE_NB, SQUARE_NB>; +using PawnHistory = Stats; // Correction histories record differences between the static evaluation of // positions and their search score. It is used to improve the static evaluation @@ -143,18 +143,16 @@ enum CorrHistType { Continuation, // Combined history of move pairs }; -using CorrHistEntry = StatsEntry; - namespace Detail { template struct CorrHistTypedef { - using type = MultiArray; + using type = Stats; }; template<> struct CorrHistTypedef { - using type = MultiArray; + using type = Stats; }; template<> From f0ee27b71dc6959bd3ef90f7fc6720263d57fc87 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Sun, 12 Jan 2025 12:00:11 -0800 Subject: [PATCH 4/4] fix includes no functional change --- src/misc.h | 56 ++++++++++++++++++++++++++---------------------- src/movepick.cpp | 1 + 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/src/misc.h b/src/misc.h index 4019addf559..81c7b17fe59 100644 --- a/src/misc.h +++ b/src/misc.h @@ -20,6 +20,7 @@ #define MISC_H_INCLUDED #include +#include #include #include #include @@ -165,7 +166,7 @@ template class MultiArray { using ChildType = typename Detail::MultiArrayHelper::ChildType; using ArrayType = std::array; - ArrayType data; + ArrayType data_; public: using value_type = typename ArrayType::value_type; @@ -180,39 +181,42 @@ class MultiArray { using reverse_iterator = typename ArrayType::reverse_iterator; using const_reverse_iterator = typename ArrayType::const_reverse_iterator; - constexpr auto& at(size_type index) noexcept { return data.at(index); } - constexpr const auto& at(size_type index) const noexcept { return data.at(index); } + constexpr auto& at(size_type index) noexcept { return data_.at(index); } + constexpr const auto& at(size_type index) const noexcept { return data_.at(index); } - constexpr auto& operator[](size_type index) noexcept { return data[index]; } - constexpr const auto& operator[](size_type index) const noexcept { return data[index]; } + constexpr auto& operator[](size_type index) noexcept { return data_[index]; } + constexpr const auto& operator[](size_type index) const noexcept { return data_[index]; } - constexpr auto& front() noexcept { return data.front(); } - constexpr const auto& front() const noexcept { return data.front(); } - constexpr auto& back() noexcept { return data.back(); } - constexpr const auto& back() const noexcept { return data.back(); } + constexpr auto& front() noexcept { return data_.front(); } + constexpr const auto& front() const noexcept { return data_.front(); } + constexpr auto& back() noexcept { return data_.back(); } + constexpr const auto& back() const noexcept { return data_.back(); } - constexpr auto begin() noexcept { return data.begin(); } - constexpr auto end() noexcept { return data.end(); } - constexpr auto begin() const noexcept { return data.begin(); } - constexpr auto end() const noexcept { return data.end(); } - constexpr auto cbegin() const noexcept { return data.cbegin(); } - constexpr auto cend() const noexcept { return data.cend(); } + auto* data() { return data_.data(); } + const auto* data() const { return data_.data(); } - constexpr auto rbegin() noexcept { return data.rbegin(); } - constexpr auto rend() noexcept { return data.rend(); } - constexpr auto rbegin() const noexcept { return data.rbegin(); } - constexpr auto rend() const noexcept { return data.rend(); } - constexpr auto crbegin() const noexcept { return data.crbegin(); } - constexpr auto crend() const noexcept { return data.crend(); } + constexpr auto begin() noexcept { return data_.begin(); } + constexpr auto end() noexcept { return data_.end(); } + constexpr auto begin() const noexcept { return data_.begin(); } + constexpr auto end() const noexcept { return data_.end(); } + constexpr auto cbegin() const noexcept { return data_.cbegin(); } + constexpr auto cend() const noexcept { return data_.cend(); } - constexpr bool empty() const noexcept { return data.empty(); } - constexpr size_type size() const noexcept { return data.size(); } - constexpr size_type max_size() const noexcept { return data.max_size(); } + constexpr auto rbegin() noexcept { return data_.rbegin(); } + constexpr auto rend() noexcept { return data_.rend(); } + constexpr auto rbegin() const noexcept { return data_.rbegin(); } + constexpr auto rend() const noexcept { return data_.rend(); } + constexpr auto crbegin() const noexcept { return data_.crbegin(); } + constexpr auto crend() const noexcept { return data_.crend(); } + + constexpr bool empty() const noexcept { return data_.empty(); } + constexpr size_type size() const noexcept { return data_.size(); } + constexpr size_type max_size() const noexcept { return data_.max_size(); } template void fill(const U& v) { static_assert(std::is_assignable_v, "Cannot assign fill value to entry type"); - for (auto& ele : data) + for (auto& ele : data_) { if constexpr (sizeof...(Sizes) == 0) ele = v; @@ -221,7 +225,7 @@ class MultiArray { } } - constexpr void swap(MultiArray& other) noexcept { data.swap(other.data); } + constexpr void swap(MultiArray& other) noexcept { data_.swap(other.data_); } }; diff --git a/src/movepick.cpp b/src/movepick.cpp index 4c5cc11dec1..8448616949d 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -22,6 +22,7 @@ #include #include "bitboard.h" +#include "misc.h" #include "position.h" namespace Stockfish {