Use thread specific mutexes instead of a global one.
authorJoona Kiiski <zamar@meripeto.(none)>
Wed, 11 Mar 2015 21:50:41 +0000 (21:50 +0000)
committerJoona Kiiski <joona.kiiski@gmail.com>
Wed, 11 Mar 2015 21:59:34 +0000 (21:59 +0000)
This is necessary to improve the scalability with high number of cores.

There is no functional change in a single thread mode.

Resolves #281

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

index 14e95a84e219f26645e030df02384fcc08ca81c6..501d6c60b447f5b132ddcc6ef3a307b47d8235dd 100644 (file)
@@ -1526,13 +1526,12 @@ void Thread::idle_loop() {
       // If this thread has been assigned work, launch a search
       while (searching)
       {
-          Threads.mutex.lock();
+          mutex.lock();
 
           assert(activeSplitPoint);
-
           SplitPoint* sp = activeSplitPoint;
 
-          Threads.mutex.unlock();
+          mutex.unlock();
 
           Stack stack[MAX_PLY+4], *ss = stack+2; // To allow referencing (ss-2) and (ss+2)
           Position pos(*sp->pos, this);
@@ -1618,20 +1617,24 @@ void Thread::idle_loop() {
               sp = bestSp;
 
               // Recheck the conditions under lock protection
-              Threads.mutex.lock();
               sp->mutex.lock();
 
               if (   sp->allSlavesSearching
-                  && sp->slavesMask.count() < MAX_SLAVES_PER_SPLITPOINT
-                  && can_join(sp))
+                  && sp->slavesMask.count() < MAX_SLAVES_PER_SPLITPOINT)
               {
-                  sp->slavesMask.set(idx);
-                  activeSplitPoint = sp;
-                  searching = true;
+                  mutex.lock();
+
+                  if (can_join(sp))
+                  {
+                      sp->slavesMask.set(idx);
+                      activeSplitPoint = sp;
+                      searching = true;
+                  }
+
+                  mutex.unlock();
               }
 
               sp->mutex.unlock();
-              Threads.mutex.unlock();
           }
       }
 
@@ -1687,12 +1690,11 @@ void check_time() {
 
   else if (Limits.nodes)
   {
-      Threads.mutex.lock();
-
       int64_t nodes = RootPos.nodes_searched();
 
       // Loop across all split points and sum accumulated SplitPoint nodes plus
       // all the currently active positions nodes.
+      // FIXME: Racy...
       for (Thread* th : Threads)
           for (size_t i = 0; i < th->splitPointsSize; ++i)
           {
@@ -1709,8 +1711,6 @@ void check_time() {
               sp.mutex.unlock();
           }
 
-      Threads.mutex.unlock();
-
       if (nodes >= Limits.nodes)
           Signals.stop = true;
   }
index 3f901445ce1429f3fd6a00920459a66b78417ce0..d8740db2a66c39f04e49171cc7991e7d0e75b4f8 100644 (file)
@@ -144,6 +144,8 @@ void Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, Value* bes
   // Pick and init the next available split point
   SplitPoint& sp = splitPoints[splitPointsSize];
 
+  sp.mutex.lock(); // No contention here until we don't increment splitPointsSize
+
   sp.master = this;
   sp.parentSplitPoint = activeSplitPoint;
   sp.slavesMask = 0, sp.slavesMask.set(idx);
@@ -160,27 +162,29 @@ void Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, Value* bes
   sp.nodes = 0;
   sp.cutoff = false;
   sp.ss = ss;
-
-  // Try to allocate available threads and ask them to start searching setting
-  // 'searching' flag. This must be done under lock protection to avoid concurrent
-  // allocation of the same slave by another master.
-  Threads.mutex.lock();
-  sp.mutex.lock();
-
   sp.allSlavesSearching = true; // Must be set under lock protection
+
   ++splitPointsSize;
   activeSplitPoint = &sp;
   activePosition = nullptr;
 
+  // Try to allocate available threads
   Thread* slave;
 
   while (    sp.slavesMask.count() < MAX_SLAVES_PER_SPLITPOINT
-         && (slave = Threads.available_slave(activeSplitPoint)) != nullptr)
+         && (slave = Threads.available_slave(&sp)) != nullptr)
   {
-      sp.slavesMask.set(slave->idx);
-      slave->activeSplitPoint = activeSplitPoint;
-      slave->searching = true; // Slave leaves idle_loop()
-      slave->notify_one(); // Could be sleeping
+     slave->mutex.lock();
+
+      if (slave->can_join(activeSplitPoint))
+      {
+          activeSplitPoint->slavesMask.set(slave->idx);
+          slave->activeSplitPoint = activeSplitPoint;
+          slave->searching = true;
+          slave->sleepCondition.notify_one(); // Could be sleeping
+      }
+
+      slave->mutex.unlock();
   }
 
   // Everything is set up. The master thread enters the idle loop, from which
@@ -188,7 +192,6 @@ void Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, Value* bes
   // The thread will return from the idle loop when all slaves have finished
   // their work at this split point.
   sp.mutex.unlock();
-  Threads.mutex.unlock();
 
   Thread::idle_loop(); // Force a call to base class idle_loop()
 
@@ -198,13 +201,13 @@ void Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, Value* bes
   assert(!searching);
   assert(!activePosition);
 
+  searching = true;
+
   // We have returned from the idle loop, which means that all threads are
-  // finished. Note that setting 'searching' and decreasing splitPointsSize must
-  // be done under lock protection to avoid a race with Thread::available_to().
-  Threads.mutex.lock();
+  // finished. Note that decreasing splitPointsSize must be done under lock
+  // protection to avoid a race with Thread::can_join().
   sp.mutex.lock();
 
-  searching = true;
   --splitPointsSize;
   activeSplitPoint = sp.parentSplitPoint;
   activePosition = &pos;
@@ -213,7 +216,6 @@ void Thread::split(Position& pos, Stack* ss, Value alpha, Value beta, Value* bes
   *bestValue = sp.bestValue;
 
   sp.mutex.unlock();
-  Threads.mutex.unlock();
 }
 
 
index 231443dbba0d9f9c521b085ec2837c20587b2973..4d70bf2fbd038a9da98971c04c1bada7ba523743 100644 (file)
@@ -151,7 +151,6 @@ struct ThreadPool : public std::vector<Thread*> {
   void start_thinking(const Position&, const Search::LimitsType&, Search::StateStackPtr&);
 
   Depth minimumSplitDepth;
-  Mutex mutex;
   ConditionVariable sleepCondition;
   TimerThread* timer;
 };