The main change of the patch is that now time check
is done only by main thread. In the past, before lazy
SMP, we needed all the threds to check for available
time because main thread could have been blocked on
a split point, now this is no more the case and main
thread can do the job alone, greatly simplifying the logic.
Verified for regression testing on STC with 7 threads:
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 11895 W: 1741 L: 1608 D: 8546
No functional change.
Closes #1152
assert(is_ok(m));
assert(&newSt != st);
assert(is_ok(m));
assert(&newSt != st);
+ thisThread->nodes.fetch_add(1, std::memory_order_relaxed);
Key k = st->key ^ Zobrist::side;
// Copy some fields of the old state to our new StateInfo object except the
Key k = st->key ^ Zobrist::side;
// Copy some fields of the old state to our new StateInfo object except the
void undo_move(Move m);
void do_null_move(StateInfo& newSt);
void undo_null_move();
void undo_move(Move m);
void do_null_move(StateInfo& newSt);
void undo_null_move();
- void increment_nodes();
- void increment_tbHits();
// Static Exchange Evaluation
bool see_ge(Move m, Value threshold = VALUE_ZERO) const;
// Static Exchange Evaluation
bool see_ge(Move m, Value threshold = VALUE_ZERO) const;
int game_ply() const;
bool is_chess960() const;
Thread* this_thread() const;
int game_ply() const;
bool is_chess960() const;
Thread* this_thread() const;
- uint64_t nodes_searched() const;
- uint64_t tb_hits() const;
bool is_draw(int ply) const;
int rule50_count() const;
Score psq_score() const;
bool is_draw(int ply) const;
int rule50_count() const;
Score psq_score() const;
int castlingRightsMask[SQUARE_NB];
Square castlingRookSquare[CASTLING_RIGHT_NB];
Bitboard castlingPath[CASTLING_RIGHT_NB];
int castlingRightsMask[SQUARE_NB];
Square castlingRookSquare[CASTLING_RIGHT_NB];
Bitboard castlingPath[CASTLING_RIGHT_NB];
- uint64_t nodes;
- uint64_t tbHits;
int gamePly;
Color sideToMove;
Thread* thisThread;
int gamePly;
Color sideToMove;
Thread* thisThread;
-inline uint64_t Position::nodes_searched() const {
- return nodes;
-}
-
-inline void Position::increment_nodes() {
- nodes++;
-}
-
-inline uint64_t Position::tb_hits() const {
- return tbHits;
-}
-
-inline void Position::increment_tbHits() {
- tbHits++;
-}
-
inline bool Position::opposite_bishops() const {
return pieceCount[W_BISHOP] == 1
&& pieceCount[B_BISHOP] == 1
inline bool Position::opposite_bishops() const {
return pieceCount[W_BISHOP] == 1
&& pieceCount[B_BISHOP] == 1
void update_pv(Move* pv, Move move, Move* childPv);
void update_cm_stats(Stack* ss, Piece pc, Square s, int bonus);
void update_stats(const Position& pos, Stack* ss, Move move, Move* quiets, int quietsCnt, int bonus);
void update_pv(Move* pv, Move move, Move* childPv);
void update_cm_stats(Stack* ss, Piece pc, Square s, int bonus);
void update_stats(const Position& pos, Stack* ss, Move move, Move* quiets, int quietsCnt, int bonus);
for (Thread* th : Threads)
{
for (Thread* th : Threads)
{
th->counterMoves.fill(MOVE_NONE);
th->history.fill(0);
th->counterMoves.fill(MOVE_NONE);
th->history.fill(0);
th->counterMoveHistory[NO_PIECE][0].fill(CounterMovePruneThreshold - 1);
}
th->counterMoveHistory[NO_PIECE][0].fill(CounterMovePruneThreshold - 1);
}
+ Threads.main()->callsCnt = 0;
Threads.main()->previousScore = VALUE_INFINITE;
}
Threads.main()->previousScore = VALUE_INFINITE;
}
{
bestValue = ::search<PV>(rootPos, ss, alpha, beta, rootDepth, false, false);
{
bestValue = ::search<PV>(rootPos, ss, alpha, beta, rootDepth, false, false);
- this->tbHits = rootPos.tb_hits();
- this->nodes = rootPos.nodes_searched();
-
// Bring the best move to the front. It is critical that sorting
// is done with a stable algorithm because all the values but the
// first and eventually the new best one are set to -VALUE_INFINITE
// Bring the best move to the front. It is critical that sorting
// is done with a stable algorithm because all the values but the
// first and eventually the new best one are set to -VALUE_INFINITE
ss->ply = (ss-1)->ply + 1;
// Check for the available remaining time
ss->ply = (ss-1)->ply + 1;
// Check for the available remaining time
- if (thisThread->resetCalls.load(std::memory_order_relaxed))
- {
- thisThread->resetCalls = false;
-
- thisThread->tbHits = pos.tb_hits();
- thisThread->nodes = pos.nodes_searched();
-
- // At low node count increase the checking rate to about 0.1% of nodes
- // otherwise use a default value.
- thisThread->callsCnt = Limits.nodes ? std::min(4096, int(Limits.nodes / 1024))
- : 4096;
- }
-
- if (--thisThread->callsCnt <= 0)
- {
- for (Thread* th : Threads)
- th->resetCalls = true;
-
- check_time();
- }
+ if (thisThread == Threads.main())
+ static_cast<MainThread*>(thisThread)->check_time();
// Used to send selDepth info to GUI
if (PvNode && thisThread->maxPly < ss->ply)
// Used to send selDepth info to GUI
if (PvNode && thisThread->maxPly < ss->ply)
if (err != TB::ProbeState::FAIL)
{
if (err != TB::ProbeState::FAIL)
{
- pos.increment_tbHits();
+ thisThread->tbHits.fetch_add(1, std::memory_order_relaxed);
int drawScore = TB::UseRule50 ? 1 : 0;
int drawScore = TB::UseRule50 ? 1 : 0;
ss->moveCount = --moveCount;
continue;
}
ss->moveCount = --moveCount;
continue;
}
if (move == ttMove && captureOrPromotion)
ttCapture = true;
if (move == ttMove && captureOrPromotion)
ttCapture = true;
r -= r ? ONE_PLY : DEPTH_ZERO;
else
{
r -= r ? ONE_PLY : DEPTH_ZERO;
else
{
// Increase reduction if ttMove is a capture
if (ttCapture)
r += ONE_PLY;
// Increase reduction if ttMove is a capture
if (ttCapture)
r += ONE_PLY;
// Increase reduction for cut nodes
if (cutNode)
r += 2 * ONE_PLY;
// Increase reduction for cut nodes
if (cutNode)
r += 2 * ONE_PLY;
// 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.
// 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 MainThread::check_time() {
+
+ if (--callsCnt > 0)
+ return;
+
+ // At low node count increase the checking rate to about 0.1% of nodes
+ // otherwise use a default value.
+ callsCnt = Limits.nodes ? std::min(4096, int(Limits.nodes / 1024)) : 4096;
- static std::atomic<TimePoint> lastInfoTime = { now() };
+ static TimePoint lastInfoTime = now();
int elapsed = Time.elapsed();
TimePoint tick = Limits.startTime + elapsed;
int elapsed = Time.elapsed();
TimePoint tick = Limits.startTime + elapsed;
/// UCI::pv() formats PV information according to the UCI protocol. UCI requires
/// that all (if any) unsearched PV lines are sent using a previous search score.
/// UCI::pv() formats PV information according to the UCI protocol. UCI requires
/// that all (if any) unsearched PV lines are sent using a previous search score.
- resetCalls = exit = false;
- maxPly = callsCnt = 0;
- tbHits = 0;
+ exit = false;
+ maxPly = 0;
+ nodes = tbHits = 0;
idx = Threads.size(); // Start from 0
std::unique_lock<Mutex> lk(mutex);
idx = Threads.size(); // Start from 0
std::unique_lock<Mutex> lk(mutex);
uint64_t nodes = 0;
for (Thread* th : *this)
uint64_t nodes = 0;
for (Thread* th : *this)
+ nodes += th->nodes.load(std::memory_order_relaxed);
uint64_t hits = 0;
for (Thread* th : *this)
uint64_t hits = 0;
for (Thread* th : *this)
+ hits += th->tbHits.load(std::memory_order_relaxed);
for (Thread* th : Threads)
{
th->maxPly = 0;
for (Thread* th : Threads)
{
th->maxPly = 0;
th->rootDepth = DEPTH_ZERO;
th->rootMoves = rootMoves;
th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th);
th->rootDepth = DEPTH_ZERO;
th->rootMoves = rootMoves;
th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th);
Material::Table materialTable;
Endgames endgames;
size_t idx, PVIdx;
Material::Table materialTable;
Endgames endgames;
size_t idx, PVIdx;
- int maxPly, callsCnt;
- std::atomic<uint64_t> tbHits;
- std::atomic<uint64_t> nodes;
+ int maxPly;
+ std::atomic<uint64_t> nodes, tbHits;
Position rootPos;
Search::RootMoves rootMoves;
std::atomic<Depth> rootDepth;
Depth completedDepth;
Position rootPos;
Search::RootMoves rootMoves;
std::atomic<Depth> rootDepth;
Depth completedDepth;
- std::atomic_bool resetCalls;
CounterMoveStat counterMoves;
ButterflyHistory history;
CounterMoveHistoryStat counterMoveHistory;
CounterMoveStat counterMoves;
ButterflyHistory history;
CounterMoveHistoryStat counterMoveHistory;
struct MainThread : public Thread {
virtual void search();
struct MainThread : public Thread {
virtual void search();
bool easyMovePlayed, failedLow;
double bestMoveChanges;
Value previousScore;
bool easyMovePlayed, failedLow;
double bestMoveChanges;
Value previousScore;