mirror of
https://github.com/HChaZZY/Stockfish.git
synced 2025-12-20 17:16:33 +08:00
Get rid of timer thread
Unfortunately std::condition_variable::wait_for() is not accurate in general case and the timer thread can wake up also after tens or even hundreds of millisecs after time has elapsded. CPU load, process priorities, number of concurrent threads, even from other processes, will have effect upon it. Even official documentation says: "This function may block for longer than timeout_duration due to scheduling or resource contention delays." So retire timer and use a polling scheme based on a local thread counter that counts search() calls and a small trick to keep polling frequency constant, independently from the number of threads. Tested for no regression at very fast TC 2+0.05 th 7: LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 32969 W: 6720 L: 6620 D: 19629 TC 2+0.05 th 1: LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 7765 W: 1917 L: 1765 D: 4083 And at STC TC, both single thread LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 15587 W: 3036 L: 2905 D: 9646 And with 7 threads LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 8149 W: 1367 L: 1227 D: 5555 bench: 8639247
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user