diff --git a/src/search.cpp b/src/search.cpp index 2dcfb86d9..a949541cb 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -128,7 +128,6 @@ void update_correction_history(const Position& pos, Value value_draw(size_t nodes) { return VALUE_DRAW - 1 + Value(nodes & 0x2); } Value value_to_tt(Value v, int ply); Value value_from_tt(Value v, int ply, int r50c); -void update_pv(Move* pv, Move move, const Move* childPv); void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus); void update_quiet_histories( const Position& pos, Stack* ss, Search::Worker& workerThread, Move move, int bonus); @@ -263,7 +262,7 @@ bool Search::Worker::iterative_deepening() { SearchManager* mainThread = (is_mainthread() ? main_manager() : nullptr); - Move pv[MAX_PLY + 1]; + PVMoves pv; Depth lastBestMoveDepth = 0; @@ -290,7 +289,7 @@ bool Search::Worker::iterative_deepening() { for (int i = 0; i <= MAX_PLY + 2; ++i) (ss + i)->ply = i; - ss->pv = pv; + ss->pv = &pv; if (mainThread) { @@ -652,7 +651,7 @@ Value Search::Worker::search( assert(0 < depth && depth < MAX_PLY); assert(!(PvNode && cutNode)); - Move pv[MAX_PLY + 1]; + PVMoves pv; StateInfo st; Key posKey; @@ -1293,8 +1292,8 @@ moves_loop: // When in check, search starts here // otherwise let the parent node fail low with value <= alpha and try another move. if (PvNode && (moveCount == 1 || value > alpha)) { - (ss + 1)->pv = pv; - (ss + 1)->pv[0] = Move::none(); + (ss + 1)->pv = &pv; + (ss + 1)->pv->clear(); // Extend move from transposition table if we are about to dive into qsearch. // decisive score handling improves mate finding and retrograde analysis. @@ -1353,8 +1352,8 @@ moves_loop: // When in check, search starts here assert((ss + 1)->pv); - for (Move* m = (ss + 1)->pv; *m != Move::none(); ++m) - rm.pv.push_back(*m); + for (Move pvMove : *(ss + 1)->pv) + rm.pv.push_back(pvMove); // We record how often the best move has been changed in each iteration. // This information is used for time management. In MultiPV mode, @@ -1383,7 +1382,7 @@ moves_loop: // When in check, search starts here bestMove = move; if (PvNode && !rootNode) // Update pv even in fail-high case - update_pv(ss->pv, move, (ss + 1)->pv); + ss->pv->update(move, (ss + 1)->pv); if (value >= beta) { @@ -1527,7 +1526,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta) return alpha; } - Move pv[MAX_PLY + 1]; + PVMoves pv; StateInfo st; Key posKey; @@ -1539,8 +1538,8 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta) // Step 1. Initialize node if (PvNode) { - (ss + 1)->pv = pv; - ss->pv[0] = Move::none(); + (ss + 1)->pv = &pv; + ss->pv->clear(); } bestMove = Move::none(); @@ -1701,7 +1700,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta) bestMove = move; if (PvNode) // Update pv even in fail-high case - update_pv(ss->pv, move, (ss + 1)->pv); + ss->pv->update(move, (ss + 1)->pv); if (value < beta) // Update alpha here! alpha = value; @@ -1817,15 +1816,6 @@ Value value_from_tt(Value v, int ply, int r50c) { } -// Adds current move and appends child pv[] -void update_pv(Move* pv, Move move, const Move* childPv) { - - for (*pv++ = move; childPv && *childPv != Move::none();) - *pv++ = *childPv++; - *pv = Move::none(); -} - - // Updates stats at the end of search() when a bestMove is found void update_all_stats(const Position& pos, Stack* ss, @@ -1923,7 +1913,6 @@ void update_quiet_histories( workerThread.sharedHistory.pawn_entry(pos)[pos.moved_piece(move)][move.to_sq()] << bonus * (bonus > 0 ? 974 : 543) / 1024; } - } // When playing with strength handicap, choose the best move among a set of @@ -2113,8 +2102,8 @@ void syzygy_extend_pv(const OptionsMap& options, v = VALUE_DRAW; // Undo the PV moves - for (auto it = rootMove.pv.rbegin(); it != rootMove.pv.rend(); ++it) - pos.undo_move(*it); + for (size_t i = rootMove.pv.size(); i > 0; --i) + pos.undo_move(rootMove.pv[i - 1]); // Inform if we couldn't get a full extension in time if (time_abort()) diff --git a/src/search.h b/src/search.h index c70b1f885..3bdf3c783 100644 --- a/src/search.h +++ b/src/search.h @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -31,6 +30,7 @@ #include #include #include +#include #include "history.h" #include "misc.h" @@ -58,11 +58,52 @@ class OptionsMap; namespace Search { +struct PVMoves { + Move moves[MAX_PLY + 1]; + std::size_t length = 0; + + Move* begin() { return moves; } + const Move* begin() const { return moves; } + Move* end() { return moves + length; } + const Move* end() const { return moves + length; } + + Move& operator[](std::size_t index) { return moves[index]; } + const Move& operator[](std::size_t index) const { return moves[index]; } + + bool empty() const { return length == 0; } + std::size_t size() const { return length; } + + void clear() { length = 0; } + + void push_back(Move move) { + assert(length < MAX_PLY + 1); + moves[length++] = move; + } + + void resize(std::size_t newSize) { + assert(newSize <= length); + length = newSize; + } + + void update(Move move, const PVMoves* childPv) { + assert(childPv == nullptr || childPv->size() <= MAX_PLY); + length = childPv ? childPv->length : 0; + + if (childPv) + { + std::memcpy(moves + 1, childPv->moves, length * sizeof(Move)); + } + + moves[0] = move; + ++length; + } +}; + // Stack struct keeps track of the information we need to remember from nodes // shallower and deeper in the tree during the search. Each search thread has // its own array of Stack objects, indexed by the current ply. struct Stack { - Move* pv; + PVMoves* pv; PieceToHistory* continuationHistory; CorrectionHistory* continuationCorrectionHistory; int ply; @@ -85,8 +126,7 @@ struct Stack { // fail low). Score is normally set at -VALUE_INFINITE for all non-pv moves. struct RootMove { - explicit RootMove(Move m) : - pv(1, m) {} + explicit RootMove(Move m) { pv.push_back(m); } bool extract_ponder_from_tt(const TranspositionTable& tt, Position& pos); bool operator==(const Move& m) const { return pv[0] == m; } // Sort in descending order @@ -94,18 +134,18 @@ struct RootMove { return m.score != score ? m.score < score : m.previousScore < previousScore; } - uint64_t effort = 0; - Value score = -VALUE_INFINITE; - Value previousScore = -VALUE_INFINITE; - Value averageScore = -VALUE_INFINITE; - Value meanSquaredScore = -VALUE_INFINITE * VALUE_INFINITE; - Value uciScore = -VALUE_INFINITE; - bool scoreLowerbound = false; - bool scoreUpperbound = false; - int selDepth = 0; - int tbRank = 0; - Value tbScore; - std::vector pv; + uint64_t effort = 0; + Value score = -VALUE_INFINITE; + Value previousScore = -VALUE_INFINITE; + Value averageScore = -VALUE_INFINITE; + Value meanSquaredScore = -VALUE_INFINITE * VALUE_INFINITE; + Value uciScore = -VALUE_INFINITE; + bool scoreLowerbound = false; + bool scoreUpperbound = false; + int selDepth = 0; + int tbRank = 0; + Value tbScore; + PVMoves pv; }; using RootMoves = std::vector; @@ -343,7 +383,7 @@ class Worker { Depth rootDepth, completedDepth; Value rootDelta; - std::vector lastIterationPV; + PVMoves lastIterationPV; size_t threadIdx, numaThreadIdx, numaTotal; NumaReplicatedAccessToken numaAccessToken;