// split() does the actual work of distributing the work at a node between // several available threads. If it does not succeed in splitting the node // (because no idle threads are available, or because we have no unused split // point objects), the function immediately returns. If splitting is possible, a // SplitPoint object is initialized with all the data that must be copied to the // helper threads and then helper threads are told that they have been assigned // work. This will cause them to instantly leave their idle loops and call // search(). When all threads have returned from search() then split() returns. internal static Value split(bool Fake, Position pos, Stack[] ss, int ssPos, Value alpha, Value beta, Value bestValue, ref Move bestMove, Depth depth, Move threatMove, int moveCount, MovePicker mp, int nodeType) { Debug.Assert(pos.pos_is_ok()); Debug.Assert(bestValue > -ValueC.VALUE_INFINITE); Debug.Assert(bestValue <= alpha); Debug.Assert(alpha < beta); Debug.Assert(beta <= ValueC.VALUE_INFINITE); Debug.Assert(depth > DepthC.DEPTH_ZERO); Thread master = pos.this_thread(); if (master.splitPointsCnt >= Constants.MAX_SPLITPOINTS_PER_THREAD) return bestValue; // Pick the next available split point from the split point stack SplitPoint sp = master.splitPoints[master.splitPointsCnt]; sp.parent = master.curSplitPoint; sp.master = master; sp.cutoff = false; sp.slavesMask = 1UL << master.idx; #if ACTIVE_REPARENT sp.allSlavesRunning = true; #endif sp.depth = depth; sp.bestMove = bestMove; sp.threatMove = threatMove; sp.alpha = alpha; sp.beta = beta; sp.nodeType = nodeType; sp.bestValue = bestValue; sp.mp = mp; sp.moveCount = moveCount; sp.pos = pos; sp.nodes = 0; sp.ss = ss; sp.ssPos = ssPos; Debug.Assert(master.is_searching); master.curSplitPoint = sp; int slavesCnt = 0; ThreadHelper.lock_grab(sp.Lock); ThreadHelper.lock_grab(splitLock); for (int i = 0; i < size() && !Fake; ++i) if (threads[i].is_available_to(master)) { sp.slavesMask |= 1UL << i; threads[i].curSplitPoint = sp; threads[i].is_searching = true; // Slave leaves idle_loop() if (useSleepingThreads) threads[i].wake_up(); if (++slavesCnt + 1 >= maxThreadsPerSplitPoint) // Master is always included break; } master.splitPointsCnt++; ThreadHelper.lock_release(splitLock); ThreadHelper.lock_release(sp.Lock); // Everything is set up. The master thread enters the idle loop, from which // it will instantly launch a search, because its is_searching flag is set. // We pass the split point as a parameter to the idle loop, which means that // the thread will return from the idle loop when all slaves have finished // their work at this split point. if (slavesCnt != 0 || Fake) { master.idle_loop(sp, null); // In helpful master concept a master can help only a sub-tree of its split // point, and because here is all finished is not possible master is booked. Debug.Assert(!master.is_searching); } // We have returned from the idle loop, which means that all threads are // finished. Note that setting is_searching and decreasing activeSplitPoints is // done under lock protection to avoid a race with Thread::is_available_to(). ThreadHelper.lock_grab(sp.Lock); // To protect sp->nodes ThreadHelper.lock_grab(splitLock); master.is_searching = true; master.splitPointsCnt--; master.curSplitPoint = sp.parent; pos.nodes += sp.nodes; bestMove = sp.bestMove; ThreadHelper.lock_release(splitLock); ThreadHelper.lock_release(sp.Lock); return sp.bestValue; }
// split() does the actual work of distributing the work at a node between // several available threads. If it does not succeed in splitting the node // (because no idle threads are available), the function immediately returns. // If splitting is possible, a SplitPoint object is initialized with all the // data that must be copied to the helper threads and then helper threads are // told that they have been assigned work. This will cause them to instantly // leave their idle loops and call search(). When all threads have returned from // search() then split() returns. internal static void split( bool Fake, Position pos, Stack[] ss, int ssPos, int alpha, int beta, ref int bestValue, ref int bestMove, int depth, int threatMove, int moveCount, MovePicker movePicker, int nodeType) { Debug.Assert(bestValue <= alpha && alpha < beta && beta <= ValueC.VALUE_INFINITE); Debug.Assert(pos.pos_is_ok()); Debug.Assert(bestValue > -ValueC.VALUE_INFINITE); Debug.Assert(depth > DepthC.DEPTH_ZERO); var thisThread = pos.this_thread(); Debug.Assert(thisThread.searching); Debug.Assert(thisThread.splitPointsSize < Constants.MAX_SPLITPOINTS_PER_THREAD); // Pick the next available split point from the split point stack var sp = thisThread.splitPoints[thisThread.splitPointsSize]; sp.parentSplitPoint = thisThread.activeSplitPoint; sp.master = thisThread; sp.cutoff = false; sp.slavesMask = 1UL << thisThread.idx; #if ACTIVE_REPARENT sp.allSlavesRunning = true; #endif sp.depth = depth; sp.bestMove = bestMove; sp.threatMove = threatMove; sp.alpha = alpha; sp.beta = beta; sp.nodeType = nodeType; sp.bestValue = bestValue; sp.movePicker = movePicker; sp.moveCount = moveCount; sp.pos = pos; sp.nodes = 0; sp.ss = ss; sp.ssPos = ssPos; // 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. ThreadHelper.lock_grab(splitLock); ThreadHelper.lock_grab(sp.Lock); thisThread.splitPointsSize++; thisThread.activeSplitPoint = sp; thisThread.activePosition = null; var slavesCnt = 1; // Master is always included Thread slave; while ((slave = Threads.available_slave(thisThread)) != null && ++slavesCnt <= Threads.maxThreadsPerSplitPoint && !Fake) { sp.slavesMask |= 1UL << slave.idx; slave.activeSplitPoint = sp; slave.searching = true; // Slave leaves idle_loop() slave.notify_one(); // Could be sleeping } ThreadHelper.lock_release(sp.Lock); ThreadHelper.lock_release(splitLock); // Everything is set up. The master thread enters the idle loop, from which // it will instantly launch a search, because its searching flag is set. // We pass the split point as a parameter to the idle loop, which means that // the thread will return from the idle loop when all slaves have finished // their work at this split point. if (slavesCnt > 1 || Fake) { thisThread.base_idle_loop(null); // Force a call to base class idle_loop() // In helpful master concept a master can help only a sub-tree of its split // point, and because here is all finished is not possible master is booked. Debug.Assert(!thisThread.searching); Debug.Assert(thisThread.activePosition == null); } // We have returned from the idle loop, which means that all threads are // finished. Note that setting searching and decreasing activeSplitPoints is // done under lock protection to avoid a race with Thread::is_available_to(). ThreadHelper.lock_grab(splitLock); ThreadHelper.lock_grab(sp.Lock); // To protect sp->nodes thisThread.searching = true; thisThread.splitPointsSize--; thisThread.activeSplitPoint = sp.parentSplitPoint; thisThread.activePosition = pos; pos.nodes += sp.nodes; bestMove = sp.bestMove; bestValue = sp.bestValue; ThreadHelper.lock_release(sp.Lock); ThreadHelper.lock_release(splitLock); }