Only main thread checks time
authorMarco Costalba <mcostalba@gmail.com>
Sat, 24 Jun 2017 05:15:46 +0000 (07:15 +0200)
committerJoona Kiiski <joona@zoox.com>
Thu, 29 Jun 2017 00:03:35 +0000 (17:03 -0700)
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

src/position.cpp
src/position.h
src/search.cpp
src/thread.cpp
src/thread.h

index 67cf2a02522c7f062d0cc41c647e01bb6463e91d..5df07a092433c3dd1975102722840fb696a110fe 100644 (file)
@@ -690,7 +690,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
   assert(is_ok(m));
   assert(&newSt != st);
 
-  ++nodes;
+  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
index 8342df3c1efa067dea1f688fdb42d719d33c68f6..22afb3e13278be4e09d8f363e445bcbf4a75864c 100644 (file)
@@ -134,8 +134,6 @@ public:
   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;
@@ -152,8 +150,6 @@ public:
   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;
@@ -187,8 +183,6 @@ private:
   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;
@@ -353,22 +347,6 @@ inline int Position::rule50_count() const {
   return st->rule50;
 }
 
-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
index 12cbffe020b25f7bf92921dc860e23e83eb3d919..e6c3cd701485947c1c174b98f037661dca03409e 100644 (file)
@@ -154,7 +154,6 @@ namespace {
   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 check_time();
 
 } // namespace
 
@@ -193,7 +192,6 @@ void Search::clear() {
 
   for (Thread* th : Threads)
   {
-      th->resetCalls = true;
       th->counterMoves.fill(MOVE_NONE);
       th->history.fill(0);
 
@@ -204,6 +202,7 @@ void Search::clear() {
       th->counterMoveHistory[NO_PIECE][0].fill(CounterMovePruneThreshold - 1);
   }
 
+  Threads.main()->callsCnt = 0;
   Threads.main()->previousScore = VALUE_INFINITE;
 }
 
@@ -400,9 +399,6 @@ void Thread::search() {
           {
               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
@@ -567,26 +563,8 @@ namespace {
     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)
@@ -674,7 +652,7 @@ namespace {
 
             if (err != TB::ProbeState::FAIL)
             {
-                pos.increment_tbHits();
+                thisThread->tbHits.fetch_add(1, std::memory_order_relaxed);
 
                 int drawScore = TB::UseRule50 ? 1 : 0;
 
@@ -960,7 +938,7 @@ moves_loop: // When in check search starts from here
           ss->moveCount = --moveCount;
           continue;
       }
-      
+
       if (move == ttMove && captureOrPromotion)
           ttCapture = true;
 
@@ -983,11 +961,10 @@ moves_loop: // When in check search starts from here
               r -= r ? ONE_PLY : DEPTH_ZERO;
           else
           {
-          
               // Increase reduction if ttMove is a capture
               if (ttCapture)
                   r += ONE_PLY;
-          
+
               // Increase reduction for cut nodes
               if (cutNode)
                   r += 2 * ONE_PLY;
@@ -1482,13 +1459,21 @@ moves_loop: // When in check search starts from here
     return best;
   }
 
+} // namespace
 
   // 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 check_time() {
+  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;
@@ -1509,8 +1494,6 @@ moves_loop: // When in check search starts from here
             Signals.stop = true;
   }
 
-} // namespace
-
 
 /// 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.
index c80b9bd1fba91c8a1c4ae4f677de646959a0d50a..b4e9f8aa0269f21cfab7eb77a07f980f98634856 100644 (file)
@@ -34,9 +34,9 @@ ThreadPool Threads; // Global object
 
 Thread::Thread() {
 
-  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);
@@ -163,7 +163,7 @@ uint64_t ThreadPool::nodes_searched() const {
 
   uint64_t nodes = 0;
   for (Thread* th : *this)
-      nodes += th->nodes;
+      nodes += th->nodes.load(std::memory_order_relaxed);
   return nodes;
 }
 
@@ -174,7 +174,7 @@ uint64_t ThreadPool::tb_hits() const {
 
   uint64_t hits = 0;
   for (Thread* th : *this)
-      hits += th->tbHits;
+      hits += th->tbHits.load(std::memory_order_relaxed);
   return hits;
 }
 
@@ -211,8 +211,8 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states,
   for (Thread* th : Threads)
   {
       th->maxPly = 0;
-      th->tbHits = 0;
       th->nodes = 0;
+      th->tbHits = 0;
       th->rootDepth = DEPTH_ZERO;
       th->rootMoves = rootMoves;
       th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th);
index 0f117c2d2a7426040424abce254b5ee57ed67618..c254047c23f4ce90a04415bcc97090f3c57aae0b 100644 (file)
@@ -60,15 +60,13 @@ public:
   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;
-  std::atomic_bool resetCalls;
   CounterMoveStat counterMoves;
   ButterflyHistory history;
   CounterMoveHistoryStat counterMoveHistory;
@@ -79,10 +77,12 @@ public:
 
 struct MainThread : public Thread {
   virtual void search();
+  void check_time();
 
   bool easyMovePlayed, failedLow;
   double bestMoveChanges;
   Value previousScore;
+  int callsCnt = 0;
 };