X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fsearch.cpp;h=aea07fdae7fdf93df1cc60b3981ea8bf99810f27;hb=b39a24ecca28620239818ca393a46a47f9d42824;hp=857aaff87ea9fd1d0812fcd2519f560ab1adf4fd;hpb=2b740f549534aff950330eff0f4806da71c76726;p=stockfish diff --git a/src/search.cpp b/src/search.cpp index 857aaff8..aea07fda 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -70,7 +70,7 @@ namespace { int active_threads() const { return ActiveThreads; } void set_active_threads(int newActiveThreads) { ActiveThreads = newActiveThreads; } - void set_stop_request(int threadID) { threads[threadID].stop = true; } + void set_stop_request(int threadID) { threads[threadID].stopRequest = true; } void incrementNodeCounter(int threadID) { threads[threadID].nodes++; } void incrementBetaCounter(Color us, Depth d, int threadID) { threads[threadID].betaCutOffs[us] += unsigned(d); } void print_current_line(SearchStack ss[], int ply, int threadID); @@ -79,9 +79,9 @@ namespace { void resetBetaCounters(); int64_t nodes_searched() const; void get_beta_counters(Color us, int64_t& our, int64_t& their) const; - bool idle_thread_exists(int master) const; + bool available_thread_exists(int master) const; bool thread_is_available(int slave, int master) const; - bool thread_should_stop(int threadID); + bool thread_should_stop(int threadID) const; void wake_sleeping_threads(); void put_threads_to_sleep(); void idle_loop(int threadID, SplitPoint* waitSp); @@ -92,9 +92,9 @@ namespace { friend void poll(); int ActiveThreads; - bool AllThreadsShouldExit, AllThreadsShouldSleep; - Thread threads[THREAD_MAX]; - SplitPoint SplitPointStack[THREAD_MAX][ACTIVE_SPLIT_POINTS_MAX]; + volatile bool AllThreadsShouldExit, AllThreadsShouldSleep; + Thread threads[MAX_THREADS]; + SplitPoint SplitPointStack[MAX_THREADS][ACTIVE_SPLIT_POINTS_MAX]; Lock MPLock, IOLock; @@ -102,7 +102,7 @@ namespace { pthread_cond_t WaitCond; pthread_mutex_t WaitLock; #else - HANDLE SitIdleEvent[THREAD_MAX]; + HANDLE SitIdleEvent[MAX_THREADS]; #endif }; @@ -1208,7 +1208,7 @@ namespace { && bestValue < beta && depth >= MinimumSplitDepth && Iteration <= 99 - && TM.idle_thread_exists(threadID) + && TM.available_thread_exists(threadID) && !AbortSearch && !TM.thread_should_stop(threadID) && TM.split(pos, ss, ply, &alpha, &beta, &bestValue, VALUE_NONE, @@ -1334,7 +1334,7 @@ namespace { && !isCheck && !value_is_mate(beta) && ok_to_do_nullmove(pos) - && staticValue >= beta - NullMoveMargin) + && staticValue >= beta - (depth >= 4 * OnePly ? NullMoveMargin : 0)) { ss[ply].currentMove = MOVE_NULL; @@ -1522,7 +1522,7 @@ namespace { && bestValue < beta && depth >= MinimumSplitDepth && Iteration <= 99 - && TM.idle_thread_exists(threadID) + && TM.available_thread_exists(threadID) && !AbortSearch && !TM.thread_should_stop(threadID) && TM.split(pos, ss, ply, &beta, &beta, &bestValue, futilityValue, //FIXME: SMP & futilityValue @@ -1695,6 +1695,7 @@ namespace { // Don't search moves with negative SEE values if ( (!isCheck || evasionPrunable) + && !pvNode && move != ttMove && !move_is_promotion(move) && pos.see_sign(move) < 0) @@ -1881,10 +1882,12 @@ namespace { /* Here we have the lock still grabbed */ // If this is the master thread and we have been asked to stop because of - // a beta cutoff higher up in the tree, stop all slave threads. + // a beta cutoff higher up in the tree, stop all slave threads. Note that + // thread_should_stop(threadID) does not imply that 'stop' flag is set, so + // do this explicitly now, under lock protection. if (sp->master == threadID && TM.thread_should_stop(threadID)) for (int i = 0; i < TM.active_threads(); i++) - if (sp->slaves[i]) + if (sp->slaves[i] || i == threadID) TM.set_stop_request(i); sp->cpus--; @@ -2016,10 +2019,12 @@ namespace { /* Here we have the lock still grabbed */ // If this is the master thread and we have been asked to stop because of - // a beta cutoff higher up in the tree, stop all slave threads. + // a beta cutoff higher up in the tree, stop all slave threads. Note that + // thread_should_stop(threadID) does not imply that 'stop' flag is set, so + // do this explicitly now, under lock protection. if (sp->master == threadID && TM.thread_should_stop(threadID)) for (int i = 0; i < TM.active_threads(); i++) - if (sp->slaves[i]) + if (sp->slaves[i] || i == threadID) TM.set_stop_request(i); sp->cpus--; @@ -2580,13 +2585,13 @@ namespace { void ThreadsManager::resetNodeCounters() { - for (int i = 0; i < THREAD_MAX; i++) + for (int i = 0; i < MAX_THREADS; i++) threads[i].nodes = 0ULL; } void ThreadsManager::resetBetaCounters() { - for (int i = 0; i < THREAD_MAX; i++) + for (int i = 0; i < MAX_THREADS; i++) threads[i].betaCutOffs[WHITE] = threads[i].betaCutOffs[BLACK] = 0ULL; } @@ -2602,7 +2607,7 @@ namespace { void ThreadsManager::get_beta_counters(Color us, int64_t& our, int64_t& their) const { our = their = 0UL; - for (int i = 0; i < THREAD_MAX; i++) + for (int i = 0; i < MAX_THREADS; i++) { our += threads[i].betaCutOffs[us]; their += threads[i].betaCutOffs[opposite_color(us)]; @@ -2616,57 +2621,69 @@ namespace { void ThreadsManager::idle_loop(int threadID, SplitPoint* waitSp) { - assert(threadID >= 0 && threadID < THREAD_MAX); - - threads[threadID].running = true; + assert(threadID >= 0 && threadID < MAX_THREADS); - while (!AllThreadsShouldExit || threadID == 0) + while (true) { + // Slave threads can exit as soon as AllThreadsShouldExit raises, + // master should exit as last one. + if (AllThreadsShouldExit && !waitSp) + { + threads[threadID].state = THREAD_TERMINATED; + return; + } + // If we are not thinking, wait for a condition to be signaled // instead of wasting CPU time polling for work. while ( threadID != 0 && !AllThreadsShouldExit && (AllThreadsShouldSleep || threadID >= ActiveThreads)) { - - threads[threadID].sleeping = true; + threads[threadID].state = THREAD_SLEEPING; #if !defined(_MSC_VER) pthread_mutex_lock(&WaitLock); - if (AllThreadsShouldSleep || threadID >= ActiveThreads) - pthread_cond_wait(&WaitCond, &WaitLock); - + pthread_cond_wait(&WaitCond, &WaitLock); pthread_mutex_unlock(&WaitLock); #else WaitForSingleObject(SitIdleEvent[threadID], INFINITE); #endif + // State is already changed by wake_sleeping_threads() + assert(threads[threadID].state == THREAD_AVAILABLE || threadID >= ActiveThreads); } - // 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) + if (threads[threadID].state == THREAD_WORKISWAITING) { - assert(!threads[threadID].idle); + threads[threadID].state = THREAD_SEARCHING; - threads[threadID].workIsWaiting = false; if (threads[threadID].splitPoint->pvNode) sp_search_pv(threads[threadID].splitPoint, threadID); else sp_search(threads[threadID].splitPoint, threadID); - threads[threadID].idle = true; + assert(threads[threadID].state == THREAD_SEARCHING); + + // If this is a slave thread reset to available, instead + // if it is a master thread and all slaves have finished + // then leave as is to avoid booking by another master, + // we will leave idle loop shortly anyhow. + if ( !AllThreadsShouldExit + && (!waitSp || waitSp->cpus > 0)) + threads[threadID].state = THREAD_AVAILABLE; } // If this thread is the master of a split point and all threads have // finished their work at this split point, return from the idle loop. if (waitSp != NULL && waitSp->cpus == 0) + { + assert( threads[threadID].state == THREAD_AVAILABLE + || threads[threadID].state == THREAD_SEARCHING); + + threads[threadID].state = THREAD_SEARCHING; return; + } } - - threads[threadID].running = false; } @@ -2688,7 +2705,7 @@ namespace { lock_init(&IOLock, NULL); // Initialize SplitPointStack locks - for (int i = 0; i < THREAD_MAX; i++) + for (int i = 0; i < MAX_THREADS; i++) for (int j = 0; j < ACTIVE_SPLIT_POINTS_MAX; j++) { SplitPointStack[i][j].parent = NULL; @@ -2699,7 +2716,7 @@ namespace { pthread_mutex_init(&WaitLock, NULL); pthread_cond_init(&WaitCond, NULL); #else - for (i = 0; i < THREAD_MAX; i++) + for (i = 0; i < MAX_THREADS; i++) SitIdleEvent[i] = CreateEvent(0, FALSE, FALSE, 0); #endif @@ -2709,13 +2726,14 @@ namespace { // Threads will be put to sleep as soon as created AllThreadsShouldSleep = true; - // All threads except the main thread should be initialized to idle state + // All threads except the main thread should be initialized to THREAD_AVAILABLE ActiveThreads = 1; - for (i = 1; i < THREAD_MAX; i++) - threads[i].idle = true; + threads[0].state = THREAD_SEARCHING; + for (i = 1; i < MAX_THREADS; i++) + threads[i].state = THREAD_AVAILABLE; // Launch the helper threads - for (i = 1; i < THREAD_MAX; i++) + for (i = 1; i < MAX_THREADS; i++) { #if !defined(_MSC_VER) @@ -2732,7 +2750,7 @@ namespace { } // Wait until the thread has finished launching and is gone to sleep - while (!threads[i].running || !threads[i].sleeping); + while (threads[i].state != THREAD_SLEEPING); } } @@ -2742,18 +2760,20 @@ namespace { void ThreadsManager::exit_threads() { - ActiveThreads = THREAD_MAX; // HACK + ActiveThreads = MAX_THREADS; // HACK AllThreadsShouldSleep = true; // HACK wake_sleeping_threads(); + + // This makes the threads to exit idle_loop() AllThreadsShouldExit = true; - for (int i = 1; i < THREAD_MAX; i++) - { - threads[i].stop = true; - while (threads[i].running); - } + + // Wait for thread termination + for (int i = 1; i < MAX_THREADS; i++) + while (threads[i].state != THREAD_TERMINATED) + threads[i].stopRequest = true; // Now we can safely destroy the locks - for (int i = 0; i < THREAD_MAX; i++) + for (int i = 0; i < MAX_THREADS; i++) for (int j = 0; j < ACTIVE_SPLIT_POINTS_MAX; j++) lock_destroy(&(SplitPointStack[i][j].lock)); } @@ -2764,13 +2784,13 @@ namespace { // cutoff has occurred in the thread's currently active split point, or in // some ancestor of the current split point. - bool ThreadsManager::thread_should_stop(int threadID) { + bool ThreadsManager::thread_should_stop(int threadID) const { assert(threadID >= 0 && threadID < ActiveThreads); SplitPoint* sp; - if (threads[threadID].stop) + if (threads[threadID].stopRequest) return true; if (ActiveThreads <= 2) @@ -2778,10 +2798,7 @@ namespace { for (sp = threads[threadID].splitPoint; sp != NULL; sp = sp->parent) if (sp->finished) - { - threads[threadID].stop = true; return true; - } return false; } @@ -2801,7 +2818,7 @@ namespace { assert(master >= 0 && master < ActiveThreads); assert(ActiveThreads > 1); - if (!threads[slave].idle || slave == master) + if (threads[slave].state != THREAD_AVAILABLE || slave == master) return false; // Make a local copy to be sure doesn't change under our feet @@ -2825,10 +2842,10 @@ namespace { } - // idle_thread_exists() tries to find an idle thread which is available as + // available_thread_exists() tries to find an idle thread which is available as // a slave for the thread with threadID "master". - bool ThreadsManager::idle_thread_exists(int master) const { + bool ThreadsManager::available_thread_exists(int master) const { assert(master >= 0 && master < ActiveThreads); assert(ActiveThreads > 1); @@ -2873,7 +2890,7 @@ namespace { // If no other thread is available to help us, or if we have too many // active split points, don't split. - if ( !idle_thread_exists(master) + if ( !available_thread_exists(master) || threads[master].activeSplitPoints >= ACTIVE_SPLIT_POINTS_MAX) { lock_release(&MPLock); @@ -2903,16 +2920,17 @@ namespace { for (int i = 0; i < ActiveThreads; i++) splitPoint->slaves[i] = 0; - threads[master].idle = false; - threads[master].stop = false; threads[master].splitPoint = splitPoint; - // Allocate available threads setting idle flag to false + // If we are here it means we are not available + assert(threads[master].state != THREAD_AVAILABLE); + + // Allocate available threads setting state to THREAD_BOOKED for (int i = 0; i < ActiveThreads && splitPoint->cpus < MaxThreadsPerSplitPoint; i++) if (thread_is_available(i, master)) { - threads[i].idle = false; - threads[i].stop = false; + threads[i].state = THREAD_BOOKED; + threads[i].stopRequest = false; threads[i].splitPoint = splitPoint; splitPoint->slaves[i] = 1; splitPoint->cpus++; @@ -2920,7 +2938,7 @@ namespace { assert(splitPoint->cpus > 1); - // We can release the lock because master and slave threads are already booked + // We can release the lock because slave threads are already booked and master is not available lock_release(&MPLock); // Tell the threads that they have work to do. This will make them leave @@ -2929,12 +2947,15 @@ namespace { if (i == master || splitPoint->slaves[i]) { memcpy(splitPoint->sstack[i] + ply - 1, sstck + ply - 1, 4 * sizeof(SearchStack)); - threads[i].workIsWaiting = true; // This makes the slave to exit from idle_loop() + + assert(i == master || threads[i].state == THREAD_BOOKED); + + threads[i].state = THREAD_WORKISWAITING; // This makes the slave to exit from idle_loop() } // Everything is set up. The master thread enters the idle loop, from - // which it will instantly launch a search, because its workIsWaiting - // slot is 'true'. We send the split point as a second parameter to the + // which it will instantly launch a search, because its state is + // THREAD_WORKISWAITING. We send the split point as a second parameter to the // idle loop, which means that the main thread will return from the idle // loop when all threads have finished their work at this split point // (i.e. when splitPoint->cpus == 0). @@ -2949,8 +2970,7 @@ namespace { *beta = splitPoint->beta; *bestValue = splitPoint->bestValue; - threads[master].stop = false; - threads[master].idle = false; + threads[master].stopRequest = false; threads[master].activeSplitPoints--; threads[master].splitPoint = splitPoint->parent; @@ -2974,10 +2994,9 @@ namespace { for (int i = 1; i < ActiveThreads; i++) { - assert(threads[i].sleeping == true); + assert(threads[i].state == THREAD_SLEEPING); - threads[i].idle = true; - threads[i].workIsWaiting = false; + threads[i].state = THREAD_AVAILABLE; } #if !defined(_MSC_VER) @@ -2985,31 +3004,34 @@ namespace { pthread_cond_broadcast(&WaitCond); pthread_mutex_unlock(&WaitLock); #else - for (int i = 1; i < THREAD_MAX; i++) + for (int i = 1; i < MAX_THREADS; 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); } // put_threads_to_sleep() makes all the threads go to sleep just before - // to leave think(), at the end of the search. threads should have already + // to leave think(), at the end of the search. Threads should have already // finished the job and should be idle. void ThreadsManager::put_threads_to_sleep() { assert(!AllThreadsShouldSleep); + // This makes the threads to go to sleep AllThreadsShouldSleep = true; - // Wait for the threads to be all sleeping + // Wait for the threads to be all sleeping and reset flags + // to a known state. for (int i = 1; i < ActiveThreads; i++) - while (!threads[i].sleeping); - } + { + while (threads[i].state != THREAD_SLEEPING); + // These two flags can be in a random state + threads[i].stopRequest = threads[i].printCurrentLineRequest = false; + } + } // print_current_line() prints _once_ the current line of search for a // given thread and then setup the print request for the next thread. @@ -3026,7 +3048,7 @@ namespace { // One shot only threads[threadID].printCurrentLineRequest = false; - if (!threads[threadID].idle) + if (threads[threadID].state == THREAD_SEARCHING) { lock_grab(&IOLock); cout << "info currline " << (threadID + 1);