Skip to content

Commit

Permalink
Use atomics instead of volatile
Browse files Browse the repository at this point in the history
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 official-stockfish#474
  • Loading branch information
lucasart authored and zamar committed Oct 25, 2015
1 parent 3428a28 commit 00d9e9f
Show file tree
Hide file tree
Showing 4 changed files with 22 additions and 20 deletions.
12 changes: 6 additions & 6 deletions src/search.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@

namespace Search {

volatile SignalsType Signals;
SignalsType Signals;
LimitsType Limits;
StateStackPtr SetupStates;
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -841,7 +841,7 @@ namespace {

if (RootNode && thisThread == Threads.main())
{
Signals.firstRootMove = (moveCount == 1);
Signals.firstRootMove = moveCount == 1;

if (Time.elapsed() > 3000)
sync_cout << "info depth " << depth / ONE_PLY
Expand Down Expand Up @@ -1008,7 +1008,7 @@ namespace {
// 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)
Expand Down Expand Up @@ -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)
Expand Down
9 changes: 5 additions & 4 deletions src/search.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
#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>

Expand Down Expand Up @@ -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<bool> stop, stopOnPonderhit, firstRootMove, failedLowAtRoot;
};

typedef std::unique_ptr<std::stack<StateInfo>> StateStackPtr;

extern volatile SignalsType Signals;
extern SignalsType Signals;
extern LimitsType Limits;
extern StateStackPtr SetupStates;

Expand Down
9 changes: 4 additions & 5 deletions src/thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<bool>& condition) {

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

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; });
Expand All @@ -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;
Expand Down
12 changes: 7 additions & 5 deletions src/thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<bool>& b);
void wait_while(std::atomic<bool>& b);

Mutex mutex;
ConditionVariable sleepCondition;
volatile bool exit = false;
std::atomic<bool> exit;
};


Expand All @@ -72,7 +73,7 @@ struct Thread : public ThreadBase {
Endgames endgames;
size_t idx, PVIdx;
int maxPly;
volatile bool searching;
std::atomic<bool> searching;

Position rootPos;
Search::RootMoveVector rootMoves;
Expand All @@ -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<bool> thinking;
};

struct TimerThread : public ThreadBase {
Expand Down

0 comments on commit 00d9e9f

Please sign in to comment.