X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fsearch.cpp;h=df1c9b506d7c8e79f957bf90e1d9dcfe3f5c0aa4;hp=af895a376388890827fc4277e842ae036cc697c8;hb=c9d7e99de682516c560009b550c41da9ae2008b8;hpb=fdc9f8cbd787e61c4b9bff8a220235e9daa22c2c diff --git a/src/search.cpp b/src/search.cpp index af895a37..df1c9b50 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -64,6 +64,7 @@ namespace { static storage duration are automatically set to zero before enter main() */ public: + Thread& operator[](int threadID) { return threads[threadID]; } void init_threads(); void exit_threads(); @@ -75,13 +76,11 @@ namespace { bool available_thread_exists(int master) const; bool thread_is_available(int slave, int master) const; bool cutoff_at_splitpoint(int threadID) const; - void wake_sleeping_thread(int threadID); void idle_loop(int threadID, SplitPoint* sp); template void split(Position& pos, SearchStack* ss, Value* alpha, const Value beta, Value* bestValue, Depth depth, Move threatMove, int moveCount, MovePicker* mp, bool pvNode); - private: Lock mpLock; Depth minimumSplitDepth; @@ -117,8 +116,8 @@ namespace { void extract_pv_from_tt(Position& pos); void insert_pv_in_tt(Position& pos); - std::string pv_info_to_uci(Position& pos, int depth, Value alpha, Value beta, int pvIdx); - + std::string pv_info_to_uci(Position& pos, int depth, int selDepth, + Value alpha, Value beta, int pvIdx); int64_t nodes; Value pv_score; Value non_pv_score; @@ -126,7 +125,7 @@ namespace { }; - // RootMoveList struct is just a std::vector<> of RootMove objects, + // RootMoveList struct is just a vector of RootMove objects, // with an handful of methods above the standard ones. struct RootMoveList : public std::vector { @@ -181,7 +180,7 @@ namespace { // Step 9. Internal iterative deepening // Minimum depth for use of internal iterative deepening - const Depth IIDDepth[2] = { 8 * ONE_PLY /* non-PV */, 5 * ONE_PLY /* PV */}; + const Depth IIDDepth[] = { 8 * ONE_PLY, 5 * ONE_PLY }; // At Non-PV nodes we do an internal iterative deepening search // when the static evaluation is bigger then beta - IIDMargin. @@ -189,13 +188,14 @@ namespace { // Step 11. Decide the new search depth - // Extensions. Configurable UCI options - // Array index 0 is used at non-PV nodes, index 1 at PV nodes. - Depth CheckExtension[2], PawnPushTo7thExtension[2]; - Depth PassedPawnExtension[2], PawnEndgameExtension[2]; + // Extensions. Array index 0 is used for non-PV nodes, index 1 for PV nodes + const Depth CheckExtension[] = { ONE_PLY / 2, ONE_PLY / 1 }; + const Depth PawnEndgameExtension[] = { ONE_PLY / 1, ONE_PLY / 1 }; + const Depth PawnPushTo7thExtension[] = { ONE_PLY / 2, ONE_PLY / 2 }; + const Depth PassedPawnExtension[] = { DEPTH_ZERO, ONE_PLY / 2 }; // Minimum depth for use of singular extension - const Depth SingularExtensionDepth[2] = { 8 * ONE_PLY /* non-PV */, 6 * ONE_PLY /* PV */}; + const Depth SingularExtensionDepth[] = { 8 * ONE_PLY, 6 * ONE_PLY }; // Step 12. Futility pruning @@ -204,14 +204,22 @@ namespace { // Futility lookup tables (initialized at startup) and their access functions Value FutilityMarginsMatrix[16][64]; // [depth][moveNumber] - int FutilityMoveCountArray[32]; // [depth] + int FutilityMoveCountArray[32]; // [depth] + + inline Value futility_margin(Depth d, int mn) { + + return d < 7 * ONE_PLY ? FutilityMarginsMatrix[Max(d, 1)][Min(mn, 63)] + : 2 * VALUE_INFINITE; + } - inline Value futility_margin(Depth d, int mn) { return d < 7 * ONE_PLY ? FutilityMarginsMatrix[Max(d, 1)][Min(mn, 63)] : 2 * VALUE_INFINITE; } - inline int futility_move_count(Depth d) { return d < 16 * ONE_PLY ? FutilityMoveCountArray[d] : 512; } + inline int futility_move_count(Depth d) { + + return d < 16 * ONE_PLY ? FutilityMoveCountArray[d] : MAX_MOVES; + } // Step 14. Reduced search - // Reduction lookup tables (initialized at startup) and their getter functions + // Reduction lookup tables (initialized at startup) and their access function int8_t ReductionMatrix[2][64][64]; // [pv][depth][moveNumber] template @@ -234,13 +242,11 @@ namespace { int MultiPV, UCIMultiPV; // Time management variables - int SearchStartTime, MaxNodes, MaxDepth, ExactMaxTime; - bool UseTimeManagement, InfiniteSearch, Pondering, StopOnPonderhit; - bool FirstRootMove, StopRequest, QuitRequest, AspirationFailLow; + bool StopOnPonderhit, FirstRootMove, StopRequest, QuitRequest, AspirationFailLow; TimeManager TimeMgr; + SearchLimits Limits; // Log file - bool UseLogFile; std::ofstream LogFile; // Skill level adjustment @@ -292,7 +298,7 @@ namespace { void update_gains(const Position& pos, Move move, Value before, Value after); void do_skill_level(Move* best, Move* ponder); - int current_search_time(); + int current_search_time(int set = 0); std::string value_to_uci(Value v); std::string speed_to_uci(int64_t nodes); void poll(const Position& pos); @@ -305,7 +311,7 @@ namespace { #endif - // MovePickerExt is an extended MovePicker used to choose at compile time + // MovePickerExt is an extended MovePicker class used to choose at compile time // the proper move source according to the type of node. template struct MovePickerExt; @@ -412,7 +418,7 @@ void exit_threads() { ThreadsMgr.exit_threads(); } int64_t perft(Position& pos, Depth depth) { - MoveStack mlist[MOVES_MAX]; + MoveStack mlist[MAX_MOVES]; StateInfo st; Move m; int64_t sum = 0; @@ -440,25 +446,30 @@ int64_t perft(Position& pos, Depth depth) { /// think() is the external interface to Stockfish's search, and is called when /// the program receives the UCI 'go' command. It initializes various global -/// variables, and calls id_loop(). It returns false when a quit command is +/// variables, and calls id_loop(). It returns false when a "quit" command is /// received during the search. -bool think(Position& pos, bool infinite, bool ponder, int time[], int increment[], - int movesToGo, int maxDepth, int maxNodes, int maxTime, Move searchMoves[]) { +bool think(Position& pos, const SearchLimits& limits, Move searchMoves[]) { // Initialize global search-related variables StopOnPonderhit = StopRequest = QuitRequest = AspirationFailLow = SendSearchedNodes = false; NodesSincePoll = 0; - SearchStartTime = get_system_time(); - ExactMaxTime = maxTime; - MaxDepth = maxDepth; - MaxNodes = maxNodes; - InfiniteSearch = infinite; - Pondering = ponder; - UseTimeManagement = !ExactMaxTime && !MaxDepth && !MaxNodes && !InfiniteSearch; + current_search_time(get_system_time()); + Limits = limits; + TimeMgr.init(Limits, pos.startpos_ply_counter()); + + // Set best NodesBetweenPolls interval to avoid lagging under time pressure + if (Limits.maxNodes) + NodesBetweenPolls = Min(Limits.maxNodes, 30000); + else if (Limits.time && Limits.time < 1000) + NodesBetweenPolls = 1000; + else if (Limits.time && Limits.time < 5000) + NodesBetweenPolls = 5000; + else + NodesBetweenPolls = 30000; // Look for a book move, only during games, not tests - if (UseTimeManagement && Options["OwnBook"].value()) + if (Limits.useTimeManagement() && Options["OwnBook"].value()) { if (Options["Book File"].value() != OpeningBook.name()) OpeningBook.open(Options["Book File"].value()); @@ -466,7 +477,7 @@ bool think(Position& pos, bool infinite, bool ponder, int time[], int increment[ Move bookMove = OpeningBook.get_move(pos, Options["Best Book Move"].value()); if (bookMove != MOVE_NONE) { - if (Pondering) + if (Limits.ponder) wait_for_stop_or_ponderhit(); cout << "bestmove " << bookMove << endl; @@ -475,17 +486,8 @@ bool think(Position& pos, bool infinite, bool ponder, int time[], int increment[ } // Read UCI options - CheckExtension[1] = Options["Check Extension (PV nodes)"].value(); - CheckExtension[0] = Options["Check Extension (non-PV nodes)"].value(); - PawnPushTo7thExtension[1] = Options["Pawn Push to 7th Extension (PV nodes)"].value(); - PawnPushTo7thExtension[0] = Options["Pawn Push to 7th Extension (non-PV nodes)"].value(); - PassedPawnExtension[1] = Options["Passed Pawn Extension (PV nodes)"].value(); - PassedPawnExtension[0] = Options["Passed Pawn Extension (non-PV nodes)"].value(); - PawnEndgameExtension[1] = Options["Pawn Endgame Extension (PV nodes)"].value(); - PawnEndgameExtension[0] = Options["Pawn Endgame Extension (non-PV nodes)"].value(); - UCIMultiPV = Options["MultiPV"].value(); - SkillLevel = Options["Skill level"].value(); - UseLogFile = Options["Use Search Log"].value(); + UCIMultiPV = Options["MultiPV"].value(); + SkillLevel = Options["Skill level"].value(); read_evaluation_uci_options(pos.side_to_move()); @@ -505,54 +507,42 @@ bool think(Position& pos, bool infinite, bool ponder, int time[], int increment[ ThreadsMgr.read_uci_options(); init_eval(ThreadsMgr.active_threads()); - // Wake up needed threads. Main thread, with threadID == 0, is always active - for (int i = 1; i < ThreadsMgr.active_threads(); i++) - ThreadsMgr.wake_sleeping_thread(i); - - // Set thinking time - int myTime = time[pos.side_to_move()]; - int myIncrement = increment[pos.side_to_move()]; - if (UseTimeManagement) - TimeMgr.init(myTime, myIncrement, movesToGo, pos.startpos_ply_counter()); - - // Set best NodesBetweenPolls interval to avoid lagging under time pressure - if (MaxNodes) - NodesBetweenPolls = Min(MaxNodes, 30000); - else if (myTime && myTime < 1000) - NodesBetweenPolls = 1000; - else if (myTime && myTime < 5000) - NodesBetweenPolls = 5000; - else - NodesBetweenPolls = 30000; + // Wake up needed threads and reset maxPly counter + for (int i = 0; i < ThreadsMgr.active_threads(); i++) + { + ThreadsMgr[i].wake_up(); + ThreadsMgr[i].maxPly = 0; + } - // Write search information to log file - if (UseLogFile) + // Write to log file and keep it open to be accessed during the search + if (Options["Use Search Log"].value()) { std::string name = Options["Search Log Filename"].value(); LogFile.open(name.c_str(), std::ios::out | std::ios::app); - LogFile << "\nSearching: " << pos.to_fen() - << "\ninfinite: " << infinite - << " ponder: " << ponder - << " time: " << myTime - << " increment: " << myIncrement - << " moves to go: " << movesToGo - << endl; + if (LogFile.is_open()) + LogFile << "\nSearching: " << pos.to_fen() + << "\ninfinite: " << Limits.infinite + << " ponder: " << Limits.ponder + << " time: " << Limits.time + << " increment: " << Limits.increment + << " moves to go: " << Limits.movesToGo + << endl; } // We're ready to start thinking. Call the iterative deepening loop function Move ponderMove = MOVE_NONE; Move bestMove = id_loop(pos, searchMoves, &ponderMove); - // Print final search statistics cout << "info" << speed_to_uci(pos.nodes_searched()) << endl; - if (UseLogFile) + // Write final search statistics and close log file + if (LogFile.is_open()) { int t = current_search_time(); LogFile << "Nodes: " << pos.nodes_searched() - << "\nNodes/second: " << (t > 0 ? int(pos.nodes_searched() * 1000 / t) : 0) + << "\nNodes/second: " << (t > 0 ? pos.nodes_searched() * 1000 / t : 0) << "\nBest move: " << move_to_san(pos, bestMove); StateInfo st; @@ -567,7 +557,7 @@ bool think(Position& pos, bool infinite, bool ponder, int time[], int increment[ // If we are pondering or in infinite search, we shouldn't print the // best move before we are told to do so. - if (!StopRequest && (Pondering || InfiniteSearch)) + if (!StopRequest && (Limits.ponder || Limits.infinite)) wait_for_stop_or_ponderhit(); // Could be MOVE_NONE when searching on a stalemate position @@ -595,7 +585,7 @@ namespace { SearchStack ss[PLY_MAX_PLUS_2]; Value bestValues[PLY_MAX_PLUS_2]; int bestMoveChanges[PLY_MAX_PLUS_2]; - int depth, aspirationDelta; + int depth, selDepth, aspirationDelta; Value value, alpha, beta; Move bestMove, easyMove, skillBest, skillPonder; @@ -621,8 +611,8 @@ namespace { return MOVE_NONE; } - // Iterative deepening loop - while (++depth <= PLY_MAX && (!MaxDepth || depth <= MaxDepth) && !StopRequest) + // Iterative deepening loop until requested to stop or target depth reached + while (!StopRequest && ++depth <= PLY_MAX && (!Limits.maxDepth || depth <= Limits.maxDepth)) { Rml.bestMoveChanges = 0; cout << set960(pos.is_chess960()) << "info depth " << depth << endl; @@ -687,11 +677,17 @@ namespace { if (SkillLevelEnabled && depth == 1 + SkillLevel) do_skill_level(&skillBest, &skillPonder); + // Retrieve max searched depth among threads + selDepth = 0; + for (int i = 0; i < ThreadsMgr.active_threads(); i++) + if (ThreadsMgr[i].maxPly > selDepth) + selDepth = ThreadsMgr[i].maxPly; + // Send PV line to GUI and to log file for (int i = 0; i < Min(UCIMultiPV, (int)Rml.size()); i++) - cout << Rml[i].pv_info_to_uci(pos, depth, alpha, beta, i) << endl; + cout << Rml[i].pv_info_to_uci(pos, depth, selDepth, alpha, beta, i) << endl; - if (UseLogFile) + if (LogFile.is_open()) LogFile << pretty_pv(pos, depth, value, current_search_time(), Rml[0].pv) << endl; // Init easyMove after first iteration or drop if differs from the best move @@ -700,20 +696,18 @@ namespace { else if (bestMove != easyMove) easyMove = MOVE_NONE; - if (UseTimeManagement && !StopRequest) + // Check for some early stop condition + if (!StopRequest && Limits.useTimeManagement()) { - // Time to stop? - bool noMoreTime = false; - // Stop search early when the last two iterations returned a mate score if ( depth >= 5 - && abs(bestValues[depth]) >= abs(VALUE_MATE) - 100 - && abs(bestValues[depth - 1]) >= abs(VALUE_MATE) - 100) - noMoreTime = true; + && abs(bestValues[depth]) >= VALUE_MATE_IN_PLY_MAX + && abs(bestValues[depth - 1]) >= VALUE_MATE_IN_PLY_MAX) + StopRequest = true; // Stop search early if one move seems to be much better than the - // others or if there is only a single legal move. In this latter - // case we search up to Iteration 8 anyway to get a proper score. + // others or if there is only a single legal move. Also in the latter + // case we search up to some depth anyway to get a proper score. if ( depth >= 7 && easyMove == bestMove && ( Rml.size() == 1 @@ -721,29 +715,27 @@ namespace { && current_search_time() > TimeMgr.available_time() / 16) ||( Rml[0].nodes > (pos.nodes_searched() * 98) / 100 && current_search_time() > TimeMgr.available_time() / 32))) - noMoreTime = true; + StopRequest = true; - // Add some extra time if the best move has changed during the last two iterations + // 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]); + TimeMgr.pv_instability(bestMoveChanges[depth], bestMoveChanges[depth - 1]); - // Stop search if most of MaxSearchTime is consumed at the end of the - // iteration. We probably don't have enough time to search the first - // move at the next iteration anyway. - if (current_search_time() > (TimeMgr.available_time() * 80) / 128) - noMoreTime = true; + // Stop search if most of available time is already consumed. We probably don't + // have enough time to search the first move at the next iteration anyway. + if (current_search_time() > (TimeMgr.available_time() * 62) / 100) + StopRequest = true; - if (noMoreTime) + // If we are allowed to ponder do not stop the search now but keep pondering + if (StopRequest && Limits.ponder) { - if (Pondering) - StopOnPonderhit = true; - else - break; + StopRequest = false; + StopOnPonderhit = true; } } } - // When using skills fake best and ponder moves with the sub-optimal ones + // When using skills overwrite best and ponder moves with the sub-optimal ones if (SkillLevelEnabled) { if (skillBest == MOVE_NONE) // Still unassigned ? @@ -772,7 +764,7 @@ namespace { assert(PvNode || alpha == beta - 1); assert(pos.thread() >= 0 && pos.thread() < ThreadsMgr.active_threads()); - Move movesSearched[MOVES_MAX]; + Move movesSearched[MAX_MOVES]; int64_t nodes; StateInfo st; const TTEntry *tte; @@ -792,6 +784,10 @@ namespace { isCheck = pos.is_check(); ss->ply = (ss-1)->ply + 1; + // Used to send selDepth info to GUI + if (PvNode && ThreadsMgr[threadID].maxPly < ss->ply) + ThreadsMgr[threadID].maxPly = ss->ply; + if (SpNode) { sp = ss->sp; @@ -1708,8 +1704,7 @@ split_point_start: // At split points actual search starts from here && pos.type_of_piece_on(move_to(m)) != PAWN && ( pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) - pos.midgame_value_of_piece_on(move_to(m)) == VALUE_ZERO) - && !move_is_promotion(m) - && !move_is_ep(m)) + && !move_is_special(m)) { result += PawnEndgameExtension[PvNode]; *dangerous = true; @@ -1831,9 +1826,14 @@ split_point_start: // At split points actual search starts from here // current_search_time() returns the number of milliseconds which have passed // since the beginning of the current search. - int current_search_time() { + int current_search_time(int set) { - return get_system_time() - SearchStartTime; + static int searchStartTime; + + if (set) + searchStartTime = set; + + return get_system_time() - searchStartTime; } @@ -1849,9 +1849,9 @@ split_point_start: // At split points actual search starts from here std::stringstream s; if (abs(v) < VALUE_MATE - PLY_MAX * ONE_PLY) - s << "cp " << int(v) * 100 / int(PawnValueMidgame); // Scale to centipawns + s << "cp " << int(v) * 100 / int(PawnValueMidgame); // Scale to centipawns else - s << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2; + s << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2; return s.str(); } @@ -1891,7 +1891,7 @@ split_point_start: // At split points actual search starts from here if (!std::getline(std::cin, command) || command == "quit") { // Quit the program as soon as possible - Pondering = false; + Limits.ponder = false; QuitRequest = StopRequest = true; return; } @@ -1899,7 +1899,7 @@ split_point_start: // At split points actual search starts from here { // Stop calculating as soon as possible, but still send the "bestmove" // and possibly the "ponder" token when finishing the search. - Pondering = false; + Limits.ponder = false; StopRequest = true; } else if (command == "ponderhit") @@ -1907,7 +1907,7 @@ split_point_start: // At split points actual search starts from here // The opponent has played the expected move. GUI sends "ponderhit" if // we were told to ponder on the same move the opponent has played. We // should continue searching but switching from pondering to normal search. - Pondering = false; + Limits.ponder = false; if (StopOnPonderhit) StopRequest = true; @@ -1935,7 +1935,7 @@ split_point_start: // At split points actual search starts from here } // Should we stop the search? - if (Pondering) + if (Limits.ponder) return; bool stillAtFirstMove = FirstRootMove @@ -1945,9 +1945,9 @@ split_point_start: // At split points actual search starts from here bool noMoreTime = t > TimeMgr.maximum_time() || stillAtFirstMove; - if ( (UseTimeManagement && noMoreTime) - || (ExactMaxTime && t >= ExactMaxTime) - || (MaxNodes && pos.nodes_searched() >= MaxNodes)) // FIXME + if ( (Limits.useTimeManagement() && noMoreTime) + || (Limits.maxTime && t >= Limits.maxTime) + || (Limits.maxNodes && pos.nodes_searched() >= Limits.maxNodes)) // FIXME StopRequest = true; } @@ -2021,7 +2021,7 @@ split_point_start: // At split points actual search starts from here assert(threadID >= 0 && threadID < MAX_THREADS); int i; - bool allFinished = false; + bool allFinished; while (true) { @@ -2036,7 +2036,8 @@ split_point_start: // At split points actual search starts from here // If we are not thinking, wait for a condition to be signaled // instead of wasting CPU time polling for work. - while ( threadID >= activeThreads || threads[threadID].state == THREAD_INITIALIZING + while ( threadID >= activeThreads + || threads[threadID].state == THREAD_INITIALIZING || (useSleepingThreads && threads[threadID].state == THREAD_AVAILABLE)) { assert(!sp || useSleepingThreads); @@ -2045,7 +2046,7 @@ split_point_start: // At split points actual search starts from here if (threads[threadID].state == THREAD_INITIALIZING) threads[threadID].state = THREAD_AVAILABLE; - // Grab the lock to avoid races with wake_sleeping_thread() + // Grab the lock to avoid races with Thread::wake_up() lock_grab(&threads[threadID].sleepLock); // If we are master and all slaves have finished do not go to sleep @@ -2072,7 +2073,7 @@ split_point_start: // At split points actual search starts from here threads[threadID].state = THREAD_SEARCHING; - // Copy SplitPoint position and search stack and call search() + // Copy split point position and search stack and call search() // with SplitPoint template parameter set to true. SearchStack ss[PLY_MAX_PLUS_2]; SplitPoint* tsp = threads[threadID].splitPoint; @@ -2092,8 +2093,10 @@ split_point_start: // At split points actual search starts from here // 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 (useSleepingThreads && threadID != tsp->master && threads[tsp->master].state == THREAD_AVAILABLE) - wake_sleeping_thread(tsp->master); + if ( useSleepingThreads + && threadID != tsp->master + && threads[tsp->master].state == THREAD_AVAILABLE) + threads[tsp->master].wake_up(); } // If this thread is the master of a split point and all slaves have @@ -2119,41 +2122,36 @@ split_point_start: // At split points actual search starts from here } - // init_threads() is called during startup. It launches all helper threads, - // and initializes the split point stack and the global locks and condition - // objects. + // init_threads() is called during startup. Initializes locks and condition + // variables and launches all threads sending them immediately to sleep. void ThreadsManager::init_threads() { int i, arg[MAX_THREADS]; bool ok; - // Initialize global locks + // This flag is needed to properly end the threads when program exits + allThreadsShouldExit = false; + + // Threads will sent to sleep as soon as created, only main thread is kept alive + activeThreads = 1; + lock_init(&mpLock); for (i = 0; i < MAX_THREADS; i++) { + // Initialize thread and split point locks lock_init(&threads[i].sleepLock); cond_init(&threads[i].sleepCond); - } - // Initialize splitPoints[] locks - for (i = 0; i < MAX_THREADS; i++) for (int j = 0; j < MAX_ACTIVE_SPLIT_POINTS; j++) lock_init(&(threads[i].splitPoints[j].lock)); - // Will be set just before program exits to properly end the threads - allThreadsShouldExit = false; - - // Threads will be put all threads to sleep as soon as created - activeThreads = 1; - - // All threads except the main thread should be initialized to THREAD_INITIALIZING - threads[0].state = THREAD_SEARCHING; - for (i = 1; i < MAX_THREADS; i++) - threads[i].state = THREAD_INITIALIZING; + // All threads but first should be set to THREAD_INITIALIZING + threads[i].state = (i == 0 ? THREAD_SEARCHING : THREAD_INITIALIZING); + } - // Launch the helper threads + // Create and startup the threads for (i = 1; i < MAX_THREADS; i++) { arg[i] = i; @@ -2182,28 +2180,27 @@ split_point_start: // At split points actual search starts from here void ThreadsManager::exit_threads() { - allThreadsShouldExit = true; // Let the woken up threads to exit idle_loop() + // Force the woken up threads to exit idle_loop() and hence terminate + allThreadsShouldExit = true; - // Wake up all the threads and waits for termination - for (int i = 1; i < MAX_THREADS; i++) + for (int i = 0; i < MAX_THREADS; i++) { - wake_sleeping_thread(i); - while (threads[i].state != THREAD_TERMINATED) {} - } + // Wake up all the threads and waits for termination + if (i != 0) + { + threads[i].wake_up(); + while (threads[i].state != THREAD_TERMINATED) {} + } + + // Now we can safely destroy the locks and wait conditions + lock_destroy(&threads[i].sleepLock); + cond_destroy(&threads[i].sleepCond); - // Now we can safely destroy the locks - for (int i = 0; i < MAX_THREADS; i++) for (int j = 0; j < MAX_ACTIVE_SPLIT_POINTS; j++) lock_destroy(&(threads[i].splitPoints[j].lock)); + } lock_destroy(&mpLock); - - // Now we can safely destroy the wait conditions - for (int i = 0; i < MAX_THREADS; i++) - { - lock_destroy(&threads[i].sleepLock); - cond_destroy(&threads[i].sleepCond); - } } @@ -2362,7 +2359,7 @@ split_point_start: // At split points actual search starts from here threads[i].state = THREAD_WORKISWAITING; // This makes the slave to exit from idle_loop() if (useSleepingThreads && i != master) - wake_sleeping_thread(i); + threads[i].wake_up(); } // Everything is set up. The master thread enters the idle loop, from @@ -2386,17 +2383,6 @@ split_point_start: // At split points actual search starts from here } - // wake_sleeping_thread() wakes up the thread with the given threadID - // when it is time to start a new search. - - void ThreadsManager::wake_sleeping_thread(int threadID) { - - lock_grab(&threads[threadID].sleepLock); - cond_signal(&threads[threadID].sleepCond); - lock_release(&threads[threadID].sleepLock); - } - - /// RootMove and RootMoveList method's definitions RootMove::RootMove() { @@ -2483,21 +2469,20 @@ split_point_start: // At split points actual search starts from here // pv_info_to_uci() returns a string with information on the current PV line // formatted according to UCI specification. - std::string RootMove::pv_info_to_uci(Position& pos, int depth, Value alpha, + std::string RootMove::pv_info_to_uci(Position& pos, int depth, int selDepth, Value alpha, Value beta, int pvIdx) { - std::stringstream s, l; - Move* m = pv; - - while (*m != MOVE_NONE) - l << *m++ << " "; + std::stringstream s; s << "info depth " << depth - << " seldepth " << int(m - pv) + << " seldepth " << selDepth << " multipv " << pvIdx + 1 << " score " << value_to_uci(pv_score) << (pv_score >= beta ? " lowerbound" : pv_score <= alpha ? " upperbound" : "") << speed_to_uci(pos.nodes_searched()) - << " pv " << l.str(); + << " pv "; + + for (Move* m = pv; *m != MOVE_NONE; m++) + s << *m << " "; return s.str(); } @@ -2505,7 +2490,7 @@ split_point_start: // At split points actual search starts from here void RootMoveList::init(Position& pos, Move searchMoves[]) { - MoveStack mlist[MOVES_MAX]; + MoveStack mlist[MAX_MOVES]; Move* sm; clear();