diff --git a/src/history.h b/src/history.h index 127fb9ef3..9811d0397 100644 --- a/src/history.h +++ b/src/history.h @@ -35,22 +35,18 @@ namespace Stockfish { -constexpr int PAWN_HISTORY_SIZE = 8192; // has to be a power of 2 +constexpr int PAWN_HISTORY_BASE_SIZE = 8192; // has to be a power of 2 constexpr int UINT_16_HISTORY_SIZE = std::numeric_limits::max() + 1; constexpr int CORRHIST_BASE_SIZE = UINT_16_HISTORY_SIZE; constexpr int CORRECTION_HISTORY_LIMIT = 1024; constexpr int LOW_PLY_HISTORY_SIZE = 5; -static_assert((PAWN_HISTORY_SIZE & (PAWN_HISTORY_SIZE - 1)) == 0, - "PAWN_HISTORY_SIZE has to be a power of 2"); +static_assert((PAWN_HISTORY_BASE_SIZE & (PAWN_HISTORY_BASE_SIZE - 1)) == 0, + "PAWN_HISTORY_BASE_SIZE has to be a power of 2"); static_assert((CORRHIST_BASE_SIZE & (CORRHIST_BASE_SIZE - 1)) == 0, "CORRHIST_BASE_SIZE has to be a power of 2"); -inline int pawn_history_index(const Position& pos) { - return pos.pawn_key() & (PAWN_HISTORY_SIZE - 1); -} - // 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, @@ -96,6 +92,9 @@ enum StatsType { template using Stats = MultiArray, Sizes...>; +template +using AtomicStats = MultiArray, Sizes...>; + // DynStats is a dynamically sized array of Stats, used for thread-shared histories // which should scale with the total number of threads. The SizeMultiplier gives // the per-thread allocation count of T. @@ -106,11 +105,13 @@ struct DynStats { data = make_unique_large_page(size); } // Sets all values in the range to 0 - void clear_range(size_t start, size_t end) { + void clear_range(int value, size_t threadIdx) { + size_t start = threadIdx * SizeMultiplier; assert(start < size); - assert(end <= size); - T* fill_start = &(*this)[start]; - memset(reinterpret_cast(fill_start), 0, sizeof(T) * (end - start)); + size_t end = std::min(start + SizeMultiplier, size); + + while (start < end) + data[start++].fill(value); } size_t get_size() const { return size; } T& operator[](size_t index) { @@ -149,7 +150,8 @@ using PieceToHistory = Stats; using ContinuationHistory = MultiArray; // PawnHistory is addressed by the pawn structure and a move's [piece][to] -using PawnHistory = Stats; +using PawnHistory = + DynStats, PAWN_HISTORY_BASE_SIZE>; // Correction histories record differences between the static evaluation of // positions and their search score. It is used to improve the static evaluation @@ -169,6 +171,13 @@ struct CorrectionBundle { StatsEntry minor; StatsEntry nonPawnWhite; StatsEntry nonPawnBlack; + + void operator=(T val) { + pawn = val; + minor = val; + nonPawnWhite = val; + nonPawnBlack = val; + } }; namespace Detail { @@ -212,13 +221,22 @@ using TTMoveHistory = StatsEntry; // the indexing more efficient. struct SharedHistories { SharedHistories(size_t threadCount) : - correctionHistory(threadCount) { + correctionHistory(threadCount), + pawnHistory(threadCount) { assert((threadCount & (threadCount - 1)) == 0 && threadCount != 0); - sizeMinus1 = correctionHistory.get_size() - 1; + sizeMinus1 = correctionHistory.get_size() - 1; + pawnHistSizeMinus1 = pawnHistory.get_size() - 1; } size_t get_size() const { return sizeMinus1 + 1; } + auto& pawn_entry(const Position& pos) { + return pawnHistory[pos.pawn_key() & pawnHistSizeMinus1]; + } + const auto& pawn_entry(const Position& pos) const { + return pawnHistory[pos.pawn_key() & pawnHistSizeMinus1]; + } + auto& pawn_correction_entry(const Position& pos) { return correctionHistory[pos.pawn_key() & sizeMinus1]; } @@ -243,9 +261,11 @@ struct SharedHistories { } UnifiedCorrectionHistory correctionHistory; + PawnHistory pawnHistory; + private: - size_t sizeMinus1; + size_t sizeMinus1, pawnHistSizeMinus1; }; } // namespace Stockfish diff --git a/src/movepick.cpp b/src/movepick.cpp index d20ab151e..15f64d1cb 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -87,14 +87,14 @@ MovePicker::MovePicker(const Position& p, const LowPlyHistory* lph, const CapturePieceToHistory* cph, const PieceToHistory** ch, - const PawnHistory* ph, + const SharedHistories* sh, int pl) : pos(p), mainHistory(mh), lowPlyHistory(lph), captureHistory(cph), continuationHistory(ch), - pawnHistory(ph), + sharedHistory(sh), ttMove(ttm), depth(d), ply(pl) { @@ -159,7 +159,7 @@ ExtMove* MovePicker::score(MoveList& ml) { { // histories m.value = 2 * (*mainHistory)[us][m.raw()]; - m.value += 2 * (*pawnHistory)[pawn_history_index(pos)][pc][to]; + m.value += 2 * sharedHistory->pawn_entry(pos)[pc][to]; m.value += (*continuationHistory[0])[pc][to]; m.value += (*continuationHistory[1])[pc][to]; m.value += (*continuationHistory[2])[pc][to]; diff --git a/src/movepick.h b/src/movepick.h index 5b3190594..b1e041c17 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -45,7 +45,7 @@ class MovePicker { const LowPlyHistory*, const CapturePieceToHistory*, const PieceToHistory**, - const PawnHistory*, + const SharedHistories*, int); MovePicker(const Position&, Move, int, const CapturePieceToHistory*); Move next_move(); @@ -64,7 +64,7 @@ class MovePicker { const LowPlyHistory* lowPlyHistory; const CapturePieceToHistory* captureHistory; const PieceToHistory** continuationHistory; - const PawnHistory* pawnHistory; + const SharedHistories* sharedHistory; Move ttMove; ExtMove * cur, *endCur, *endBadCaptures, *endCaptures, *endGenerated; int stage; diff --git a/src/position.cpp b/src/position.cpp index cfbb85e84..f32530fdd 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -885,6 +885,7 @@ void Position::do_move(Move m, if (history) { + prefetch(&history->pawn_entry(*this)[pc][to]); prefetch(&history->pawn_correction_entry(*this)); prefetch(&history->minor_piece_correction_entry(*this)); prefetch(&history->nonpawn_correction_entry(*this)); diff --git a/src/search.cpp b/src/search.cpp index 95dd97196..87a96cab8 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -580,14 +580,10 @@ void Search::Worker::undo_null_move(Position& pos) { pos.undo_null_move(); } void Search::Worker::clear() { mainHistory.fill(68); captureHistory.fill(-689); - pawnHistory.fill(-1238); // Each thread is responsible for clearing their part of shared history - size_t len = sharedHistory.get_size() / numaTotal; - size_t start = numaThreadIdx * len; - size_t end = std::min(start + len, sharedHistory.get_size()); - - sharedHistory.correctionHistory.clear_range(start, end); + sharedHistory.correctionHistory.clear_range(0, numaThreadIdx); + sharedHistory.pawnHistory.clear_range(-1238, numaThreadIdx); ttMoveHistory = 0; @@ -861,7 +857,7 @@ Value Search::Worker::search( mainHistory[~us][((ss - 1)->currentMove).raw()] << evalDiff * 9; if (!ttHit && type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION) - pawnHistory[pawn_history_index(pos)][pos.piece_on(prevSq)][prevSq] << evalDiff * 13; + sharedHistory.pawn_entry(pos)[pos.piece_on(prevSq)][prevSq] << evalDiff * 13; } @@ -992,7 +988,7 @@ moves_loop: // When in check, search starts here MovePicker mp(pos, ttData.move, depth, &mainHistory, &lowPlyHistory, &captureHistory, contHist, - &pawnHistory, ss->ply); + &sharedHistory, ss->ply); value = bestValue; @@ -1081,7 +1077,7 @@ moves_loop: // When in check, search starts here { int history = (*contHist[0])[movedPiece][move.to_sq()] + (*contHist[1])[movedPiece][move.to_sq()] - + pawnHistory[pawn_history_index(pos)][movedPiece][move.to_sq()]; + + sharedHistory.pawn_entry(pos)[movedPiece][move.to_sq()]; // Continuation history based pruning if (history < -4083 * depth) @@ -1439,7 +1435,7 @@ moves_loop: // When in check, search starts here mainHistory[~us][((ss - 1)->currentMove).raw()] << scaledBonus * 243 / 32768; if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION) - pawnHistory[pawn_history_index(pos)][pos.piece_on(prevSq)][prevSq] + sharedHistory.pawn_entry(pos)[pos.piece_on(prevSq)][prevSq] << scaledBonus * 1160 / 32768; } @@ -1611,7 +1607,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta) // the moves. We presently use two stages of move generator in quiescence search: // captures, or evasions only when in check. MovePicker mp(pos, ttData.move, DEPTH_QS, &mainHistory, &lowPlyHistory, &captureHistory, - contHist, &pawnHistory, ss->ply); + contHist, &sharedHistory, ss->ply); // Step 5. Loop through all pseudo-legal moves until no moves remain or a beta // cutoff occurs. @@ -1900,8 +1896,7 @@ void update_quiet_histories( update_continuation_histories(ss, pos.moved_piece(move), move.to_sq(), bonus * 896 / 1024); - int pIndex = pawn_history_index(pos); - workerThread.pawnHistory[pIndex][pos.moved_piece(move)][move.to_sq()] + workerThread.sharedHistory.pawn_entry(pos)[pos.moved_piece(move)][move.to_sq()] << bonus * (bonus > 0 ? 905 : 505) / 1024; } diff --git a/src/search.h b/src/search.h index 9644f6aa5..eb4dda77c 100644 --- a/src/search.h +++ b/src/search.h @@ -292,7 +292,6 @@ class Worker { CapturePieceToHistory captureHistory; ContinuationHistory continuationHistory[2][2]; - PawnHistory pawnHistory; CorrectionHistory continuationCorrectionHistory; TTMoveHistory ttMoveHistory;