X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fsearch.cpp;h=cff8e768a73693ec3ec29046165602aac7fd404c;hp=86f6fff0c694f86679c429d30f16f562a5def0f4;hb=a24da071f0d6128c633febab7df55f14475217c3;hpb=1c4e6d7ea2d7fb6892ae56889109b32cc98fd79a diff --git a/src/search.cpp b/src/search.cpp index 86f6fff0..cff8e768 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1,7 +1,7 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -98,7 +98,7 @@ namespace { void id_loop(Position& pos); Value value_to_tt(Value v, int ply); Value value_from_tt(Value v, int ply); - bool check_is_dangerous(Position& pos, Move move, Value futilityBase, Value beta); + bool check_is_dangerous(const Position& pos, Move move, Value futilityBase, Value beta); bool allows(const Position& pos, Move first, Move second); bool refutes(const Position& pos, Move first, Move second); string uci_pv(const Position& pos, int depth, Value alpha, Value beta); @@ -182,7 +182,7 @@ void Search::think() { static PolyglotBook book; // Defined static to initialize the PRNG only once RootColor = RootPos.side_to_move(); - TimeMgr.init(Limits, RootPos.startpos_ply_counter(), RootColor); + TimeMgr.init(Limits, RootPos.game_ply(), RootColor); if (RootMoves.empty()) { @@ -229,22 +229,22 @@ void Search::think() { // Reset the threads, still sleeping: will be wake up at split time for (size_t i = 0; i < Threads.size(); i++) - Threads[i].maxPly = 0; + Threads[i]->maxPly = 0; Threads.sleepWhileIdle = Options["Use Sleeping Threads"]; // Set best timer interval to avoid lagging under time pressure. Timer is // used to check for remaining available thinking time. - Threads.timer_thread()->msec = + Threads.timer->msec = Limits.use_time_management() ? std::min(100, std::max(TimeMgr.available_time() / 16, TimerResolution)) : Limits.nodes ? 2 * TimerResolution : 100; - Threads.timer_thread()->notify_one(); // Wake up the recurring timer + Threads.timer->notify_one(); // Wake up the recurring timer id_loop(RootPos); // Let's start searching ! - Threads.timer_thread()->msec = 0; // Stop the timer + Threads.timer->msec = 0; // Stop the timer Threads.sleepWhileIdle = true; // Send idle threads to sleep if (Options["Use Search Log"]) @@ -293,7 +293,7 @@ namespace { Stack ss[MAX_PLY_PLUS_2]; int depth, prevBestMoveChanges; Value bestValue, alpha, beta, delta; - bool bestMoveNeverChanged = true; + bool triedEasyMove = false; memset(ss, 0, 4 * sizeof(Stack)); depth = BestMoveChanges = 0; @@ -354,7 +354,7 @@ namespace { // we want to keep the same order for all the moves but the new // PV that goes to the front. Note that in case of MultiPV search // the already searched PV lines are preserved. - sort(RootMoves.begin() + PVIdx, RootMoves.end()); + std::stable_sort(RootMoves.begin() + PVIdx, RootMoves.end()); // Write PV back to transposition table in case the relevant // entries have been overwritten during the search. @@ -399,7 +399,8 @@ namespace { } // Sort the PV lines searched so far and update the GUI - sort(RootMoves.begin(), RootMoves.begin() + PVIdx + 1); + std::stable_sort(RootMoves.begin(), RootMoves.begin() + PVIdx + 1); + if (PVIdx + 1 == PVSize || Time::now() - SearchTime > 3000) sync_cout << uci_pv(pos, depth, alpha, beta) << sync_endl; } @@ -415,10 +416,6 @@ namespace { << std::endl; } - // Filter out startup noise when monitoring best move stability - if (depth > 2 && BestMoveChanges) - bestMoveNeverChanged = false; - // Do we have found a "mate in x"? if ( Limits.mate && bestValue >= VALUE_MATE_IN_MAX_PLY @@ -443,10 +440,12 @@ namespace { // Stop search early if one move seems to be much better than others if ( depth >= 12 && !stop + && !triedEasyMove && PVSize == 1 - && ( (bestMoveNeverChanged && pos.captured_piece_type()) - || Time::now() - SearchTime > (TimeMgr.available_time() * 40) / 100)) + && ( RootMoves.size() == 1 + || Time::now() - SearchTime > (TimeMgr.available_time() * 20) / 100)) { + triedEasyMove = true; Value rBeta = bestValue - 2 * PawnValueMg; (ss+1)->excludedMove = RootMoves[0].pv[0]; (ss+1)->skipNullMove = true; @@ -493,7 +492,7 @@ namespace { Move movesSearched[64]; StateInfo st; const TTEntry *tte; - SplitPoint* sp; + SplitPoint* splitPoint; Key posKey; Move ttMove, move, excludedMove, bestMove, threatMove; Depth ext, newDepth; @@ -510,15 +509,15 @@ namespace { if (SpNode) { - sp = ss->sp; - bestMove = sp->bestMove; - threatMove = sp->threatMove; - bestValue = sp->bestValue; + splitPoint = ss->splitPoint; + bestMove = splitPoint->bestMove; + threatMove = splitPoint->threatMove; + bestValue = splitPoint->bestValue; tte = NULL; ttMove = excludedMove = MOVE_NONE; ttValue = VALUE_NONE; - assert(sp->bestValue > -VALUE_INFINITE && sp->moveCount > 0); + assert(splitPoint->bestValue > -VALUE_INFINITE && splitPoint->moveCount > 0); goto split_point_start; } @@ -593,8 +592,8 @@ namespace { else if (tte) { // Never assume anything on values stored in TT - if ( (ss->staticEval = eval = tte->static_value()) == VALUE_NONE - ||(ss->evalMargin = tte->static_value_margin()) == VALUE_NONE) + if ( (ss->staticEval = eval = tte->eval_value()) == VALUE_NONE + ||(ss->evalMargin = tte->eval_margin()) == VALUE_NONE) eval = ss->staticEval = evaluate(pos, ss->evalMargin); // Can ttValue be used as a better position evaluation? @@ -793,8 +792,8 @@ split_point_start: // At split points actual search starts from here if (!pos.pl_move_is_legal(move, ci.pinned)) continue; - moveCount = ++sp->moveCount; - sp->mutex.unlock(); + moveCount = ++splitPoint->moveCount; + splitPoint->mutex.unlock(); } else moveCount++; @@ -860,9 +859,7 @@ split_point_start: // At split points actual search starts from here && !captureOrPromotion && !inCheck && !dangerous - && move != ttMove - && (bestValue > VALUE_MATED_IN_MAX_PLY || ( bestValue == -VALUE_INFINITE - && alpha > VALUE_MATED_IN_MAX_PLY))) + && move != ttMove) { // Move count based pruning if ( depth < 16 * ONE_PLY @@ -870,7 +867,7 @@ split_point_start: // At split points actual search starts from here && (!threatMove || !refutes(pos, move, threatMove))) { if (SpNode) - sp->mutex.lock(); + splitPoint->mutex.lock(); continue; } @@ -885,17 +882,17 @@ split_point_start: // At split points actual search starts from here if (futilityValue < beta) { if (SpNode) - sp->mutex.lock(); + splitPoint->mutex.lock(); continue; } // Prune moves with negative SEE at low depths - if ( predictedDepth < 2 * ONE_PLY + if ( predictedDepth < 3 * ONE_PLY && pos.see_sign(move) < 0) { if (SpNode) - sp->mutex.lock(); + splitPoint->mutex.lock(); continue; } @@ -922,12 +919,14 @@ split_point_start: // At split points actual search starts from here && !pvMove && !captureOrPromotion && !dangerous - && ss->killers[0] != move - && ss->killers[1] != move) + && move != ttMove + && move != ss->killers[0] + && move != ss->killers[1]) { ss->reduction = reduction(depth, moveCount); Depth d = std::max(newDepth - ss->reduction, ONE_PLY); - alpha = SpNode ? sp->alpha : alpha; + if (SpNode) + alpha = splitPoint->alpha; value = -search(pos, ss+1, -(alpha+1), -alpha, d); @@ -940,7 +939,9 @@ split_point_start: // At split points actual search starts from here // Step 16. Full depth search, when LMR is skipped or fails high if (doFullDepthSearch) { - alpha = SpNode ? sp->alpha : alpha; + if (SpNode) + alpha = splitPoint->alpha; + value = newDepth < ONE_PLY ? givesCheck ? -qsearch(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO) : -qsearch(pos, ss+1, -(alpha+1), -alpha, DEPTH_ZERO) @@ -963,9 +964,9 @@ split_point_start: // At split points actual search starts from here // Step 18. Check for new best move if (SpNode) { - sp->mutex.lock(); - bestValue = sp->bestValue; - alpha = sp->alpha; + splitPoint->mutex.lock(); + bestValue = splitPoint->bestValue; + alpha = splitPoint->alpha; } // Finished searching the move. If Signals.stop is true, the search @@ -1000,20 +1001,20 @@ split_point_start: // At split points actual search starts from here if (value > bestValue) { - bestValue = SpNode ? sp->bestValue = value : value; + bestValue = SpNode ? splitPoint->bestValue = value : value; if (value > alpha) { - bestMove = SpNode ? sp->bestMove = move : move; + bestMove = SpNode ? splitPoint->bestMove = move : move; if (PvNode && value < beta) // Update alpha! Always alpha < beta - alpha = SpNode ? sp->alpha = value : value; + alpha = SpNode ? splitPoint->alpha = value : value; else { assert(value >= beta); // Fail high if (SpNode) - sp->cutoff = true; + splitPoint->cutoff = true; break; } @@ -1023,13 +1024,13 @@ split_point_start: // At split points actual search starts from here // Step 19. Check for splitting the search if ( !SpNode && depth >= Threads.minimumSplitDepth - && Threads.slave_available(thisThread) + && Threads.available_slave(thisThread) && thisThread->splitPointsSize < MAX_SPLITPOINTS_PER_THREAD) { assert(bestValue < beta); - bestValue = Threads.split(pos, ss, alpha, beta, bestValue, &bestMove, - depth, threatMove, moveCount, mp, NT); + thisThread->split(pos, ss, alpha, beta, &bestValue, &bestMove, + depth, threatMove, moveCount, &mp, NT); if (bestValue >= beta) break; } @@ -1162,8 +1163,8 @@ split_point_start: // At split points actual search starts from here if (tte) { // Never assume anything on values stored in TT - if ( (ss->staticEval = bestValue = tte->static_value()) == VALUE_NONE - ||(ss->evalMargin = tte->static_value_margin()) == VALUE_NONE) + if ( (ss->staticEval = bestValue = tte->eval_value()) == VALUE_NONE + ||(ss->evalMargin = tte->eval_margin()) == VALUE_NONE) ss->staticEval = bestValue = evaluate(pos, ss->evalMargin); } else @@ -1333,7 +1334,7 @@ split_point_start: // At split points actual search starts from here // check_is_dangerous() tests if a checking move can be pruned in qsearch() - bool check_is_dangerous(Position& pos, Move move, Value futilityBase, Value beta) + bool check_is_dangerous(const Position& pos, Move move, Value futilityBase, Value beta) { Piece pc = pos.piece_moved(move); Square from = from_sq(move); @@ -1511,8 +1512,8 @@ split_point_start: // At split points actual search starts from here int selDepth = 0; for (size_t i = 0; i < Threads.size(); i++) - if (Threads[i].maxPly > selDepth) - selDepth = Threads[i].maxPly; + if (Threads[i]->maxPly > selDepth) + selDepth = Threads[i]->maxPly; for (size_t i = 0; i < uciPVSize; i++) { @@ -1612,13 +1613,11 @@ void Thread::idle_loop() { // Pointer 'this_sp' is not null only if we are called from split(), and not // at the thread creation. So it means we are the split point's master. - const SplitPoint* this_sp = splitPointsSize ? activeSplitPoint : NULL; + SplitPoint* this_sp = splitPointsSize ? activeSplitPoint : NULL; - assert(!this_sp || (this_sp->master == this && searching)); + assert(!this_sp || (this_sp->masterThread == this && searching)); - // 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. - while (!this_sp || this_sp->slavesMask) + while (true) { // If we are not searching, wait for a condition to be signaled instead of // wasting CPU time polling for work. @@ -1666,13 +1665,13 @@ void Thread::idle_loop() { Position pos(*sp->pos, this); memcpy(ss, sp->ss - 1, 4 * sizeof(Stack)); - (ss+1)->sp = sp; + (ss+1)->splitPoint = sp; sp->mutex.lock(); - assert(sp->slavesPositions[idx] == NULL); + assert(activePosition == NULL); - sp->slavesPositions[idx] = &pos; + activePosition = &pos; switch (sp->nodeType) { case Root: @@ -1691,18 +1690,18 @@ void Thread::idle_loop() { assert(searching); searching = false; - sp->slavesPositions[idx] = NULL; + activePosition = NULL; sp->slavesMask &= ~(1ULL << idx); sp->nodes += pos.nodes_searched(); // Wake up master thread so to allow it to return from the idle loop // in case we are the last slave of the split point. if ( Threads.sleepWhileIdle - && this != sp->master + && this != sp->masterThread && !sp->slavesMask) { - assert(!sp->master->searching); - sp->master->notify_one(); + assert(!sp->masterThread->searching); + sp->masterThread->notify_one(); } // After releasing the lock we cannot access anymore any SplitPoint @@ -1711,6 +1710,17 @@ void Thread::idle_loop() { // unsafe because if we are exiting there is a chance are already freed. sp->mutex.unlock(); } + + // 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 (this_sp && !this_sp->slavesMask) + { + this_sp->mutex.lock(); + bool finished = !this_sp->slavesMask; // Retest under lock protection + this_sp->mutex.unlock(); + if (finished) + return; + } } } @@ -1740,11 +1750,11 @@ void check_time() { nodes = RootPos.nodes_searched(); // Loop across all split points and sum accumulated SplitPoint nodes plus - // all the currently active slaves positions. + // all the currently active positions nodes. for (size_t i = 0; i < Threads.size(); i++) - for (int j = 0; j < Threads[i].splitPointsSize; j++) + for (int j = 0; j < Threads[i]->splitPointsSize; j++) { - SplitPoint& sp = Threads[i].splitPoints[j]; + SplitPoint& sp = Threads[i]->splitPoints[j]; sp.mutex.lock(); @@ -1752,8 +1762,9 @@ void check_time() { Bitboard sm = sp.slavesMask; while (sm) { - Position* pos = sp.slavesPositions[pop_lsb(&sm)]; - nodes += pos ? pos->nodes_searched() : 0; + Position* pos = Threads[pop_lsb(&sm)]->activePosition; + if (pos) + nodes += pos->nodes_searched(); } sp.mutex.unlock();