From bf2c7306ac7f83200ba4d894867e3c0c78c0802c Mon Sep 17 00:00:00 2001 From: Disservin Date: Sun, 11 Feb 2024 14:19:44 +0100 Subject: [PATCH] Use node counting to early stop search This introduces a form of node counting which can be used to further tweak the usage of our search time. The current approach stops the search when almost all nodes are searched on a single move. The idea originally came from Koivisto, but the implemention is a bit different, Koivisto scales the optimal time by the nodes effort and then determines if the search should be stopped. We just scale down the `totalTime` and stop the search if we exceed it and the effort is large enough. Passed STC: https://tests.stockfishchess.org/tests/view/65c8e0661d8e83c78bfcd5ec LLR: 2.97 (-2.94,2.94) <0.00,2.00> Total: 88672 W: 22907 L: 22512 D: 43253 Ptnml(0-2): 310, 10163, 23041, 10466, 356 Passed LTC: https://tests.stockfishchess.org/tests/view/65ca632b1d8e83c78bfcf554 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 170856 W: 42910 L: 42320 D: 85626 Ptnml(0-2): 104, 18337, 47960, 18919, 108 closes https://github.com/official-stockfish/Stockfish/pull/5053 Bench: 1198939 --- src/search.cpp | 17 +++++++++++++++++ src/search.h | 2 ++ src/thread.cpp | 2 ++ 3 files changed, 21 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index ae1c3414380..9574465388e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -418,6 +419,10 @@ void Search::Worker::iterative_deepening() { // Do we have time for the next iteration? Can we stop searching now? if (limits.use_time_management() && !threads.stop && !mainThread->stopOnPonderhit) { + auto bestmove = rootMoves[0].pv[0]; + int nodesEffort = effort[bestmove.from_sq()][bestmove.to_sq()] * 100 + / std::max(size_t(1), size_t(nodes)); + double fallingEval = (66 + 14 * (mainThread->bestPreviousAverageScore - bestValue) + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 616.6; @@ -435,6 +440,13 @@ void Search::Worker::iterative_deepening() { if (rootMoves.size() == 1) totalTime = std::min(500.0, totalTime); + if (completedDepth >= 10 && nodesEffort >= 95 + && mainThread->tm.elapsed(threads.nodes_searched()) > totalTime * 3 / 4 + && !mainThread->ponder) + { + threads.stop = true; + } + // Stop the search if we have exceeded the totalTime if (mainThread->tm.elapsed(threads.nodes_searched()) > totalTime) { @@ -1087,6 +1099,8 @@ Value Search::Worker::search( ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck][capture][movedPiece][move.to_sq()]; + uint64_t nodeCount = rootNode ? uint64_t(nodes) : 0; + // Step 16. Make the move thisThread->nodes.fetch_add(1, std::memory_order_relaxed); pos.do_move(move, st, givesCheck); @@ -1186,6 +1200,9 @@ Value Search::Worker::search( // Step 19. Undo move pos.undo_move(move); + if (rootNode) + effort[move.from_sq()][move.to_sq()] += nodes - nodeCount; + assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); // Step 20. Check for a new best move diff --git a/src/search.h b/src/search.h index 2d1077f825e..4a1c68bb9ce 100644 --- a/src/search.h +++ b/src/search.h @@ -219,6 +219,8 @@ class Worker { return static_cast(manager.get()); } + std::array, SQUARE_NB> effort; + LimitsType limits; size_t pvIdx, pvLast; diff --git a/src/thread.cpp b/src/thread.cpp index 61beb3994d0..95646601106 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include "misc.h" #include "movegen.h" @@ -203,6 +204,7 @@ void ThreadPool::start_thinking(const OptionsMap& options, th->worker->rootPos.set(pos.fen(), pos.is_chess960(), &th->worker->rootState); th->worker->rootState = setupStates->back(); th->worker->tbConfig = tbConfig; + th->worker->effort = {}; } main_thread()->start_searching();