X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fsearch.cpp;h=21d5d786b6ba8be9e558bca2ffb18dfd78c951f1;hp=fb5d375e8e591a58a91fb0e9afe8a147846b426c;hb=ce159b16b9483f83b9e96ac6bf3d6e2ba7e5619c;hpb=f8224fc7d31324376c36c4788f1935a341d2187b diff --git a/src/search.cpp b/src/search.cpp index fb5d375e..21d5d786 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -28,7 +28,6 @@ #include "book.h" #include "evaluate.h" #include "history.h" -#include "misc.h" #include "movegen.h" #include "movepick.h" #include "search.h" @@ -43,6 +42,7 @@ namespace Search { LimitsType Limits; std::vector RootMoves; Position RootPosition; + Time SearchTime; } using std::string; @@ -118,7 +118,6 @@ namespace { size_t MultiPV, UCIMultiPV, PVIdx; TimeManager TimeMgr; - Time SearchTime; int BestMoveChanges; int SkillLevel; bool SkillLevelEnabled, Chess960; @@ -253,8 +252,7 @@ void Search::think() { Position& pos = RootPosition; Chess960 = pos.is_chess960(); Eval::RootColor = pos.side_to_move(); - SearchTime.restart(); - TimeMgr.init(Limits, pos.startpos_ply_counter()); + TimeMgr.init(Limits, pos.startpos_ply_counter(), pos.side_to_move()); TT.new_search(); H.clear(); @@ -267,7 +265,7 @@ void Search::think() { goto finalize; } - if (Options["OwnBook"]) + if (Options["OwnBook"] && !Limits.infinite) { Move bookMove = book.probe(pos, Options["Book File"], Options["Best Book Move"]); @@ -292,13 +290,13 @@ void Search::think() { log << "\nSearching: " << pos.to_fen() << "\ninfinite: " << Limits.infinite << " ponder: " << Limits.ponder - << " time: " << Limits.time - << " increment: " << Limits.increment - << " moves to go: " << Limits.movesToGo + << " time: " << Limits.time[pos.side_to_move()] + << " increment: " << Limits.inc[pos.side_to_move()] + << " moves to go: " << Limits.movestogo << endl; } - Threads.set_size(Options["Threads"]); + Threads.wake_up(); // Set best timer interval to avoid lagging under time pressure. Timer is // used to check for remaining available thinking time. @@ -310,9 +308,8 @@ void Search::think() { // We're ready to start searching. Call the iterative deepening loop function id_loop(pos); - // Stop timer and send all the slaves to sleep, if not already sleeping - Threads.set_timer(0); - Threads.set_size(1); + Threads.set_timer(0); // Stop timer + Threads.sleep(); if (Options["Use Search Log"]) { @@ -335,7 +332,7 @@ finalize: // but if we are pondering or in infinite search, we shouldn't print the best // move before we are told to do so. if (!Signals.stop && (Limits.ponder || Limits.infinite)) - Threads[pos.thread()].wait_for_stop_or_ponderhit(); + pos.this_thread()->wait_for_stop_or_ponderhit(); // Best move could be MOVE_NONE when searching on a stalemate position cout << "bestmove " << move_to_uci(RootMoves[0].pv[0], Chess960) @@ -363,7 +360,7 @@ namespace { ss->currentMove = MOVE_NULL; // Hack to skip update gains // Iterative deepening loop until requested to stop or target depth reached - while (!Signals.stop && ++depth <= MAX_PLY && (!Limits.maxDepth || depth <= Limits.maxDepth)) + while (!Signals.stop && ++depth <= MAX_PLY && (!Limits.depth || depth <= Limits.depth)) { // Save last iteration's scores before first PV line is searched and all // the move scores but the (new) PV are set to -VALUE_INFINITE. @@ -533,9 +530,8 @@ namespace { assert(alpha >= -VALUE_INFINITE && alpha < beta && beta <= VALUE_INFINITE); assert((alpha == beta - 1) || PvNode); assert(depth > DEPTH_ZERO); - assert(pos.thread() >= 0 && pos.thread() < Threads.size()); - Move movesSearched[MAX_MOVES]; + Move movesSearched[64]; StateInfo st; const TTEntry *tte; Key posKey; @@ -547,7 +543,7 @@ namespace { bool isPvMove, inCheck, singularExtensionNode, givesCheck; bool captureOrPromotion, dangerous, doFullDepthSearch; int moveCount = 0, playedMoveCount = 0; - Thread& thread = Threads[pos.thread()]; + Thread* thisThread = pos.this_thread(); SplitPoint* sp = NULL; refinedValue = bestValue = value = -VALUE_INFINITE; @@ -556,8 +552,8 @@ namespace { ss->ply = (ss-1)->ply + 1; // Used to send selDepth info to GUI - if (PvNode && thread.maxPly < ss->ply) - thread.maxPly = ss->ply; + if (PvNode && thisThread->maxPly < ss->ply) + thisThread->maxPly = ss->ply; // Step 1. Initialize node if (SpNode) @@ -585,7 +581,7 @@ namespace { // Step 2. Check for aborted search and immediate draw // Enforce node limit here. FIXME: This only works with 1 search thread. - if (Limits.maxNodes && pos.nodes_searched() >= Limits.maxNodes) + if (Limits.nodes && pos.nodes_searched() >= Limits.nodes) Signals.stop = true; if (( Signals.stop @@ -673,7 +669,7 @@ namespace { && refinedValue + razor_margin(depth) < beta && ttMove == MOVE_NONE && abs(beta) < VALUE_MATE_IN_MAX_PLY - && !pos.has_pawn_on_7th(pos.side_to_move())) + && !pos.pawn_on_7th(pos.side_to_move())) { Value rbeta = beta - razor_margin(depth); Value v = qsearch(pos, ss, rbeta-1, rbeta, DEPTH_ZERO); @@ -820,7 +816,7 @@ split_point_start: // At split points actual search starts from here // Loop through all pseudo-legal moves until no moves remain or a beta cutoff occurs while ( bestValue < beta && (move = mp.next_move()) != MOVE_NONE - && !thread.cutoff_occurred() + && !thisThread->cutoff_occurred() && !Signals.stop) { assert(is_ok(move)); @@ -850,7 +846,7 @@ split_point_start: // At split points actual search starts from here { Signals.firstRootMove = (moveCount == 1); - if (pos.thread() == 0 && SearchTime.elapsed() > 2000) + if (thisThread == Threads.main_thread() && SearchTime.elapsed() > 2000) cout << "info depth " << depth / ONE_PLY << " currmove " << move_to_uci(move, Chess960) << " currmovenumber " << moveCount + PVIdx << endl; @@ -948,7 +944,7 @@ split_point_start: // At split points actual search starts from here } ss->currentMove = move; - if (!SpNode && !captureOrPromotion) + if (!SpNode && !captureOrPromotion && playedMoveCount < 64) movesSearched[playedMoveCount++] = move; // Step 14. Make the move @@ -1042,7 +1038,7 @@ split_point_start: // At split points actual search starts from here && value < beta) // We want always alpha < beta alpha = value; - if (SpNode && !thread.cutoff_occurred()) + if (SpNode && !thisThread->cutoff_occurred()) { sp->bestValue = value; sp->bestMove = move; @@ -1057,9 +1053,9 @@ split_point_start: // At split points actual search starts from here if ( !SpNode && depth >= Threads.min_split_depth() && bestValue < beta - && Threads.available_slave_exists(pos.thread()) + && Threads.available_slave_exists(thisThread) && !Signals.stop - && !thread.cutoff_occurred()) + && !thisThread->cutoff_occurred()) bestValue = Threads.split(pos, ss, alpha, beta, bestValue, &bestMove, depth, threatMove, moveCount, &mp, NT); } @@ -1083,7 +1079,7 @@ split_point_start: // At split points actual search starts from here // Step 21. Update tables // Update transposition table entry, killers and history - if (!SpNode && !Signals.stop && !thread.cutoff_occurred()) + if (!SpNode && !Signals.stop && !thisThread->cutoff_occurred()) { move = bestValue <= oldAlpha ? MOVE_NONE : bestMove; bt = bestValue <= oldAlpha ? BOUND_UPPER @@ -1134,7 +1130,6 @@ split_point_start: // At split points actual search starts from here assert(alpha >= -VALUE_INFINITE && alpha < beta && beta <= VALUE_INFINITE); assert((alpha == beta - 1) || PvNode); assert(depth <= DEPTH_ZERO); - assert(pos.thread() >= 0 && pos.thread() < Threads.size()); StateInfo st; Move ttMove, move, bestMove; @@ -1342,7 +1337,7 @@ split_point_start: // At split points actual search starts from here // Rule 1. Checks which give opponent's king at most one escape square are dangerous b = kingAtt & ~pos.pieces(them) & ~newAtt & ~(1ULL << to); - if (single_bit(b)) // Catches also !b + if (!more_than_one(b)) return true; // Rule 2. Queen contact check is very dangerous @@ -1391,7 +1386,7 @@ split_point_start: // At split points actual search starts from here // Case 3: Moving through the vacated square p2 = pos.piece_on(f2); - if (piece_is_slider(p2) && (squares_between(f2, t2) & f1)) + if (piece_is_slider(p2) && (between_bb(f2, t2) & f1)) return true; // Case 4: The destination square for m2 is defended by the moving piece in m1 @@ -1402,7 +1397,7 @@ split_point_start: // At split points actual search starts from here // Case 5: Discovered check, checking piece is the piece moved in m1 ksq = pos.king_square(pos.side_to_move()); if ( piece_is_slider(p1) - && (squares_between(t1, ksq) & f2) + && (between_bb(t1, ksq) & f2) && (pos.attacks_from(p1, t1, pos.pieces() ^ f2) & ksq)) return true; @@ -1474,7 +1469,7 @@ split_point_start: // At split points actual search starts from here // Case 3: If the moving piece in the threatened move is a slider, don't // prune safe moves which block its ray. if ( piece_is_slider(pos.piece_on(tfrom)) - && (squares_between(tfrom, tto) & mto) + && (between_bb(tfrom, tto) & mto) && pos.see_sign(m) >= 0) return true; @@ -1830,8 +1825,7 @@ void Thread::idle_loop(SplitPoint* sp_master) { lock_release(Threads.splitLock); Stack ss[MAX_PLY_PLUS_2]; - Position pos(*sp->pos, threadID); - int master = sp->master; + Position pos(*sp->pos, this); memcpy(ss, sp->ss - 1, 4 * sizeof(Stack)); (ss+1)->sp = sp; @@ -1850,20 +1844,59 @@ void Thread::idle_loop(SplitPoint* sp_master) { assert(is_searching); is_searching = false; - sp->slavesMask &= ~(1ULL << threadID); + sp->allSlavesRunning = false; + sp->slavesMask &= ~(1ULL << idx); sp->nodes += pos.nodes_searched(); - // After releasing the lock we cannot access anymore any SplitPoint - // related data in a reliably way becuase it could have been released - // under our feet by the sp master. - lock_release(sp->lock); - // 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.use_sleeping_threads() - && threadID != master - && !Threads[master].is_searching) - Threads[master].wake_up(); + && this != sp->master + && !sp->master->is_searching) + sp->master->wake_up(); + + // After releasing the lock we cannot access anymore any SplitPoint + // related data in a safe way becuase it could have been released under + // our feet by the sp master. Also accessing other Thread objects is + // unsafe because if we are exiting there is a chance are already freed. + lock_release(sp->lock); + + // Try to reparent to the first split point, with still all slaves + // running, where we are available as a possible slave. + for (int i = 0; i < Threads.size(); i++) + { + Thread* th = &Threads[i]; + int spCnt = th->splitPointsCnt; + SplitPoint* latest = &th->splitPoints[spCnt ? spCnt - 1 : 0]; + + if ( this->is_available_to(th) + && spCnt > 0 + && !th->cutoff_occurred() + && latest->allSlavesRunning + && more_than_one(latest->slavesMask)) + { + lock_grab(latest->lock); + lock_grab(Threads.splitLock); + + // Retest all under lock protection, we are in the middle + // of a race storm here ! + if ( this->is_available_to(th) + && spCnt == th->splitPointsCnt + && !th->cutoff_occurred() + && latest->allSlavesRunning + && more_than_one(latest->slavesMask)) + { + latest->slavesMask |= 1ULL << idx; + curSplitPoint = latest; + is_searching = true; + } + + lock_release(Threads.splitLock); + lock_release(latest->lock); + + break; // Exit anyhow, only one try (enough in 99% of cases) + } + } } } } @@ -1895,6 +1928,6 @@ void check_time() { || stillAtFirstMove; if ( (Limits.use_time_management() && noMoreTime) - || (Limits.maxTime && e >= Limits.maxTime)) + || (Limits.movetime && e >= Limits.movetime)) Signals.stop = true; }