summary |
shortlog |
log |
commit | commitdiff |
tree
raw |
patch |
inline | side by side (from parent 1:
3428a28)
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
- volatile SignalsType Signals;
LimitsType Limits;
StateStackPtr SetupStates;
}
LimitsType Limits;
StateStackPtr SetupStates;
}
if (!RootNode)
{
// Step 2. Check for aborted search and immediate draw
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
: DrawValue[pos.side_to_move()];
// Step 3. Mate distance pruning. Even if we mate at the next move our score
if (RootNode && thisThread == Threads.main())
{
if (RootNode && thisThread == Threads.main())
{
- Signals.firstRootMove = (moveCount == 1);
+ Signals.firstRootMove = moveCount == 1;
if (Time.elapsed() > 3000)
sync_cout << "info depth " << depth / ONE_PLY
if (Time.elapsed() > 3000)
sync_cout << "info depth " << depth / ONE_PLY
// 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.
// 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.load(std::memory_order_relaxed))
return VALUE_ZERO;
if (RootNode)
return VALUE_ZERO;
if (RootNode)
{
bool stillAtFirstMove = Signals.firstRootMove
&& !Signals.failedLowAtRoot
{
bool stillAtFirstMove = Signals.firstRootMove
&& !Signals.failedLowAtRoot
- && elapsed > Time.available() * 75 / 100;
+ && elapsed > Time.available() * 3 / 4;
if ( stillAtFirstMove
|| elapsed > Time.maximum() - 2 * TimerThread::Resolution)
if ( stillAtFirstMove
|| elapsed > Time.maximum() - 2 * TimerThread::Resolution)
#ifndef SEARCH_H_INCLUDED
#define SEARCH_H_INCLUDED
#ifndef SEARCH_H_INCLUDED
#define SEARCH_H_INCLUDED
-#include <memory> // For std::auto_ptr
+#include <atomic>
+#include <memory> // For std::unique_ptr
#include <stack>
#include <vector>
#include <stack>
#include <vector>
-/// 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 {
/// typically in an async fashion e.g. to stop the search by the GUI.
struct SignalsType {
- bool stop, stopOnPonderhit, firstRootMove, failedLowAtRoot;
+ std::atomic<bool> stop, stopOnPonderhit, firstRootMove, failedLowAtRoot;
};
typedef std::unique_ptr<std::stack<StateInfo>> StateStackPtr;
};
typedef std::unique_ptr<std::stack<StateInfo>> StateStackPtr;
-extern volatile SignalsType Signals;
+extern SignalsType Signals;
extern LimitsType Limits;
extern StateStackPtr SetupStates;
extern LimitsType Limits;
extern StateStackPtr SetupStates;
// ThreadBase::wait() set the thread to sleep until 'condition' turns true
// ThreadBase::wait() set the thread to sleep until 'condition' turns true
-void ThreadBase::wait(volatile const bool& condition) {
+void ThreadBase::wait(std::atomic<bool>& condition) {
std::unique_lock<Mutex> lk(mutex);
std::unique_lock<Mutex> 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
}
// 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<bool>& condition) {
std::unique_lock<Mutex> lk(mutex);
sleepCondition.wait(lk, [&]{ return !condition; });
std::unique_lock<Mutex> lk(mutex);
sleepCondition.wait(lk, [&]{ return !condition; });
// Thread c'tor makes some init but does not launch any execution thread that
// will be started only when c'tor returns.
// 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
searching = false;
maxPly = 0;
searching = false;
maxPly = 0;
struct ThreadBase : public std::thread {
struct ThreadBase : public std::thread {
+ ThreadBase() { exit = false; }
virtual ~ThreadBase() = default;
virtual void idle_loop() = 0;
void notify_one();
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<bool>& b);
+ void wait_while(std::atomic<bool>& b);
Mutex mutex;
ConditionVariable sleepCondition;
Mutex mutex;
ConditionVariable sleepCondition;
- volatile bool exit = false;
+ std::atomic<bool> exit;
Endgames endgames;
size_t idx, PVIdx;
int maxPly;
Endgames endgames;
size_t idx, PVIdx;
int maxPly;
- volatile bool searching;
+ std::atomic<bool> searching;
Position rootPos;
Search::RootMoveVector rootMoves;
Position rootPos;
Search::RootMoveVector rootMoves;
/// special threads: the main one and the recurring timer.
struct MainThread : public Thread {
/// 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();
virtual void idle_loop();
void join();
void think();
- volatile bool thinking = true; // Avoid a race with start_thinking()
+ std::atomic<bool> thinking;
};
struct TimerThread : public ThreadBase {
};
struct TimerThread : public ThreadBase {