diff --git a/src/search.cpp b/src/search.cpp index 59c717ca..5a4b56ba 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -259,6 +259,7 @@ namespace { // Multi-threads related variables Depth MinimumSplitDepth; int MaxThreadsPerSplitPoint; + bool UseSleepingMaster; ThreadsManager ThreadsMgr; // Node counters, used only by thread[0] but try to keep in different cache @@ -451,6 +452,7 @@ bool think(Position& pos, bool infinite, bool ponder, int time[], int increment[ MaxThreadsPerSplitPoint = Options["Maximum Number of Threads per Split Point"].value(); MultiPV = Options["MultiPV"].value(); UseLogFile = Options["Use Search Log"].value(); + UseSleepingMaster = Options["Use Sleeping Master"].value(); if (UseLogFile) LogFile.open(Options["Search Log Filename"].value().c_str(), std::ios::out | std::ios::app); @@ -2178,6 +2180,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, @@ -2193,23 +2198,23 @@ split_point_start: // At split points actual search starts from here // instead of wasting CPU time polling for work. while ( threadID >= ActiveThreads || threads[threadID].state == THREAD_INITIALIZING - || (!sp && threads[threadID].state == THREAD_AVAILABLE)) + || (threads[threadID].state == THREAD_AVAILABLE && (!sp || UseSleepingMaster))) { - assert(!sp); - assert(threadID != 0); - - if (AllThreadsShouldExit) - break; - lock_grab(&MPLock); - // Retest condition under lock protection - if (!( threadID >= ActiveThreads - || threads[threadID].state == THREAD_INITIALIZING - || (!sp && threads[threadID].state == THREAD_AVAILABLE))) + // Test with lock held to avoid races with wake_sleeping_thread() + for (i = 0; sp && i < ActiveThreads && !sp->slaves[i]; i++) {} + allFinished = (i == ActiveThreads); + + // Retest sleep conditions under lock protection + if ( AllThreadsShouldExit + || allFinished + || !( threadID >= ActiveThreads + || threads[threadID].state == THREAD_INITIALIZING + || (threads[threadID].state == THREAD_AVAILABLE && (!sp || UseSleepingMaster)))) { lock_release(&MPLock); - continue; + break; } // Put thread to sleep @@ -2239,14 +2244,19 @@ split_point_start: // At split points actual search starts from here 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 (UseSleepingMaster && 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. @@ -2454,6 +2464,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; diff --git a/src/thread.h b/src/thread.h index 44b70e1e..98576cce 100644 --- a/src/thread.h +++ b/src/thread.h @@ -55,6 +55,7 @@ struct SplitPoint { bool pvNode, mateThreat; Value beta; int ply; + int master; Move threatMove; SearchStack sstack[MAX_THREADS][PLY_MAX_PLUS_2]; diff --git a/src/ucioption.cpp b/src/ucioption.cpp index b1b84980..760d7df2 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -74,6 +74,7 @@ void init_uci_options() { Options["Minimum Split Depth"] = Option(4, 4, 7); Options["Maximum Number of Threads per Split Point"] = Option(5, 4, 8); Options["Threads"] = Option(1, 1, MAX_THREADS); + Options["Use Sleeping Master"] = Option(false); Options["Hash"] = Option(32, 4, 8192); Options["Clear Hash"] = Option(false, "button"); Options["New Game"] = Option(false, "button");