mirror of
https://github.com/sockspls/badfish
synced 2025-04-29 16:23:09 +00:00
Use atomics instead of volatile
Rely on well defined behaviour for message passing, instead of volatile. Two 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
This commit is contained in:
parent
307a5a4f63
commit
a4cdc35ea8
4 changed files with 22 additions and 20 deletions
|
@ -37,7 +37,7 @@
|
||||||
|
|
||||||
namespace Search {
|
namespace Search {
|
||||||
|
|
||||||
volatile SignalsType Signals;
|
SignalsType Signals;
|
||||||
LimitsType Limits;
|
LimitsType Limits;
|
||||||
StateStackPtr SetupStates;
|
StateStackPtr SetupStates;
|
||||||
}
|
}
|
||||||
|
@ -581,7 +581,7 @@ namespace {
|
||||||
if (!RootNode)
|
if (!RootNode)
|
||||||
{
|
{
|
||||||
// Step 2. Check for aborted search and immediate draw
|
// Step 2. Check for aborted search and immediate draw
|
||||||
if (Signals.stop || pos.is_draw() || ss->ply >= MAX_PLY)
|
if (Signals.stop.load(std::memory_order_relaxed) || pos.is_draw() || ss->ply >= MAX_PLY)
|
||||||
return ss->ply >= MAX_PLY && !inCheck ? evaluate(pos)
|
return ss->ply >= MAX_PLY && !inCheck ? evaluate(pos)
|
||||||
: DrawValue[pos.side_to_move()];
|
: DrawValue[pos.side_to_move()];
|
||||||
|
|
||||||
|
@ -841,7 +841,7 @@ moves_loop: // When in check search starts from here
|
||||||
|
|
||||||
if (RootNode && thisThread == Threads.main())
|
if (RootNode && thisThread == Threads.main())
|
||||||
{
|
{
|
||||||
Signals.firstRootMove = (moveCount == 1);
|
Signals.firstRootMove = moveCount == 1;
|
||||||
|
|
||||||
if (Time.elapsed() > 3000)
|
if (Time.elapsed() > 3000)
|
||||||
sync_cout << "info depth " << depth / ONE_PLY
|
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
|
// Finished searching the move. If a stop occurred, the return value of
|
||||||
// the search cannot be trusted, and we return immediately without
|
// the search cannot be trusted, and we return immediately without
|
||||||
// updating best move, PV and TT.
|
// updating best move, PV and TT.
|
||||||
if (Signals.stop)
|
if (Signals.stop.load(std::memory_order_relaxed))
|
||||||
return VALUE_ZERO;
|
return VALUE_ZERO;
|
||||||
|
|
||||||
if (RootNode)
|
if (RootNode)
|
||||||
|
@ -1577,7 +1577,7 @@ void check_time() {
|
||||||
{
|
{
|
||||||
bool stillAtFirstMove = Signals.firstRootMove
|
bool stillAtFirstMove = Signals.firstRootMove
|
||||||
&& !Signals.failedLowAtRoot
|
&& !Signals.failedLowAtRoot
|
||||||
&& elapsed > Time.available() * 75 / 100;
|
&& elapsed > Time.available() * 3 / 4;
|
||||||
|
|
||||||
if ( stillAtFirstMove
|
if ( stillAtFirstMove
|
||||||
|| elapsed > Time.maximum() - 2 * TimerThread::Resolution)
|
|| elapsed > Time.maximum() - 2 * TimerThread::Resolution)
|
||||||
|
|
|
@ -20,7 +20,8 @@
|
||||||
#ifndef SEARCH_H_INCLUDED
|
#ifndef SEARCH_H_INCLUDED
|
||||||
#define SEARCH_H_INCLUDED
|
#define SEARCH_H_INCLUDED
|
||||||
|
|
||||||
#include <memory> // For std::auto_ptr
|
#include <atomic>
|
||||||
|
#include <memory> // For std::unique_ptr
|
||||||
#include <stack>
|
#include <stack>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -91,16 +92,16 @@ struct LimitsType {
|
||||||
TimePoint startTime;
|
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.
|
/// typically in an async fashion e.g. to stop the search by the GUI.
|
||||||
|
|
||||||
struct SignalsType {
|
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 LimitsType Limits;
|
||||||
extern StateStackPtr SetupStates;
|
extern StateStackPtr SetupStates;
|
||||||
|
|
||||||
|
|
|
@ -68,16 +68,15 @@ void ThreadBase::notify_one() {
|
||||||
|
|
||||||
// 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(std::atomic<bool>& condition) {
|
||||||
void ThreadBase::wait_while(volatile const bool& condition) {
|
|
||||||
|
|
||||||
std::unique_lock<Mutex> lk(mutex);
|
std::unique_lock<Mutex> lk(mutex);
|
||||||
sleepCondition.wait(lk, [&]{ return !condition; });
|
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
|
// Thread c'tor makes some init but does not launch any execution thread that
|
||||||
// will be started only when c'tor returns.
|
// will be started only when c'tor returns.
|
||||||
|
|
||||||
Thread::Thread() /* : splitPoints() */ { // Initialization of non POD broken in MSVC
|
Thread::Thread() {
|
||||||
|
|
||||||
searching = false;
|
searching = false;
|
||||||
maxPly = 0;
|
maxPly = 0;
|
||||||
|
|
12
src/thread.h
12
src/thread.h
|
@ -44,15 +44,16 @@ const size_t MAX_THREADS = 128;
|
||||||
|
|
||||||
struct ThreadBase : public std::thread {
|
struct ThreadBase : public std::thread {
|
||||||
|
|
||||||
|
ThreadBase() { exit = false; }
|
||||||
virtual ~ThreadBase() = default;
|
virtual ~ThreadBase() = default;
|
||||||
virtual void idle_loop() = 0;
|
virtual void idle_loop() = 0;
|
||||||
void notify_one();
|
void notify_one();
|
||||||
void wait(volatile const bool& b);
|
void wait(std::atomic<bool>& b);
|
||||||
void wait_while(volatile const bool& b);
|
void wait_while(std::atomic<bool>& b);
|
||||||
|
|
||||||
Mutex mutex;
|
Mutex mutex;
|
||||||
ConditionVariable sleepCondition;
|
ConditionVariable sleepCondition;
|
||||||
volatile bool exit = false;
|
std::atomic<bool> exit;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -72,7 +73,7 @@ struct Thread : public ThreadBase {
|
||||||
Endgames endgames;
|
Endgames endgames;
|
||||||
size_t idx, PVIdx;
|
size_t idx, PVIdx;
|
||||||
int maxPly;
|
int maxPly;
|
||||||
volatile bool searching;
|
std::atomic<bool> searching;
|
||||||
|
|
||||||
Position rootPos;
|
Position rootPos;
|
||||||
Search::RootMoveVector rootMoves;
|
Search::RootMoveVector rootMoves;
|
||||||
|
@ -87,10 +88,11 @@ struct Thread : public ThreadBase {
|
||||||
/// special threads: the main one and the recurring timer.
|
/// special threads: the main one and the recurring timer.
|
||||||
|
|
||||||
struct MainThread : public Thread {
|
struct MainThread : public Thread {
|
||||||
|
MainThread() { thinking = true; } // Avoid a race with start_thinking()
|
||||||
virtual void idle_loop();
|
virtual void idle_loop();
|
||||||
void join();
|
void join();
|
||||||
void think();
|
void think();
|
||||||
volatile bool thinking = true; // Avoid a race with start_thinking()
|
std::atomic<bool> thinking;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TimerThread : public ThreadBase {
|
struct TimerThread : public ThreadBase {
|
||||||
|
|
Loading…
Add table
Reference in a new issue