]> git.sesse.net Git - stockfish/blobdiff - src/search.cpp
Further reduce sleep lock contention
[stockfish] / src / search.cpp
index f5e1a00002b2fc1fc21035d3835c710632132ca7..ac870f5bf617aa8d75fbdede2e5e04048eaf970c 100644 (file)
@@ -94,8 +94,8 @@ namespace {
     int ActiveThreads;
     volatile bool AllThreadsShouldExit;
     Thread threads[MAX_THREADS];
-    Lock MPLock, WaitLock;
-    WaitCondition WaitCond[MAX_THREADS];
+    Lock MPLock, SleepLock[MAX_THREADS];
+    WaitCondition SleepCond[MAX_THREADS];
   };
 
 
@@ -263,6 +263,7 @@ namespace {
   // Multi-threads related variables
   Depth MinimumSplitDepth;
   int MaxThreadsPerSplitPoint;
+  bool UseSleepingThreads;
   ThreadsManager ThreadsMgr;
 
   // Node counters, used only by thread[0] but try to keep in different cache
@@ -347,7 +348,7 @@ void init_search() {
   // Init reductions array
   for (hd = 1; hd < 64; hd++) for (mc = 1; mc < 64; mc++)
   {
-      double    pvRed = 0.33 + log(double(hd)) * log(double(mc)) / 4.5;
+      double    pvRed = log(double(hd)) * log(double(mc)) / 3.0;
       double nonPVRed = 0.33 + log(double(hd)) * log(double(mc)) / 2.25;
       ReductionMatrix[PV][hd][mc]    = (int8_t) (   pvRed >= 1.0 ? floor(   pvRed * int(ONE_PLY)) : 0);
       ReductionMatrix[NonPV][hd][mc] = (int8_t) (nonPVRed >= 1.0 ? floor(nonPVRed * int(ONE_PLY)) : 0);
@@ -455,6 +456,7 @@ bool think(Position& pos, bool infinite, bool ponder, int time[], int increment[
   MaxThreadsPerSplitPoint = Options["Maximum Number of Threads per Split Point"].value<int>();
   MultiPV                 = Options["MultiPV"].value<int>();
   UseLogFile              = Options["Use Search Log"].value<bool>();
+  UseSleepingThreads      = Options["Use Sleeping Threads"].value<bool>();
 
   if (UseLogFile)
       LogFile.open(Options["Search Log Filename"].value<std::string>().c_str(), std::ios::out | std::ios::app);
@@ -466,7 +468,7 @@ bool think(Position& pos, bool infinite, bool ponder, int time[], int increment[
   if (newActiveThreads != ThreadsMgr.active_threads())
   {
       ThreadsMgr.set_active_threads(newActiveThreads);
-      init_eval(ThreadsMgr.active_threads());
+      init_eval(newActiveThreads);
   }
 
   // Wake up needed threads
@@ -1070,7 +1072,6 @@ namespace {
         && !isCheck
         &&  refinedValue < beta - razor_margin(depth)
         &&  ttMove == MOVE_NONE
-        &&  (ss-1)->currentMove != MOVE_NULL
         && !value_is_mate(beta)
         && !pos.has_pawn_on_7th(pos.side_to_move()))
     {
@@ -1287,6 +1288,17 @@ split_point_start: // At split points actual search starts from here
 
               continue;
           }
+
+          // Prune neg. see moves at low depths
+          if (   predictedDepth < 2 * ONE_PLY
+              && bestValue > value_mated_in(PLY_MAX)
+              && pos.see_sign(move) < 0)
+          {
+              if (SpNode)
+                  lock_grab(&(sp->lock));
+
+              continue;
+          }
       }
 
       // Step 13. Make the move
@@ -2189,6 +2201,9 @@ split_point_start: // At split points actual search starts from here
 
     assert(threadID >= 0 && threadID < MAX_THREADS);
 
+    int i;
+    bool allFinished = false;
+
     while (true)
     {
         // Slave threads can exit as soon as AllThreadsShouldExit raises,
@@ -2202,22 +2217,33 @@ split_point_start: // At split points actual search starts from here
 
         // If we are not thinking, wait for a condition to be signaled
         // instead of wasting CPU time polling for work.
-        while (threadID >= ActiveThreads || threads[threadID].state == THREAD_INITIALIZING)
+        while (   threadID >= ActiveThreads || threads[threadID].state == THREAD_INITIALIZING
+               || (UseSleepingThreads && threads[threadID].state == THREAD_AVAILABLE))
         {
-            assert(!sp);
-            assert(threadID != 0);
+            assert(!sp || UseSleepingThreads);
+            assert(threadID != 0 || UseSleepingThreads);
 
-            if (AllThreadsShouldExit)
-                break;
+            if (threads[threadID].state == THREAD_INITIALIZING)
+                threads[threadID].state = THREAD_AVAILABLE;
 
-            threads[threadID].state = THREAD_AVAILABLE;
+            // Grab the lock to avoid races with wake_sleeping_thread()
+            lock_grab(&SleepLock[threadID]);
+
+            // If we are master and all slaves have finished do not go to sleep
+            for (i = 0; sp && i < ActiveThreads && !sp->slaves[i]; i++) {}
+            allFinished = (i == ActiveThreads);
 
-            lock_grab(&WaitLock);
+            if (allFinished || AllThreadsShouldExit)
+            {
+                lock_release(&SleepLock[threadID]);
+                break;
+            }
 
-            if (threadID >= ActiveThreads || threads[threadID].state == THREAD_INITIALIZING)
-                cond_wait(&WaitCond[threadID], &WaitLock);
+            // Do sleep here after retesting sleep conditions
+            if (threadID >= ActiveThreads || threads[threadID].state == THREAD_AVAILABLE)
+                cond_wait(&SleepCond[threadID], &SleepLock[threadID]);
 
-            lock_release(&WaitLock);
+            lock_release(&SleepLock[threadID]);
         }
 
         // If this thread has been assigned work, launch a search
@@ -2235,20 +2261,25 @@ split_point_start: // At split points actual search starts from here
 
             if (tsp->pvNode)
                 search<PV, true>(pos, ss, tsp->alpha, tsp->beta, tsp->depth, tsp->ply);
-            else {
+            else
                 search<NonPV, true>(pos, ss, tsp->alpha, tsp->beta, tsp->depth, tsp->ply);
-            }
+
             assert(threads[threadID].state == THREAD_SEARCHING);
 
             threads[threadID].state = THREAD_AVAILABLE;
+
+            // Wake up master thread so to allow it to return from the idle loop in
+            // case we are the last slave of the split point.
+            if (UseSleepingThreads && threadID != tsp->master && threads[tsp->master].state == THREAD_AVAILABLE)
+                wake_sleeping_thread(tsp->master);
         }
 
         // If this thread is the master of a split point and all slaves have
         // finished their work at this split point, return from the idle loop.
-        int i = 0;
-        for ( ; sp && i < ActiveThreads && !sp->slaves[i]; i++) {}
+        for (i = 0; sp && i < ActiveThreads && !sp->slaves[i]; i++) {}
+        allFinished = (i == ActiveThreads);
 
-        if (i == ActiveThreads)
+        if (allFinished)
         {
             // Because sp->slaves[] is reset under lock protection,
             // be sure sp->lock has been released before to return.
@@ -2277,10 +2308,12 @@ split_point_start: // At split points actual search starts from here
 
     // Initialize global locks
     lock_init(&MPLock);
-    lock_init(&WaitLock);
 
     for (i = 0; i < MAX_THREADS; i++)
-        cond_init(&WaitCond[i]);
+    {
+        lock_init(&SleepLock[i]);
+        cond_init(&SleepCond[i]);
+    }
 
     // Initialize splitPoints[] locks
     for (i = 0; i < MAX_THREADS; i++)
@@ -2341,12 +2374,14 @@ split_point_start: // At split points actual search starts from here
         for (int j = 0; j < MAX_ACTIVE_SPLIT_POINTS; j++)
             lock_destroy(&(threads[i].splitPoints[j].lock));
 
-    lock_destroy(&WaitLock);
     lock_destroy(&MPLock);
 
     // Now we can safely destroy the wait conditions
     for (int i = 0; i < MAX_THREADS; i++)
-        cond_destroy(&WaitCond[i]);
+    {
+        lock_destroy(&SleepLock[i]);
+        cond_destroy(&SleepCond[i]);
+    }
   }
 
 
@@ -2458,6 +2493,7 @@ split_point_start: // At split points actual search starts from here
 
     // Initialize the split point object
     splitPoint.parent = masterThread.splitPoint;
+    splitPoint.master = master;
     splitPoint.stopRequest = false;
     splitPoint.ply = ply;
     splitPoint.depth = depth;
@@ -2507,6 +2543,9 @@ split_point_start: // At split points actual search starts from here
             assert(i == master || threads[i].state == THREAD_BOOKED);
 
             threads[i].state = THREAD_WORKISWAITING; // This makes the slave to exit from idle_loop()
+
+            if (UseSleepingThreads && i != master)
+                wake_sleeping_thread(i);
         }
 
     // Everything is set up. The master thread enters the idle loop, from
@@ -2530,14 +2569,14 @@ split_point_start: // At split points actual search starts from here
   }
 
 
-  // wake_sleeping_thread() wakes up all sleeping threads when it is time
-  // to start a new search from the root.
+  // wake_sleeping_thread() wakes up the thread with the given threadID
+  // when it is time to start a new search.
 
   void ThreadsManager::wake_sleeping_thread(int threadID) {
 
-     lock_grab(&WaitLock);
-     cond_signal(&WaitCond[threadID]);
-     lock_release(&WaitLock);
+     lock_grab(&SleepLock[threadID]);
+     cond_signal(&SleepCond[threadID]);
+     lock_release(&SleepLock[threadID]);
   }