$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_make)
@echo ""
@echo "Step 2/4. Running benchmark for pgo-build ..."
- @sleep 10 | $(PGOBENCH) > /dev/null
+ @$(PGOBENCH) > /dev/null
@echo ""
@echo "Step 3/4. Building final executable ..."
@touch *.cpp
$(MAKE) ARCH=x86-64 COMP=$(COMP) $(profile_make)
@echo ""
@echo "Step 2/6. Running benchmark for pgo-build (popcnt disabled)..."
- @sleep 10 | $(PGOBENCH) > /dev/null
+ @$(PGOBENCH) > /dev/null
@echo ""
@echo "Step 3/6. Building executable for benchmark (popcnt enabled)..."
@touch *.cpp *.h
$(MAKE) ARCH=x86-64-modern COMP=$(COMP) $(profile_make)
@echo ""
@echo "Step 4/6. Running benchmark for pgo-build (popcnt enabled)..."
- @sleep 10 | $(PGOBENCH) > /dev/null
+ @$(PGOBENCH) > /dev/null
@echo ""
@echo "Step 5/6. Building final executable ..."
@touch *.cpp *.h
#include "position.h"
#include "search.h"
+#include "thread.h"
#include "ucioption.h"
using namespace std;
void benchmark(int argc, char* argv[]) {
vector<string> fenList;
- SearchLimits limits;
int64_t totalNodes;
int time;
// Search should be limited by nodes, time or depth ?
if (valType == "nodes")
- limits.maxNodes = atoi(valStr.c_str());
+ Limits.maxNodes = atoi(valStr.c_str());
else if (valType == "time")
- limits.maxTime = 1000 * atoi(valStr.c_str()); // maxTime is in ms
+ Limits.maxTime = 1000 * atoi(valStr.c_str()); // maxTime is in ms
else
- limits.maxDepth = atoi(valStr.c_str());
+ Limits.maxDepth = atoi(valStr.c_str());
// Do we need to load positions from a given FEN file?
if (fenFile != "default")
// Ok, let's start the benchmark !
totalNodes = 0;
time = get_system_time();
+ SearchMoves.push_back(MOVE_NONE);
for (size_t i = 0; i < fenList.size(); i++)
{
- Move moves[] = { MOVE_NONE };
Position pos(fenList[i], false, 0);
+ RootPosition = &pos;
cerr << "\nBench position: " << i + 1 << '/' << fenList.size() << endl;
if (valType == "perft")
{
- int64_t cnt = perft(pos, limits.maxDepth * ONE_PLY);
+ int64_t cnt = perft(pos, Limits.maxDepth * ONE_PLY);
- cerr << "\nPerft " << limits.maxDepth
+ cerr << "\nPerft " << Limits.maxDepth
<< " nodes counted: " << cnt << endl;
totalNodes += cnt;
}
else
{
- if (!think(pos, limits, moves))
- break;
-
+ Threads.start_thinking(false);
totalNodes += pos.nodes_searched();
}
}
using std::endl;
using std::string;
+SearchLimits Limits;
+std::vector<Move> SearchMoves;
+Position* RootPosition;
+
namespace {
// Set to true to force running with one thread. Used for debugging
int MultiPV, UCIMultiPV, MultiPVIdx;
// Time management variables
- volatile bool StopOnPonderhit, FirstRootMove, StopRequest, QuitRequest, AspirationFailLow;
+ volatile bool StopOnPonderhit, FirstRootMove, StopRequest, AspirationFailLow;
TimeManager TimeMgr;
- SearchLimits Limits;
// Skill level adjustment
int SkillLevel;
string pv_to_uci(const Move pv[], int pvNum, bool chess960);
string pretty_pv(Position& pos, int depth, Value score, int time, Move pv[]);
string depth_to_uci(Depth depth);
- void wait_for_stop_or_ponderhit();
// MovePickerExt template class extends MovePicker and allows to choose at compile
// time the proper moves source according to the type of node. In the default case
/// variables, and calls id_loop(). It returns false when a "quit" command is
/// received during the search.
-bool think(Position& pos, const SearchLimits& limits, Move searchMoves[]) {
+void think() {
static Book book; // Defined static to initialize the PRNG only once
+ Position& pos = *RootPosition;
+
// Save "search start" time and reset elapsed time to zero
elapsed_search_time(get_system_time());
// Initialize global search-related variables
- StopOnPonderhit = StopRequest = QuitRequest = AspirationFailLow = false;
- Limits = limits;
+ StopOnPonderhit = StopRequest = AspirationFailLow = false;
// Set output stream mode: normal or chess960. Castling notation is different
cout << set960(pos.is_chess960());
Move bookMove = book.probe(pos, Options["Best Book Move"].value<bool>());
if (bookMove != MOVE_NONE)
{
- if (Limits.ponder)
- wait_for_stop_or_ponderhit();
+ if (!StopRequest && (Limits.ponder || Limits.infinite))
+ Threads.wait_for_stop_or_ponderhit();
cout << "bestmove " << bookMove << endl;
- return !QuitRequest;
+ return;
}
}
else
Threads.set_timer(100);
- // Start async mode to catch UCI commands sent to us while searching,
- // like "quit", "stop", etc.
- Threads.start_listener();
-
// We're ready to start thinking. Call the iterative deepening loop function
Move ponderMove = MOVE_NONE;
- Move bestMove = id_loop(pos, searchMoves, &ponderMove);
-
- // From now on any UCI command will be read in-sync with Threads.getline()
- Threads.stop_listener();
+ Move bestMove = id_loop(pos, &SearchMoves[0], &ponderMove);
// Stop timer, no need to check for available time any more
Threads.set_timer(0);
// we are pondering or in infinite search, we shouldn't print the best move
// before we are told to do so.
if (!StopRequest && (Limits.ponder || Limits.infinite))
- wait_for_stop_or_ponderhit();
+ Threads.wait_for_stop_or_ponderhit();
// Could be MOVE_NONE when searching on a stalemate position
cout << "bestmove " << bestMove;
cout << " ponder " << ponderMove;
cout << endl;
-
- return !QuitRequest;
}
}
- // wait_for_stop_or_ponderhit() is called when the maximum depth is reached
- // while the program is pondering. The point is to work around a wrinkle in
- // the UCI protocol: When pondering, the engine is not allowed to give a
- // "bestmove" before the GUI sends it a "stop" or "ponderhit" command.
- // We simply wait here until one of these commands (that raise StopRequest) is
- // sent, and return, after which the bestmove and pondermove will be printed.
-
- void wait_for_stop_or_ponderhit() {
-
- string cmd;
- StopOnPonderhit = true;
-
- while (!StopRequest)
- {
- Threads.getline(cmd);
- do_uci_async_cmd(cmd);
- }
- }
-
-
// When playing with strength handicap choose best move among the MultiPV set
// using a statistical rule dependent on SkillLevel. Idea by Heinz van Saanen.
}
-// do_uci_async_cmd() is called by listener thread when in async mode and 'cmd'
-// input line is received from the GUI.
+// ThreadsManager::wait_for_stop_or_ponderhit() is called when the maximum depth
+// is reached while the program is pondering. The point is to work around a wrinkle
+// in the UCI protocol: When pondering, the engine is not allowed to give a
+// "bestmove" before the GUI sends it a "stop" or "ponderhit" command.
+// We simply wait here until one of these commands (that raise StopRequest) is
+// sent, and return, after which the bestmove and pondermove will be printed.
+
+void ThreadsManager::wait_for_stop_or_ponderhit() {
+
+ StopOnPonderhit = true;
+
+ Thread& main = threads[0];
+
+ lock_grab(&main.sleepLock);
+
+ while (!StopRequest)
+ cond_wait(&main.sleepCond, &main.sleepLock);
+
+ lock_release(&main.sleepLock);
+}
+
-void do_uci_async_cmd(const std::string& cmd) {
+// uci_async_command() is called when a 'cmd' input line is received from the
+// GUI while searching.
- if (cmd == "quit")
- QuitRequest = StopRequest = true;
+void uci_async_command(const std::string& cmd) {
- else if (cmd == "stop")
+ if (cmd == "quit" || cmd == "stop")
StopRequest = true;
else if (cmd == "ponderhit")
#if !defined(SEARCH_H_INCLUDED)
#define SEARCH_H_INCLUDED
-#include <cstring>
-
#include "move.h"
#include "types.h"
+#include <vector>
+
class Position;
struct SplitPoint;
struct SearchLimits {
- SearchLimits() { memset(this, 0, sizeof(SearchLimits)); }
-
- SearchLimits(int t, int i, int mtg, int mt, int md, int mn, bool inf, bool pon)
- : time(t), increment(i), movesToGo(mtg), maxTime(mt), maxDepth(md),
- maxNodes(mn), infinite(inf), ponder(pon) {}
-
bool useTimeManagement() const { return !(maxTime | maxDepth | maxNodes | infinite); }
int time, increment, movesToGo, maxTime, maxDepth, maxNodes, infinite, ponder;
};
+extern SearchLimits Limits;
+extern std::vector<Move> SearchMoves;
+extern Position* RootPosition;
+
extern void init_search();
extern int64_t perft(Position& pos, Depth depth);
-extern bool think(Position& pos, const SearchLimits& limits, Move searchMoves[]);
-extern void do_uci_async_cmd(const std::string& cmd);
+extern void think();
+extern void uci_async_command(const std::string& cmd);
extern void do_timer_event();
#endif // !defined(SEARCH_H_INCLUDED)
void* start_routine(void* thread) {
#endif
- if (((Thread*)thread)->threadID == MAX_THREADS)
- ((Thread*)thread)->listener_loop();
+ if (((Thread*)thread)->threadID == 0)
+ ((Thread*)thread)->main_loop();
- else if (((Thread*)thread)->threadID == MAX_THREADS + 1)
+ else if (((Thread*)thread)->threadID == MAX_THREADS)
((Thread*)thread)->timer_loop();
else
((Thread*)thread)->idle_loop(NULL);
activeThreads = cnt;
- for (int i = 0; i < MAX_THREADS; i++)
+ for (int i = 1; i < MAX_THREADS; i++) // Ignore main thread
if (i < activeThreads)
{
// Dynamically allocate pawn and material hash tables according to the
void ThreadsManager::init() {
- // Initialize sleep condition used to block waiting for GUI input
+ // Initialize sleep condition used to block waiting for end of searching
cond_init(&sleepCond);
// Initialize threads lock, used when allocating slaves during splitting
lock_init(&threadsLock);
// Initialize sleep and split point locks
- for (int i = 0; i < MAX_THREADS + 2; i++)
+ for (int i = 0; i <= MAX_THREADS; i++)
{
lock_init(&threads[i].sleepLock);
cond_init(&threads[i].sleepCond);
}
// Initialize main thread's associated data
- threads[0].is_searching = true;
- threads[0].threadID = 0;
- set_size(1); // This makes all the threads but the main to go to sleep
+ threads[0].pawnTable.init();
+ threads[0].materialTable.init();
- // Create and launch all the threads but the main that is already running,
- // threads will go immediately to sleep.
- for (int i = 1; i < MAX_THREADS + 2; i++)
+ // Create and launch all the threads, threads will go immediately to sleep
+ for (int i = 0; i <= MAX_THREADS; i++)
{
threads[i].is_searching = false;
+ threads[i].do_sleep = true;
threads[i].threadID = i;
#if defined(_MSC_VER)
void ThreadsManager::exit() {
- for (int i = 0; i < MAX_THREADS + 2; i++)
+ for (int i = 0; i <= MAX_THREADS; i++)
{
- if (i != 0)
- {
- threads[i].do_terminate = true;
- threads[i].wake_up();
+ threads[i].do_terminate = true;
+ threads[i].wake_up();
- // Wait for slave termination
+ // Wait for slave termination
#if defined(_MSC_VER)
- WaitForSingleObject(threads[i].handle, 0);
- CloseHandle(threads[i].handle);
+ WaitForSingleObject(threads[i].handle, 0);
+ CloseHandle(threads[i].handle);
#else
- pthread_join(threads[i].handle, NULL);
+ pthread_join(threads[i].handle, NULL);
#endif
- }
// Now we can safely destroy locks and wait conditions
lock_destroy(&threads[i].sleepLock);
void ThreadsManager::set_timer(int msec) {
- Thread& timer = threads[MAX_THREADS + 1];
+ Thread& timer = threads[MAX_THREADS];
lock_grab(&timer.sleepLock);
timer.maxPly = msec;
}
-// Thread::listener_loop() is where the listener thread, used for I/O, waits for
-// input. When is_searching is false then input is read in sync with main thread
-// (that blocks), otherwise the listener thread reads any input asynchronously
-// and processes the input line calling do_uci_async_cmd().
+// Thread::main_loop() is where the main thread is parked waiting to be started
+// when there is a new search. Main thread will launch all the slave threads.
-void Thread::listener_loop() {
-
- std::string cmd;
+void Thread::main_loop() {
while (true)
{
lock_grab(&sleepLock);
- Threads.inputLine = cmd;
- do_sleep = !is_searching;
+ do_sleep = true; // Always return to sleep after a search
+
+ is_searching = false;
- // Here the thread is parked in sync mode after a line has been read
- while (do_sleep && !do_terminate) // Catches spurious wake ups
+ while (do_sleep && !do_terminate)
{
- cond_signal(&Threads.sleepCond); // Wake up main thread
- cond_wait(&sleepCond, &sleepLock); // Sleep here
+ cond_signal(&Threads.sleepCond); // Wake up UI thread if needed
+ cond_wait(&sleepCond, &sleepLock);
}
+ is_searching = true;
+
lock_release(&sleepLock);
if (do_terminate)
return;
- if (!std::getline(std::cin, cmd)) // Block waiting for input
- cmd = "quit";
-
- lock_grab(&sleepLock);
-
- // If we are in async mode then process the command now
- if (is_searching)
- {
- // Command "quit" is the last one received by the GUI, so park the
- // thread waiting for exiting. Also, after a "stop", for instance on a
- // ponder miss, GUI can immediately send the new position to search,
- // so return to in-sync mode to avoid discarding good data.
- if (cmd == "quit" || cmd == "stop")
- is_searching = false;
-
- do_uci_async_cmd(cmd);
- cmd = ""; // Input has been consumed
- }
-
- lock_release(&sleepLock);
+ think(); // Search entry point
}
}
-// ThreadsManager::getline() is used by main thread to block and wait for input,
-// the behaviour mimics std::getline().
-
-void ThreadsManager::getline(std::string& cmd) {
-
- Thread& listener = threads[MAX_THREADS];
+// ThreadsManager::start_thinking() is used by UI thread to wake up the main
+// thread parked in main_loop() and starting a new search. If asyncMode is true
+// then function returns immediately, otherwise caller is blocked waiting for
+// the search to finish.
- lock_grab(&listener.sleepLock);
+void ThreadsManager::start_thinking(bool asyncMode) {
- listener.is_searching = false; // Set sync mode
-
- // If there is already some input to grab then skip without to wake up the
- // listener. This can happen if after we send the "bestmove", the GUI sends
- // a command that the listener buffers in inputLine before going to sleep.
- if (inputLine.empty())
- {
- listener.do_sleep = false;
- cond_signal(&listener.sleepCond); // Wake up listener thread
-
- while (!listener.do_sleep)
- cond_wait(&sleepCond, &listener.sleepLock); // Wait for input
- }
-
- cmd = inputLine;
- inputLine = ""; // Input has been consumed
-
- lock_release(&listener.sleepLock);
-}
-
-
-// ThreadsManager::start_listener() is called at the beginning of the search to
-// swith from sync behaviour (default) to async and so be able to read from UCI
-// while other threads are searching. This avoids main thread polling for input.
-
-void ThreadsManager::start_listener() {
-
- Thread& listener = threads[MAX_THREADS];
-
- lock_grab(&listener.sleepLock);
- listener.is_searching = true;
- listener.do_sleep = false;
- cond_signal(&listener.sleepCond); // Wake up listener thread
- lock_release(&listener.sleepLock);
-}
+ Thread& main = threads[0];
+ lock_grab(&main.sleepLock);
-// ThreadsManager::stop_listener() is called before to send "bestmove" to GUI to
-// return to in-sync behaviour. This is needed because while in async mode any
-// command is discarded without being processed (except for a very few ones).
+ // Wait main thread has finished before to launch a new search
+ while (!main.do_sleep)
+ cond_wait(&sleepCond, &main.sleepLock);
-void ThreadsManager::stop_listener() {
+ main.do_sleep = false;
+ cond_signal(&main.sleepCond); // Wake up main thread
- Thread& listener = threads[MAX_THREADS];
+ if (!asyncMode)
+ cond_wait(&sleepCond, &main.sleepLock);
- lock_grab(&listener.sleepLock);
- listener.is_searching = false;
- lock_release(&listener.sleepLock);
+ lock_release(&main.sleepLock);
}
#include "movepick.h"
#include "pawns.h"
#include "position.h"
+#include "search.h"
const int MAX_THREADS = 32;
const int MAX_ACTIVE_SPLIT_POINTS = 8;
bool cutoff_occurred() const;
bool is_available_to(int master) const;
void idle_loop(SplitPoint* sp);
- void listener_loop();
+ void main_loop();
void timer_loop();
SplitPoint splitPoints[MAX_ACTIVE_SPLIT_POINTS];
bool available_slave_exists(int master) const;
bool split_point_finished(SplitPoint* sp) const;
- void getline(std::string& cmd);
- void start_listener();
- void stop_listener();
+ void start_thinking(bool asyncMode = true);
void set_timer(int msec);
+ void wait_for_stop_or_ponderhit();
template <bool Fake>
Value split(Position& pos, SearchStack* ss, Value alpha, Value beta, Value bestValue,
int activeThreads;
bool useSleepingThreads;
WaitCondition sleepCond;
- std::string inputLine;
};
extern ThreadsManager Threads;
void set_option(istringstream& up);
void set_position(Position& pos, istringstream& up);
- bool go(Position& pos, istringstream& up);
+ void go(Position& pos, istringstream& up);
void perft(Position& pos, istringstream& up);
}
string cmd, token;
bool quit = false;
- while (!quit)
+ while (!quit && getline(cin, cmd))
{
- Threads.getline(cmd);
-
istringstream is(cmd);
is >> skipws >> token;
- if (token == "quit")
- quit = true;
+ quit = (token == "quit");
- else if (token == "stop")
- { /* avoid to reply "Unknown command: stop" */ }
+ if (token == "quit" || token == "stop" || token == "ponderhit")
+ {
+ uci_async_command(token);
+ Threads[0].wake_up(); // In case is waiting for stop or ponderhit
+ }
else if (token == "go")
- quit = !go(pos, is);
+ go(pos, is);
else if (token == "ucinewgame")
pos.from_fen(StarFEN, false);
// string, and then calls think(). Returns false if a quit command
// is received while thinking, true otherwise.
- bool go(Position& pos, istringstream& is) {
+ void go(Position& pos, istringstream& is) {
string token;
- SearchLimits limits;
- std::vector<Move> searchMoves;
int time[] = { 0, 0 }, inc[] = { 0, 0 };
+ memset(&Limits, 0, sizeof(SearchLimits));
+ SearchMoves.clear();
+ RootPosition = &pos;
+
while (is >> token)
{
if (token == "infinite")
- limits.infinite = true;
+ Limits.infinite = true;
else if (token == "ponder")
- limits.ponder = true;
+ Limits.ponder = true;
else if (token == "wtime")
is >> time[WHITE];
else if (token == "btime")
else if (token == "binc")
is >> inc[BLACK];
else if (token == "movestogo")
- is >> limits.movesToGo;
+ is >> Limits.movesToGo;
else if (token == "depth")
- is >> limits.maxDepth;
+ is >> Limits.maxDepth;
else if (token == "nodes")
- is >> limits.maxNodes;
+ is >> Limits.maxNodes;
else if (token == "movetime")
- is >> limits.maxTime;
+ is >> Limits.maxTime;
else if (token == "searchmoves")
while (is >> token)
- searchMoves.push_back(move_from_uci(pos, token));
+ SearchMoves.push_back(move_from_uci(pos, token));
}
- searchMoves.push_back(MOVE_NONE);
- limits.time = time[pos.side_to_move()];
- limits.increment = inc[pos.side_to_move()];
+ SearchMoves.push_back(MOVE_NONE);
+ Limits.time = time[pos.side_to_move()];
+ Limits.increment = inc[pos.side_to_move()];
- return think(pos, limits, &searchMoves[0]);
+ Threads.start_thinking();
}