From: lucasart Date: Sat, 24 Oct 2015 21:50:51 +0000 (+0100) Subject: Use atomics instead of volatile X-Git-Url: https://git.sesse.net/?p=stockfish;a=commitdiff_plain;h=00d9e9fd283b31e63389af091b158dbc3fedfc0e;hp=3428a2875197c805c25fdd954ba3a24d00845a2a Use atomics instead of volatile Rely on well defined behaviour for message passing, instead of volatile. Three versions have been tested, to make sure this wouldn't cause a slowdown on any platform. v1: Sequentially consistent atomics No mesurable regression, despite the extra memory barriers on x86. Even with 15 threads and extreme time pressure, both acting as a magnifying glass: threads=15, tc=2+0.02 ELO: 2.59 +-3.4 (95%) LOS: 93.3% Total: 18132 W: 4113 L: 3978 D: 10041 threads=7, tc=2+0.02 ELO: -1.64 +-3.6 (95%) LOS: 18.8% Total: 16914 W: 4053 L: 4133 D: 8728 v2: Acquire/Release semantics This version generates no extra barriers for x86 (on the hot path). As expected, no regression either, under the same conditions: threads=15, tc=2+0.02 ELO: 2.85 +-3.3 (95%) LOS: 95.4% Total: 19661 W: 4640 L: 4479 D: 10542 threads=7, tc=2+0.02 ELO: 0.23 +-3.5 (95%) LOS: 55.1% Total: 18108 W: 4326 L: 4314 D: 9468 As suggested by Joona, another test at LTC: threads=15, tc=20+0.05 ELO: 0.64 +-2.6 (95%) LOS: 68.3% Total: 20000 W: 3053 L: 3016 D: 13931 v3: Final version: SeqCst/Relaxed threads=15, tc=10+0.1 ELO: 0.87 +-3.9 (95%) LOS: 67.1% Total: 9541 W: 1478 L: 1454 D: 6609 Resolves #474 --- diff --git a/src/search.cpp b/src/search.cpp index 78e4748f..902ba0fc 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -37,7 +37,7 @@ namespace Search { - volatile SignalsType Signals; + SignalsType Signals; LimitsType Limits; StateStackPtr SetupStates; } @@ -581,8 +581,8 @@ namespace { if (!RootNode) { // Step 2. Check for aborted search and immediate draw - if (Signals.stop || pos.is_draw() || ss->ply >= MAX_PLY) - return ss->ply >= MAX_PLY && !inCheck ? evaluate(pos) + if (Signals.stop.load(std::memory_order_relaxed) || pos.is_draw() || ss->ply >= MAX_PLY) + return ss->ply >= MAX_PLY && !inCheck ? evaluate(pos) : DrawValue[pos.side_to_move()]; // Step 3. Mate distance pruning. Even if we mate at the next move our score @@ -841,7 +841,7 @@ moves_loop: // When in check search starts from here if (RootNode && thisThread == Threads.main()) { - Signals.firstRootMove = (moveCount == 1); + Signals.firstRootMove = moveCount == 1; if (Time.elapsed() > 3000) sync_cout << "info depth " << depth / ONE_PLY @@ -1008,7 +1008,7 @@ moves_loop: // When in check search starts from here // Finished searching the move. If a stop occurred, the return value of // the search cannot be trusted, and we return immediately without // updating best move, PV and TT. - if (Signals.stop) + if (Signals.stop.load(std::memory_order_relaxed)) return VALUE_ZERO; if (RootNode) @@ -1577,7 +1577,7 @@ void check_time() { { bool stillAtFirstMove = Signals.firstRootMove && !Signals.failedLowAtRoot - && elapsed > Time.available() * 75 / 100; + && elapsed > Time.available() * 3 / 4; if ( stillAtFirstMove || elapsed > Time.maximum() - 2 * TimerThread::Resolution) diff --git a/src/search.h b/src/search.h index c7abb9dc..740063b9 100644 --- a/src/search.h +++ b/src/search.h @@ -20,7 +20,8 @@ #ifndef SEARCH_H_INCLUDED #define SEARCH_H_INCLUDED -#include // For std::auto_ptr +#include +#include // For std::unique_ptr #include #include @@ -91,16 +92,16 @@ struct LimitsType { TimePoint startTime; }; -/// The SignalsType struct stores volatile flags updated during the search +/// The SignalsType struct stores atomic flags updated during the search /// typically in an async fashion e.g. to stop the search by the GUI. struct SignalsType { - bool stop, stopOnPonderhit, firstRootMove, failedLowAtRoot; + std::atomic stop, stopOnPonderhit, firstRootMove, failedLowAtRoot; }; typedef std::unique_ptr> StateStackPtr; -extern volatile SignalsType Signals; +extern SignalsType Signals; extern LimitsType Limits; extern StateStackPtr SetupStates; diff --git a/src/thread.cpp b/src/thread.cpp index dd8c398c..eb64f7ee 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -68,16 +68,15 @@ void ThreadBase::notify_one() { // ThreadBase::wait() set the thread to sleep until 'condition' turns true -void ThreadBase::wait(volatile const bool& condition) { +void ThreadBase::wait(std::atomic& condition) { std::unique_lock lk(mutex); - sleepCondition.wait(lk, [&]{ return condition; }); + sleepCondition.wait(lk, [&]{ return bool(condition); }); } // ThreadBase::wait_while() set the thread to sleep until 'condition' turns false - -void ThreadBase::wait_while(volatile const bool& condition) { +void ThreadBase::wait_while(std::atomic& condition) { std::unique_lock lk(mutex); sleepCondition.wait(lk, [&]{ return !condition; }); @@ -87,7 +86,7 @@ void ThreadBase::wait_while(volatile const bool& condition) { // Thread c'tor makes some init but does not launch any execution thread that // will be started only when c'tor returns. -Thread::Thread() /* : splitPoints() */ { // Initialization of non POD broken in MSVC +Thread::Thread() { searching = false; maxPly = 0; diff --git a/src/thread.h b/src/thread.h index 459b1ddd..abb7a223 100644 --- a/src/thread.h +++ b/src/thread.h @@ -44,15 +44,16 @@ const size_t MAX_THREADS = 128; struct ThreadBase : public std::thread { + ThreadBase() { exit = false; } virtual ~ThreadBase() = default; virtual void idle_loop() = 0; void notify_one(); - void wait(volatile const bool& b); - void wait_while(volatile const bool& b); + void wait(std::atomic& b); + void wait_while(std::atomic& b); Mutex mutex; ConditionVariable sleepCondition; - volatile bool exit = false; + std::atomic exit; }; @@ -72,7 +73,7 @@ struct Thread : public ThreadBase { Endgames endgames; size_t idx, PVIdx; int maxPly; - volatile bool searching; + std::atomic searching; Position rootPos; Search::RootMoveVector rootMoves; @@ -87,10 +88,11 @@ struct Thread : public ThreadBase { /// special threads: the main one and the recurring timer. struct MainThread : public Thread { + MainThread() { thinking = true; } // Avoid a race with start_thinking() virtual void idle_loop(); void join(); void think(); - volatile bool thinking = true; // Avoid a race with start_thinking() + std::atomic thinking; }; struct TimerThread : public ThreadBase {