mirror of
https://github.com/HChaZZY/Stockfish.git
synced 2025-12-25 03:26:24 +08:00
RootInTB was an incorrectly shared global, probably leading to wrong scoreing Minor: setting TB global state from input by all threads (all threads write same values) setting Limits global state by all threads (idem) thread counting for finalization CI can be enabled once races are fixed in the learner, manually goes like: ``` make clean && make -j2 ARCH=x86-64-modern sanitize=thread optimize=no debug=yes build ../tests/instrumented_learn.sh --sanitizer-thread ``` Needs some review.
136 lines
3.9 KiB
C++
136 lines
3.9 KiB
C++
#include "multi_think.h"
|
|
|
|
#include "tt.h"
|
|
#include "uci.h"
|
|
#include "types.h"
|
|
#include "search.h"
|
|
|
|
#include <thread>
|
|
|
|
void MultiThink::go_think()
|
|
{
|
|
// Keep a copy to restore the Options settings later.
|
|
auto oldOptions = Options;
|
|
|
|
// When using the constant track, it takes a lot of time to perform on the fly & the part to access the file is
|
|
// Since it is not thread safe, it is guaranteed here that it is being completely read in memory.
|
|
Options["BookOnTheFly"] = std::string("false");
|
|
|
|
// Read evaluation function, etc.
|
|
// In the case of the learn command, the value of the evaluation function may be corrected after reading the evaluation function, so
|
|
// Skip memory corruption check.
|
|
Eval::init_NNUE();
|
|
|
|
// Call the derived class's init().
|
|
init();
|
|
|
|
// init global vars
|
|
Tablebases::init();
|
|
|
|
// About Search::Limits
|
|
// Be careful because this member variable is global and affects other threads.
|
|
{
|
|
auto& limits = Search::Limits;
|
|
|
|
// Make the search equivalent to the "go infinite" command. (Because it is troublesome if time management is done)
|
|
limits.infinite = true;
|
|
|
|
// Since PV is an obstacle when displayed, erase it.
|
|
limits.silent = true;
|
|
|
|
// If you use this, it will be compared with the accumulated nodes of each thread. Therefore, do not use it.
|
|
limits.nodes = 0;
|
|
|
|
// depth is also processed by the one passed as an argument of Learner::search().
|
|
limits.depth = 0;
|
|
}
|
|
|
|
// The loop upper limit is set with set_loop_max().
|
|
loop_count = 0;
|
|
done_count = 0;
|
|
|
|
// Create threads as many as Options["Threads"] and start thinking.
|
|
std::vector<std::thread> threads;
|
|
auto thread_num = (size_t)Options["Threads"];
|
|
|
|
// Secure end flag of worker thread
|
|
threads_finished=0;
|
|
|
|
// start worker thread
|
|
for (size_t i = 0; i < thread_num; ++i)
|
|
{
|
|
threads.push_back(std::thread([i, this]
|
|
{
|
|
// exhaust all processor threads.
|
|
WinProcGroup::bindThisThread(i);
|
|
|
|
// execute the overridden process
|
|
this->thread_worker(i);
|
|
|
|
// Set the end flag because the thread has ended
|
|
this->threads_finished++;
|
|
}));
|
|
}
|
|
|
|
// wait for all threads to finish
|
|
// for (auto& th :threads)
|
|
// th.join();
|
|
// If you write like, the thread will rush here while it is still working,
|
|
// During that time, callback_func() cannot be called and you cannot save.
|
|
// Therefore, you need to check the end flag yourself.
|
|
|
|
// function to determine if all threads have finished
|
|
auto threads_done = [&]()
|
|
{
|
|
return threads_finished == thread_num;
|
|
};
|
|
|
|
// Call back if the callback function is set.
|
|
auto do_a_callback = [&]()
|
|
{
|
|
if (callback_func)
|
|
callback_func();
|
|
};
|
|
|
|
|
|
for (uint64_t i = 0 ; ; )
|
|
{
|
|
// If all threads have finished, exit the loop.
|
|
if (threads_done())
|
|
break;
|
|
|
|
sleep(1000);
|
|
|
|
// callback_func() is called every callback_seconds.
|
|
if (++i == callback_seconds)
|
|
{
|
|
do_a_callback();
|
|
// Since I am returning from ↑, I reset the counter, so
|
|
// no matter how long it takes to save() etc. in do_a_callback()
|
|
// The next call will take a certain amount of time.
|
|
i = 0;
|
|
}
|
|
}
|
|
|
|
// Last save.
|
|
std::cout << std::endl << "finalize..";
|
|
|
|
// do_a_callback();
|
|
// → It should be saved by the caller, so I feel that it is not necessary here.
|
|
|
|
// It is possible that the exit code of the thread is running but the exit code of the thread is running, so
|
|
// We need to wait for the end with join().
|
|
for (auto& th : threads)
|
|
th.join();
|
|
|
|
// The file writing thread etc. are still running only when all threads are finished
|
|
// Since the work itself may not have completed, output only that all threads have finished.
|
|
std::cout << "all threads are joined." << std::endl;
|
|
|
|
// Restored because Options were rewritten.
|
|
// Restore the handler because the handler will not start unless you assign a value.
|
|
for (auto& s : oldOptions)
|
|
Options[s.first] = std::string(s.second);
|
|
|
|
}
|