]> git.sesse.net Git - stockfish/blobdiff - src/search.cpp
Be sure threads are woken in wake_sleeping_threads()
[stockfish] / src / search.cpp
index d3b229e67edd865091d85883688cbd28cb6926c2..22bc0156705121a9a02adfcd8b59c1ff66e21997 100644 (file)
@@ -126,9 +126,6 @@ namespace {
   // Search depth at iteration 1
   const Depth InitialDepth = OnePly;
 
-  // Depth limit for selective search
-  const Depth SelectiveDepth = 7 * OnePly;
-
   // Use internal iterative deepening?
   const bool UseIIDAtPVNodes = true;
   const bool UseIIDAtNonPVNodes = true;
@@ -150,17 +147,25 @@ namespace {
   // remaining ones we will extend it.
   const Value SingleReplyMargin = Value(0x20);
 
-  // Margins for futility pruning in the quiescence search, and at frontier
-  // and near frontier nodes.
-  const Value FutilityMarginQS = Value(0x80);
+  // Depth limit for razoring
+  const Depth RazorDepth = 4 * OnePly;
 
-  Value FutilityMargins[2 * PLY_MAX_PLUS_2]; // Initialized at startup.
+  /// Lookup tables initialized at startup
 
-  // Each move futility margin is decreased
-  const Value IncrementalFutilityMargin = Value(0x8);
+  // Reduction lookup tables and their getter functions
+  int8_t    PVReductionMatrix[64][64]; // [depth][moveNumber]
+  int8_t NonPVReductionMatrix[64][64]; // [depth][moveNumber]
 
-  // Depth limit for razoring
-  const Depth RazorDepth = 4 * OnePly;
+  inline Depth    pv_reduction(Depth d, int mn) { return (Depth)    PVReductionMatrix[Min(d / 2, 63)][Min(mn, 63)]; }
+  inline Depth nonpv_reduction(Depth d, int mn) { return (Depth) NonPVReductionMatrix[Min(d / 2, 63)][Min(mn, 63)]; }
+
+  // Futility lookup tables and their getter functions
+  const Value FutilityMarginQS = Value(0x80);
+  int32_t FutilityMarginsMatrix[14][64]; // [depth][moveNumber]
+  int FutilityMoveCountArray[32]; // [depth]
+
+  inline Value futility_margin(Depth d, int mn) { return Value(d < 7*OnePly ? FutilityMarginsMatrix[Max(d, 0)][Min(mn, 63)] : 2 * VALUE_INFINITE); }
+  inline int futility_move_count(Depth d) { return d < 16*OnePly ? FutilityMoveCountArray[d] : 512; }
 
   /// Variables initialized by UCI options
 
@@ -207,14 +212,6 @@ namespace {
   bool UseLogFile;
   std::ofstream LogFile;
 
-  // Reduction lookup tables and their getter functions
-  // Initialized at startup
-  int8_t    PVReductionMatrix[64][64]; // [depth][moveNumber]
-  int8_t NonPVReductionMatrix[64][64]; // [depth][moveNumber]
-
-  inline Depth    pv_reduction(Depth d, int mn) { return (Depth)    PVReductionMatrix[Min(d / 2, 63)][Min(mn, 63)]; }
-  inline Depth nonpv_reduction(Depth d, int mn) { return (Depth) NonPVReductionMatrix[Min(d / 2, 63)][Min(mn, 63)]; }
-
   // MP related variables
   int ActiveThreads = 1;
   Depth MinimumSplitDepth;
@@ -222,9 +219,8 @@ namespace {
   Thread Threads[THREAD_MAX];
   Lock MPLock;
   Lock IOLock;
-  bool AllThreadsShouldExit = false;
+  bool AllThreadsShouldExit, AllThreadsShouldSleep;
   SplitPoint SplitPointStack[THREAD_MAX][ACTIVE_SPLIT_POINTS_MAX];
-  bool Idle = true;
 
 #if !defined(_MSC_VER)
   pthread_cond_t WaitCond;
@@ -339,7 +335,7 @@ bool think(const Position& pos, bool infinite, bool ponder, int side_to_move,
            int maxNodes, int maxTime, Move searchMoves[]) {
 
   // Initialize global search variables
-  Idle = StopOnPonderhit = AbortSearch = Quit = false;
+  AllThreadsShouldSleep = StopOnPonderhit = AbortSearch = Quit = false;
   AspirationFailLow = false;
   NodesSincePoll = 0;
   SearchStartTime = get_system_time();
@@ -525,26 +521,17 @@ bool think(const Position& pos, bool infinite, bool ponder, int side_to_move,
   if (UseLogFile)
       LogFile.close();
 
-  Idle = true;
+  AllThreadsShouldSleep = true;
   return !Quit;
 }
 
 
-/// init_threads() is called during startup. It launches all helper threads,
-/// and initializes the split point stack and the global locks and condition
-/// objects.
-
-void init_threads() {
+/// init_search() is called during startup. It initializes various lookup tables
 
-  volatile int i;
-  bool ok;
-
-#if !defined(_MSC_VER)
-  pthread_t pthread[1];
-#endif
+void init_search() {
 
   // Init our reduction lookup tables
-  for (i = 1; i < 64; i++) // i == depth
+  for (int i = 1; i < 64; i++) // i == depth (OnePly = 1)
       for (int j = 1; j < 64; j++) // j == moveNumber
       {
           double    pvRed = 0.5 + log(double(i)) * log(double(j)) / 6.0;
@@ -554,15 +541,30 @@ void init_threads() {
       }
 
   // Init futility margins array
-  FutilityMargins[0] = FutilityMargins[1] = Value(0);
+  for (int i = 0; i < 14; i++) // i == depth (OnePly = 2)
+      for (int j = 0; j < 64; j++) // j == moveNumber
+      {
+          FutilityMarginsMatrix[i][j] = (i < 2 ? 0 : 112 * bitScanReverse32(i * i / 2)) - 8 * j; // FIXME: test using log instead of BSR
+      }
 
-  for (i = 2; i < 2 * PLY_MAX_PLUS_2; i++)
-  {
-      FutilityMargins[i] = Value(112 * bitScanReverse32(i * i / 2)); // FIXME: test using log instead of BSR
-  }
+  // Init futility move count array
+  for (int i = 0; i < 32; i++) // i == depth (OnePly = 2)
+      FutilityMoveCountArray[i] = 3 + (1 << (3 * i / 8));
+}
 
-  for (i = 0; i < THREAD_MAX; i++)
-      Threads[i].activeSplitPoints = 0;
+
+/// init_threads() is called during startup. It launches all helper threads,
+/// and initializes the split point stack and the global locks and condition
+/// objects.
+
+void init_threads() {
+
+  volatile int i;
+  bool ok;
+
+#if !defined(_MSC_VER)
+  pthread_t pthread[1];
+#endif
 
   // Initialize global locks
   lock_init(&MPLock, NULL);
@@ -578,14 +580,15 @@ void init_threads() {
       SitIdleEvent[i] = CreateEvent(0, FALSE, FALSE, 0);
 #endif
 
+  // Will be set just before program exits to properly end the threads
+  AllThreadsShouldExit = false;
+
+  // Threads will be put to sleep as soon as created
+  AllThreadsShouldSleep = true;
+
   // All threads except the main thread should be initialized to idle state
   for (i = 1; i < THREAD_MAX; i++)
-  {
-      Threads[i].stop = false;
-      Threads[i].workIsWaiting = false;
       Threads[i].idle = true;
-      Threads[i].running = false;
-  }
 
   // Launch the helper threads
   for (i = 1; i < THREAD_MAX; i++)
@@ -615,7 +618,7 @@ void init_threads() {
 void stop_threads() {
 
   ActiveThreads = THREAD_MAX;  // HACK
-  Idle = false;  // HACK
+  AllThreadsShouldSleep = false;  // HACK
   wake_sleeping_threads();
   AllThreadsShouldExit = true;
   for (int i = 1; i < THREAD_MAX; i++)
@@ -647,7 +650,6 @@ void SearchStack::init(int ply) {
   currentMove = threatMove = MOVE_NONE;
   reduction = Depth(0);
   eval = VALUE_NONE;
-  evalInfo = NULL;
 }
 
 void SearchStack::initKillers() {
@@ -1374,22 +1376,16 @@ namespace {
 
     isCheck = pos.is_check();
 
-    // Calculate depth dependant futility pruning parameters
-    const int FutilityMoveCountMargin = 3 + (1 << (3 * int(depth) / 8));
-
     // Evaluate the position statically
     if (!isCheck)
     {
         if (tte && (tte->type() & VALUE_TYPE_EVAL))
             staticValue = value_from_tt(tte->value(), ply);
         else
-        {
             staticValue = evaluate(pos, ei, threadID);
-            ss[ply].evalInfo = &ei;
-        }
 
         ss[ply].eval = staticValue;
-        futilityValue = staticValue + FutilityMargins[int(depth)]; //FIXME: Remove me, only for split
+        futilityValue = staticValue + futility_margin(depth, 0); //FIXME: Remove me, only for split
         staticValue = refine_eval(tte, staticValue, ply); // Enhance accuracy with TT value if possible
         update_gains(pos, ss[ply - 1].currentMove, ss[ply - 1].eval, ss[ply].eval);
     }
@@ -1400,8 +1396,8 @@ namespace {
     if (  !isCheck
         && allowNullmove
         && depth < RazorDepth
-        && staticValue - FutilityMargins[int(depth)] >= beta)
-        return staticValue - FutilityMargins[int(depth)];
+        && staticValue - futility_margin(depth, 0) >= beta)
+        return staticValue - futility_margin(depth, 0);
 
     // Null move search
     if (    allowNullmove
@@ -1473,7 +1469,7 @@ namespace {
     {
         search(pos, ss, beta, Min(depth/2, depth-2*OnePly), ply, false, threadID);
         ttMove = ss[ply].pv[ply];
-        tte = TT.retrieve(pos.get_key());
+        tte = TT.retrieve(posKey);
     }
 
     // Initialize a MovePicker object for the current position, and prepare
@@ -1533,35 +1529,21 @@ namespace {
           &&  move != ttMove)
       {
           // Move count based pruning
-          if (   moveCount >= FutilityMoveCountMargin
+          if (   moveCount >= futility_move_count(depth)
               && ok_to_prune(pos, move, ss[ply].threatMove)
               && bestValue > value_mated_in(PLY_MAX))
               continue;
 
           // Value based pruning
-          Depth predictedDepth = newDepth;
-
-          //FIXME: We are ignoring condition: depth >= 3*OnePly, BUG??
-          ss[ply].reduction = nonpv_reduction(depth, moveCount);
-          if (ss[ply].reduction)
-              predictedDepth -= ss[ply].reduction;
+          Depth predictedDepth = newDepth - nonpv_reduction(depth, moveCount); //FIXME: We are ignoring condition: depth >= 3*OnePly, BUG??
+          futilityValueScaled =  ss[ply].eval + futility_margin(predictedDepth, moveCount)
+                               + H.gain(pos.piece_on(move_from(move)), move_to(move)) + 45;
 
-          if (predictedDepth < SelectiveDepth)
+          if (futilityValueScaled < beta)
           {
-              int preFutilityValueMargin = 0;
-              if (predictedDepth >= OnePly)
-                  preFutilityValueMargin = FutilityMargins[int(predictedDepth)];
-
-              preFutilityValueMargin += H.gain(pos.piece_on(move_from(move)), move_to(move)) + 45;
-
-              futilityValueScaled = ss[ply].eval + preFutilityValueMargin - moveCount * IncrementalFutilityMargin;
-
-              if (futilityValueScaled < beta)
-              {
-                  if (futilityValueScaled > bestValue)
-                      bestValue = futilityValueScaled;
-                  continue;
-              }
+              if (futilityValueScaled > bestValue)
+                  bestValue = futilityValueScaled;
+              continue;
           }
       }
 
@@ -1860,11 +1842,9 @@ namespace {
     Move move;
     int moveCount;
     bool isCheck = pos.is_check();
-    bool useFutilityPruning =     sp->depth < SelectiveDepth
+    bool useFutilityPruning =     sp->depth < 7 * OnePly //FIXME: sync with search
                               && !isCheck;
 
-    const int FutilityMoveCountMargin = 3 + (1 << (3 * int(sp->depth) / 8));
-
     while (    lock_grab_bool(&(sp->lock))
            &&  sp->bestValue < sp->beta
            && !thread_should_stop(threadID)
@@ -1891,13 +1871,13 @@ namespace {
           && !captureOrPromotion)
       {
           // Move count based pruning
-          if (   moveCount >= FutilityMoveCountMargin
+          if (   moveCount >= futility_move_count(sp->depth)
               && ok_to_prune(pos, move, ss[sp->ply].threatMove)
               && sp->bestValue > value_mated_in(PLY_MAX))
               continue;
 
           // Value based pruning
-          Value futilityValueScaled = sp->futilityValue - moveCount * IncrementalFutilityMargin;
+          Value futilityValueScaled = sp->futilityValue - moveCount * 8; //FIXME: sync with search
 
           if (futilityValueScaled < sp->beta)
           {
@@ -2793,16 +2773,17 @@ namespace {
 
     Threads[threadID].running = true;
 
-    while (true)
+    while (!AllThreadsShouldExit || threadID == 0)
     {
-        if (AllThreadsShouldExit && threadID != 0)
-            break;
-
         // If we are not thinking, wait for a condition to be signaled
         // instead of wasting CPU time polling for work.
-        while (threadID != 0 && (Idle || threadID >= ActiveThreads))
+        while (    threadID != 0
+               && !AllThreadsShouldExit
+               && (AllThreadsShouldSleep || threadID >= ActiveThreads))
         {
 
+            Threads[threadID].sleeping = true;
+
 #if !defined(_MSC_VER)
             pthread_mutex_lock(&WaitLock);
             if (Idle || threadID >= ActiveThreads)
@@ -2814,6 +2795,10 @@ namespace {
 #endif
         }
 
+        // Out of the while loop to avoid races in case thread is woken up but
+        // while condition still holds true so that is put to sleep again.
+        Threads[threadID].sleeping = false;
+
       // If this thread has been assigned work, launch a search
       if (Threads[threadID].workIsWaiting)
       {
@@ -3069,6 +3054,8 @@ namespace {
     {
         for (int i = 1; i < ActiveThreads; i++)
         {
+            assert(Threads[i].sleeping == true);
+
             Threads[i].idle = true;
             Threads[i].workIsWaiting = false;
         }
@@ -3081,6 +3068,10 @@ namespace {
       for (int i = 1; i < THREAD_MAX; i++)
           SetEvent(SitIdleEvent[i]);
 #endif
+
+      // Wait for the threads to be all woken up
+      for (int i = 1; i < ActiveThreads; i++)
+           while (Threads[i].sleeping);
     }
   }