X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fsearch.cpp;h=74ed69ff6495b7b4a04ee84804547342192b74dd;hb=9c7d72739cffe523f0ea8c84875160c17ae6ab82;hp=8257b5bb9f33d308ef668539e75ccb4f89567f12;hpb=369789b426f1cb63a8855c131855a2e37bc61846;p=stockfish diff --git a/src/search.cpp b/src/search.cpp index 8257b5bb..74ed69ff 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -150,7 +150,7 @@ namespace { // Easy move margin. An easy move candidate must be at least this much // better than the second best move. - const Value EasyMoveMargin = Value(0x200); + const Value EasyMoveMargin = Value(0x150); /// Namespace variables @@ -162,7 +162,7 @@ namespace { int MultiPV, UCIMultiPV, MultiPVIdx; // Time management variables - bool StopOnPonderhit, FirstRootMove, StopRequest, QuitRequest, AspirationFailLow; + volatile bool StopOnPonderhit, FirstRootMove, StopRequest, QuitRequest, AspirationFailLow; TimeManager TimeMgr; SearchLimits Limits; @@ -465,9 +465,10 @@ bool think(Position& pos, const SearchLimits& limits, Move searchMoves[]) { pos.undo_move(bestMove); // Return from think() with unchanged position } - // If we are pondering or in infinite search, we shouldn't print the best move + // When we reach max depth we arrive here even without a StopRequest, but if + // we are pondering or in infinite search, we shouldn't print the best move // before we are told to do so. - if (Limits.ponder || Limits.infinite) + if (!StopRequest && (Limits.ponder || Limits.infinite)) wait_for_stop_or_ponderhit(); // Could be MOVE_NONE when searching on a stalemate position @@ -496,16 +497,17 @@ namespace { Value bestValues[PLY_MAX_PLUS_2]; int bestMoveChanges[PLY_MAX_PLUS_2]; int depth, aspirationDelta; - Value value, alpha, beta; - Move bestMove, easyMove, skillBest, skillPonder; + Value bestValue, alpha, beta; + Move bestMove, skillBest, skillPonder; + bool bestMoveNeverChanged = true; // Initialize stuff before a new search memset(ss, 0, 4 * sizeof(SearchStack)); TT.new_search(); H.clear(); - *ponderMove = bestMove = easyMove = skillBest = skillPonder = MOVE_NONE; + *ponderMove = bestMove = skillBest = skillPonder = MOVE_NONE; depth = aspirationDelta = 0; - value = alpha = -VALUE_INFINITE, beta = VALUE_INFINITE; + bestValue = alpha = -VALUE_INFINITE, beta = VALUE_INFINITE; ss->currentMove = MOVE_NULL; // Hack to skip update gains // Moves to search are verified and copied @@ -555,7 +557,7 @@ namespace { do { // Search starts from ss+1 to allow referencing (ss-1). This is // needed by update gains and ss copy when splitting at Root. - value = search(pos, ss+1, alpha, beta, depth * ONE_PLY); + bestValue = search(pos, ss+1, alpha, beta, depth * ONE_PLY); // Bring to front the best move. It is critical that sorting is // done with a stable algorithm because all the values but the first @@ -569,7 +571,7 @@ namespace { // the fail high/low loop then reorder the PV moves, otherwise // leave the last PV move in its position so to be searched again. // Of course this is needed only in MultiPV search. - if (MultiPVIdx && value > alpha && value < beta) + if (MultiPVIdx && bestValue > alpha && bestValue < beta) sort(Rml.begin(), Rml.begin() + MultiPVIdx); // Write PV back to transposition table in case the relevant entries @@ -587,7 +589,7 @@ namespace { // if we have a fail high/low and we are deep in the search. UCI // protocol requires to send all the PV lines also if are still // to be searched and so refer to the previous search's score. - if ((value > alpha && value < beta) || elapsed_search_time() > 2000) + if ((bestValue > alpha && bestValue < beta) || elapsed_search_time() > 2000) for (int i = 0; i < std::min(UCIMultiPV, (int)Rml.size()); i++) { bool updated = (i <= MultiPVIdx); @@ -608,12 +610,12 @@ namespace { // In case of failing high/low increase aspiration window and // research, otherwise exit the fail high/low loop. - if (value >= beta) + if (bestValue >= beta) { beta = std::min(beta + aspirationDelta, VALUE_INFINITE); aspirationDelta += aspirationDelta / 2; } - else if (value <= alpha) + else if (bestValue <= alpha) { AspirationFailLow = true; StopOnPonderhit = false; @@ -624,13 +626,13 @@ namespace { else break; - } while (abs(value) < VALUE_KNOWN_WIN); + } while (abs(bestValue) < VALUE_KNOWN_WIN); } // Collect info about search result bestMove = Rml[0].pv[0]; *ponderMove = Rml[0].pv[1]; - bestValues[depth] = value; + bestValues[depth] = bestValue; bestMoveChanges[depth] = Rml.bestMoveChanges; // Skills: Do we need to pick now the best and the ponder moves ? @@ -640,30 +642,16 @@ namespace { if (Options["Use Search Log"].value()) { Log log(Options["Search Log Filename"].value()); - log << pretty_pv(pos, depth, value, elapsed_search_time(), &Rml[0].pv[0]) << endl; + log << pretty_pv(pos, depth, bestValue, elapsed_search_time(), &Rml[0].pv[0]) << endl; } - // Init easyMove at first iteration or drop it if differs from the best move - if (depth == 1 && (Rml.size() == 1 || Rml[0].score > Rml[1].score + EasyMoveMargin)) - easyMove = bestMove; - else if (bestMove != easyMove) - easyMove = MOVE_NONE; + // Filter out startup noise when monitoring best move stability + if (depth > 2 && bestMoveChanges[depth]) + bestMoveNeverChanged = false; - // Check for some early stop condition - if (!StopRequest && Limits.useTimeManagement()) + // Do we have time for the next iteration? Can we stop searching now? + if (!StopRequest && !StopOnPonderhit && Limits.useTimeManagement()) { - // Easy move: Stop search early if one move seems to be much better - // than the others or if there is only a single legal move. Also in - // the latter case search to some depth anyway to get a proper score. - if ( depth >= 7 - && easyMove == bestMove - && ( Rml.size() == 1 - ||( Rml[0].nodes > (pos.nodes_searched() * 85) / 100 - && elapsed_search_time() > TimeMgr.available_time() / 16) - ||( Rml[0].nodes > (pos.nodes_searched() * 98) / 100 - && elapsed_search_time() > TimeMgr.available_time() / 32))) - StopRequest = true; - // Take in account some extra time if the best move has changed if (depth > 4 && depth < 50) TimeMgr.pv_instability(bestMoveChanges[depth], bestMoveChanges[depth - 1]); @@ -673,8 +661,25 @@ namespace { if (elapsed_search_time() > (TimeMgr.available_time() * 62) / 100) StopRequest = true; + // Stop search early if one move seems to be much better than others + if ( depth >= 10 + && !StopRequest + && ( bestMoveNeverChanged + || elapsed_search_time() > (TimeMgr.available_time() * 40) / 100)) + { + Value rBeta = bestValue - EasyMoveMargin; + (ss+1)->excludedMove = bestMove; + (ss+1)->skipNullMove = true; + Value v = search(pos, ss+1, rBeta - 1, rBeta, (depth * ONE_PLY) / 2); + (ss+1)->skipNullMove = false; + (ss+1)->excludedMove = MOVE_NONE; + + if (v < rBeta) + StopRequest = true; + } + // If we are allowed to ponder do not stop the search now but keep pondering - if (StopRequest && Limits.ponder) + if (StopRequest && Limits.ponder) // FIXME Limits.ponder is racy { StopRequest = false; StopOnPonderhit = true; @@ -1027,8 +1032,7 @@ split_point_start: // At split points actual search starts from here << " currmovenumber " << moveCount + MultiPVIdx << endl; } - // At Root and at first iteration do a PV search on all the moves to score root moves - isPvMove = (PvNode && moveCount <= (RootNode && depth <= ONE_PLY ? MAX_MOVES : 1)); + isPvMove = (PvNode && moveCount <= 1); givesCheck = pos.move_gives_check(move, ci); captureOrPromotion = pos.is_capture_or_promotion(move); @@ -2071,20 +2075,8 @@ split_point_start: // At split points actual search starts from here do pos.undo_move(pv[--ply]); while (ply); } -} // namespace - -// Little helper used by idle_loop() to check that all the slave threads of a -// split point have finished searching. - -static bool all_slaves_finished(SplitPoint* sp) { - - for (int i = 0; i < Threads.size(); i++) - if (sp->is_slave[i]) - return false; - - return true; -} +} // namespace // Thread::idle_loop() is where the thread is parked when it has no work to do. @@ -2114,7 +2106,7 @@ void Thread::idle_loop(SplitPoint* sp) { lock_grab(&sleepLock); // If we are master and all slaves have finished don't go to sleep - if (sp && all_slaves_finished(sp)) + if (sp && Threads.split_point_finished(sp)) { lock_release(&sleepLock); break; @@ -2166,7 +2158,7 @@ void Thread::idle_loop(SplitPoint* sp) { // If this thread is the master of a split point and all slaves have // finished their work at this split point, return from the idle loop. - if (sp && all_slaves_finished(sp)) + if (sp && Threads.split_point_finished(sp)) { // Because sp->is_slave[] is reset under lock protection, // be sure sp->lock has been released before to return. @@ -2209,8 +2201,8 @@ void do_timer_event() { static int lastInfoTime; int e = elapsed_search_time(); - // Print debug information every second - if (get_system_time() - lastInfoTime >= 1000) + // Print debug information every one second + if (!lastInfoTime || get_system_time() - lastInfoTime >= 1000) { lastInfoTime = get_system_time();