diff --git a/src/search.cpp b/src/search.cpp index 39a73193..eb9a0891 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -141,6 +141,7 @@ namespace { Value value_from_tt(Value v, int ply); void update_pv(Move* pv, Move move, Move* childPv); void update_stats(const Position& pos, Stack* ss, Move move, Depth depth, Move* quiets, int quietsCnt); + void check_time(); } // namespace @@ -298,14 +299,10 @@ void MainThread::think() { } } - Threads.timer->run = true; - Threads.timer->notify_one(); // Start the recurring timer - search(true); // Let's start searching! - // Stop the threads and the timer + // Stop the threads Signals.stop = true; - Threads.timer->run = false; // Wait until all threads have finished for (Thread* th : Threads) @@ -585,6 +582,20 @@ namespace { bestValue = -VALUE_INFINITE; ss->ply = (ss-1)->ply + 1; + // Check for available remaining time + if (thisThread->resetCallsCnt.load(std::memory_order_relaxed)) + { + thisThread->resetCallsCnt = false; + thisThread->callsCnt = 0; + } + if (++thisThread->callsCnt > 4096) + { + for (Thread* th : Threads) + th->resetCallsCnt = true; + + check_time(); + } + // Used to send selDepth info to GUI if (PvNode && thisThread->maxPly < ss->ply) thisThread->maxPly = ss->ply; @@ -1455,6 +1466,43 @@ moves_loop: // When in check search starts from here return best; } + + // check_time() is used to print debug info and, more importantly, to detect + // when we are out of available time and thus stop the search. + + void check_time() { + + static TimePoint lastInfoTime = now(); + + int elapsed = Time.elapsed(); + TimePoint tick = Limits.startTime + elapsed; + + if (tick - lastInfoTime >= 1000) + { + lastInfoTime = tick; + dbg_print(); + } + + // An engine may not stop pondering until told so by the GUI + if (Limits.ponder) + return; + + if (Limits.use_time_management()) + { + bool stillAtFirstMove = Signals.firstRootMove.load(std::memory_order_relaxed) + && !Signals.failedLowAtRoot.load(std::memory_order_relaxed) + && elapsed > Time.available() * 3 / 4; + + if (stillAtFirstMove || elapsed > Time.maximum() - 10) + Signals.stop = true; + } + else if (Limits.movetime && elapsed >= Limits.movetime) + Signals.stop = true; + + else if (Limits.nodes && Threads.nodes_searched() >= Limits.nodes) + Signals.stop = true; + } + } // namespace @@ -1565,40 +1613,3 @@ bool RootMove::extract_ponder_from_tt(Position& pos) return false; } - - -/// TimerThread::check_time() is called by when the timer triggers. It is used -/// to print debug info and, more importantly, to detect when we are out of -/// available time and thus stop the search. - -void TimerThread::check_time() { - - static TimePoint lastInfoTime = now(); - int elapsed = Time.elapsed(); - - if (now() - lastInfoTime >= 1000) - { - lastInfoTime = now(); - dbg_print(); - } - - // An engine may not stop pondering until told so by the GUI - if (Limits.ponder) - return; - - if (Limits.use_time_management()) - { - bool stillAtFirstMove = Signals.firstRootMove - && !Signals.failedLowAtRoot - && elapsed > Time.available() * 3 / 4; - - if ( stillAtFirstMove - || elapsed > Time.maximum() - 2 * TimerThread::Resolution) - Signals.stop = true; - } - else if (Limits.movetime && elapsed >= Limits.movetime) - Signals.stop = true; - - else if (Limits.nodes && Threads.nodes_searched() >= Limits.nodes) - Signals.stop = true; -} diff --git a/src/search.h b/src/search.h index 809a15d1..96c0a2d1 100644 --- a/src/search.h +++ b/src/search.h @@ -93,7 +93,7 @@ struct LimitsType { /// typically in an async fashion e.g. to stop the search by the GUI. struct SignalsType { - std::atomic stop, stopOnPonderhit, firstRootMove, failedLowAtRoot; + std::atomic_bool stop, stopOnPonderhit, firstRootMove, failedLowAtRoot; }; typedef std::unique_ptr> StateStackPtr; diff --git a/src/thread.cpp b/src/thread.cpp index 34424d37..c76b4b70 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -66,7 +66,7 @@ void ThreadBase::notify_one() { // ThreadBase::wait() set the thread to sleep until 'condition' turns true -void ThreadBase::wait(std::atomic& condition) { +void ThreadBase::wait(std::atomic_bool& condition) { std::unique_lock lk(mutex); sleepCondition.wait(lk, [&]{ return bool(condition); }); @@ -74,7 +74,7 @@ void ThreadBase::wait(std::atomic& condition) { // ThreadBase::wait_while() set the thread to sleep until 'condition' turns false -void ThreadBase::wait_while(std::atomic& condition) { +void ThreadBase::wait_while(std::atomic_bool& condition) { std::unique_lock lk(mutex); sleepCondition.wait(lk, [&]{ return !condition; }); @@ -86,34 +86,14 @@ void ThreadBase::wait_while(std::atomic& condition) { Thread::Thread() { - searching = false; - maxPly = 0; + searching = resetCallsCnt = false; + maxPly = callsCnt = 0; history.clear(); counterMoves.clear(); idx = Threads.size(); // Starts from 0 } -// TimerThread::idle_loop() is where the timer thread waits Resolution milliseconds -// and then calls check_time(). When not searching, thread sleeps until it's woken up. - -void TimerThread::idle_loop() { - - while (!exit) - { - std::unique_lock lk(mutex); - - if (!exit) - sleepCondition.wait_for(lk, std::chrono::milliseconds(run ? Resolution : INT_MAX)); - - lk.unlock(); - - if (!exit && run) - check_time(); - } -} - - // Thread::idle_loop() is where the thread is parked when it has no work to do void Thread::idle_loop() { @@ -174,7 +154,6 @@ void MainThread::join() { void ThreadPool::init() { - timer = new_thread(); push_back(new_thread()); read_uci_options(); } @@ -185,9 +164,6 @@ void ThreadPool::init() { void ThreadPool::exit() { - delete_thread(timer); // As first because check_time() accesses threads data - timer = nullptr; - for (Thread* th : *this) delete_thread(th); diff --git a/src/thread.h b/src/thread.h index fd7343a8..6cceca72 100644 --- a/src/thread.h +++ b/src/thread.h @@ -44,12 +44,12 @@ struct ThreadBase : public std::thread { virtual ~ThreadBase() = default; virtual void idle_loop() = 0; void notify_one(); - void wait(std::atomic& b); - void wait_while(std::atomic& b); + void wait(std::atomic_bool& b); + void wait_while(std::atomic_bool& b); Mutex mutex; ConditionVariable sleepCondition; - std::atomic exit; + std::atomic_bool exit; }; @@ -68,8 +68,8 @@ struct Thread : public ThreadBase { Material::Table materialTable; Endgames endgames; size_t idx, PVIdx; - int maxPly; - std::atomic searching; + int maxPly, callsCnt; + std::atomic_bool searching, resetCallsCnt; Position rootPos; Search::RootMoveVector rootMoves; @@ -80,25 +80,14 @@ struct Thread : public ThreadBase { }; -/// MainThread and TimerThread are derived classes used to characterize the two -/// special threads: the main one and the recurring timer. +/// MainThread is a derived classes used to characterize the the main one struct MainThread : public Thread { MainThread() { thinking = true; } // Avoid a race with start_thinking() virtual void idle_loop(); void join(); void think(); - std::atomic thinking; -}; - -struct TimerThread : public ThreadBase { - - static const int Resolution = 5; // Millisec between two check_time() calls - - virtual void idle_loop(); - void check_time(); - - bool run = false; + std::atomic_bool thinking; }; @@ -108,14 +97,13 @@ struct TimerThread : public ThreadBase { struct ThreadPool : public std::vector { - void init(); // No constructor and destructor, threads rely on globals that should + void init(); // No constructor and destructor, threads rely on globals that should void exit(); // be initialized and valid during the whole thread lifetime. MainThread* main() { return static_cast(at(0)); } void read_uci_options(); void start_thinking(const Position&, const Search::LimitsType&, Search::StateStackPtr&); int64_t nodes_searched(); - TimerThread* timer; }; extern ThreadPool Threads;