从 Stockfish 合并更多 thread 代码

This commit is contained in:
Calcitem 2020-07-11 17:02:36 +08:00
parent 60e753be3c
commit 22b38ddd1d
9 changed files with 455 additions and 231 deletions

View File

@ -75,6 +75,7 @@ std::ostream &operator<<(std::ostream &os, Term t)
os << " | " << scores[t][WHITE] - scores[t][BLACK] << "\n";
#endif
t = t;
return os;
}
}
@ -213,5 +214,6 @@ std::string Eval::trace(Position *pos)
return ss.str();
#endif
pos = pos;
return "";
}

View File

@ -134,6 +134,7 @@ namespace CTSL //Concurrent Thread Safe Library
void resize(size_t o)
{
// TODO
o = o;
return;
}

View File

@ -139,6 +139,10 @@ Position &Position::set(const string &fenStr, StateInfo *si, Thread *th)
assert(pos_is_ok());
#endif
th = th;
si = si;
string str = fenStr;
str = "";
return *this;
}
@ -162,6 +166,7 @@ void Position::set_state(StateInfo *si) const
if (sideToMove == BLACK)
si->key ^= Zobrist::side;
#endif
si = si;
}
@ -188,6 +193,10 @@ Position &Position::set(const string &code, Color c, StateInfo *si)
return set(fenStr, si, nullptr);
#endif
si = si;
c = c;
string ccc = code;
ccc = "";
return *this;
}

View File

@ -17,20 +17,31 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstring> // For std::memset
#include <iostream>
#include <sstream>
#include "evaluate.h"
#include "misc.h"
#include "movegen.h"
#include "movepick.h"
#include "position.h"
#include "search.h"
#include "thread.h"
#include "timeman.h"
#include "tt.h"
#include "uci.h"
#include <array>
#include <chrono>
#include <algorithm>
#include "search.h"
#include "evaluate.h"
#include "hashmap.h"
#include "tt.h"
#include "endgame.h"
#include "types.h"
#include "option.h"
#include "misc.h"
#include "movepick.h"
using namespace CTSL;
@ -39,6 +50,8 @@ namespace Search
LimitsType Limits;
}
using namespace Search;
vector<Key> moveHistory;
AIAlgorithm::AIAlgorithm()
@ -738,4 +751,146 @@ void Search::clear()
{
// TODO
return;
}
}
/// MainThread::search() is started when the program receives the UCI 'go'
/// command. It searches from the root position and outputs the "bestmove".
void MainThread::search()
{
// TODO
#if 0
if (Limits.perft) {
nodes = perft<true>(rootPos, Limits.perft);
sync_cout << "\nNodes searched: " << nodes << "\n" << sync_endl;
return;
}
Color us = rootPos.side_to_move();
Time.init(Limits, us, rootPos.game_ply());
TT.new_search();
if (rootMoves.empty()) {
rootMoves.emplace_back(MOVE_NONE);
sync_cout << "info depth 0 score "
<< UCI::value(false /* TODO */ ? -VALUE_MATE : VALUE_DRAW)
<< sync_endl;
} else {
for (Thread *th : Threads) {
th->bestMoveChanges = 0;
if (th != this)
th->start_searching();
}
Thread::search(); // Let's start searching!
}
// When we reach the maximum depth, we can arrive here without a raise of
// Threads.stop. However, if we are pondering or in an infinite search,
// the UCI protocol states that we shouldn't print the best move before the
// GUI sends a "stop" or "ponderhit" command. We therefore simply wait here
// until the GUI sends one of those commands.
while (!Threads.stop && (ponder || Limits.infinite)) {
} // Busy wait for a stop or a ponder reset
// Stop the threads if not already stopped (also raise the stop if
// "ponderhit" just reset Threads.ponder).
Threads.stop = true;
// Wait until all threads have finished
for (Thread *th : Threads)
if (th != this)
th->wait_for_search_finished();
// When playing in 'nodes as time' mode, subtract the searched nodes from
// the available ones before exiting.
if (Limits.npmsec)
Time.availableNodes += Limits.inc[us] - Threads.nodes_searched();
Thread *bestThread = this;
// Check if there are threads with a better score than main thread
if (Options["MultiPV"] == 1
&& !Limits.depth
&& !(Skill((int)Options["Skill Level"]).enabled() || Options["UCI_LimitStrength"])
&& rootMoves[0].pv[0] != MOVE_NONE) {
std::map<Move, int64_t> votes;
Value minScore = this->rootMoves[0].score;
// Find minimum score
for (Thread *th : Threads)
minScore = std::min(minScore, th->rootMoves[0].score);
// Vote according to score and depth, and select the best thread
for (Thread *th : Threads) {
votes[th->rootMoves[0].pv[0]] +=
(th->rootMoves[0].score - minScore + 14) * int(th->completedDepth);
if (abs(bestThread->rootMoves[0].score) >= VALUE_TB_WIN_IN_MAX_PLY) {
// Make sure we pick the shortest mate / TB conversion or stave off mate the longest
if (th->rootMoves[0].score > bestThread->rootMoves[0].score)
bestThread = th;
} else if (th->rootMoves[0].score >= VALUE_TB_WIN_IN_MAX_PLY
|| (th->rootMoves[0].score > VALUE_TB_LOSS_IN_MAX_PLY
&& votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]]))
bestThread = th;
}
}
bestPreviousScore = bestThread->rootMoves[0].score;
// Send again PV info if we have a new best thread
if (bestThread != this)
sync_cout << UCI::pv(bestThread->rootPos, bestThread->completedDepth, -VALUE_INFINITE, VALUE_INFINITE) << sync_endl;
sync_cout << "bestmove " << UCI::move(bestThread->rootMoves[0].pv[0]);
if (bestThread->rootMoves[0].pv.size() > 1 || bestThread->rootMoves[0].extract_ponder_from_tt(rootPos))
std::cout << " ponder " << UCI::move(bestThread->rootMoves[0].pv[1]);
std::cout << sync_endl;
#endif
}
/// Thread::search() is the main iterative deepening loop. It calls search()
/// repeatedly with increasing depth until the allocated thinking time has been
/// consumed, the user stops the search, or the maximum search depth is reached.
void Thread::search()
{
// TODO
return;
}
/// MainThread::check_time() is used to print debug info and, more importantly,
/// to detect when we are out of available time and thus stop the search.
void MainThread::check_time()
{
if (--callsCnt > 0)
return;
// When using nodes, ensure checking rate is not lower than 0.1% of nodes
callsCnt = Limits.nodes ? std::min(1024, int(Limits.nodes / 1024)) : 1024;
static TimePoint lastInfoTime = now();
TimePoint elapsed = Time.elapsed();
TimePoint tick = Limits.startTime + elapsed;
if (tick - lastInfoTime >= 1000) {
lastInfoTime = tick;
dbg_print();
}
// We should not stop pondering until told so by the GUI
if (ponder)
return;
if ((Limits.use_time_management() && (elapsed > Time.maximum() - 10 || stopOnPonderhit))
|| (Limits.movetime && elapsed >= Limits.movetime)
|| (Limits.nodes && Threads.nodes_searched() >= (uint64_t)Limits.nodes))
Threads.stop = true;
}

View File

@ -47,6 +47,60 @@ using namespace CTSL;
namespace Search
{
/// Threshold used for countermoves based pruning
constexpr int CounterMovePruneThreshold = 0;
/// Stack struct keeps track of the information we need to remember from nodes
/// shallower and deeper in the tree during the search. Each search thread has
/// its own array of Stack objects, indexed by the current ply.
struct Stack
{
Move *pv;
int ply;
Move currentMove;
Move excludedMove;
Move killers[2];
Value staticEval;
int statScore;
int moveCount;
bool inCheck;
};
/// RootMove struct is used for moves at the root of the tree. For each root move
/// we store a score and a PV (really a refutation in the case of moves which
/// fail low). Score is normally set at -VALUE_INFINITE for all non-pv moves.
struct RootMove
{
explicit RootMove(Move m) : pv(1, m)
{
}
bool operator==(const Move &m) const
{
return pv[0] == m;
}
bool operator<(const RootMove &m) const
{ // Sort in descending order
return m.score != score ? m.score < score
: m.previousScore < previousScore;
}
Value score = -VALUE_INFINITE;
Value previousScore = -VALUE_INFINITE;
int selDepth = 0;
int tbRank = 0;
int bestMoveCount = 0;
Value tbScore;
std::vector<Move> pv;
};
typedef std::vector<RootMove> RootMoves;
/// LimitsType struct stores information sent by GUI about available time to
/// search the current move, maximum depth/time, or if we are in analysis mode.

View File

@ -1,16 +1,16 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Fishmill, a UCI Mill Game playing engine derived from Stockfish
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2019-2020 Calcitem <calcitem@outlook.com>
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad (Stockfish author)
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad (Stockfish author)
Copyright (C) 2020 Calcitem <calcitem@outlook.com>
Stockfish is free software: you can redistribute it and/or modify
Fishmill is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
Fishmill is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
@ -25,6 +25,7 @@
#include "movegen.h"
#include "search.h"
#include "thread.h"
#include "uci.h"
#include "tt.h"
ThreadPool Threads; // Global object
@ -33,161 +34,184 @@ ThreadPool Threads; // Global object
/// Thread constructor launches the thread and waits until it goes to sleep
/// in idle_loop(). Note that 'searching' and 'exit' should be already set.
Thread::Thread(size_t n) : idx(n), stdThread(&Thread::idle_loop, this) {
Thread::Thread(size_t n) : idx(n), stdThread(&Thread::idle_loop, this)
{
wait_for_search_finished();
wait_for_search_finished();
}
/// Thread destructor wakes up the thread in idle_loop() and waits
/// for its termination. Thread should be already waiting.
Thread::~Thread() {
Thread::~Thread()
{
assert(!searching);
assert(!searching);
exit = true;
start_searching();
stdThread.join();
exit = true;
start_searching();
stdThread.join();
}
/// Thread::bestMoveCount(Move move) return best move counter for the given root move
int Thread::best_move_count(Move move) const
{
auto rm = std::find(rootMoves.begin() + pvIdx,
rootMoves.begin() + pvLast, move);
return rm != rootMoves.begin() + pvLast ? rm->bestMoveCount : 0;
}
/// Thread::clear() reset histories, usually before a new game
void Thread::clear() {
void Thread::clear()
{
// TODO
}
/// Thread::start_searching() wakes up the thread that will start the search
void Thread::start_searching() {
void Thread::start_searching()
{
std::lock_guard<Mutex> lk(mutex);
searching = true;
cv.notify_one(); // Wake up the thread in idle_loop()
std::lock_guard<std::mutex> lk(mutex);
searching = true;
cv.notify_one(); // Wake up the thread in idle_loop()
}
/// Thread::wait_for_search_finished() blocks on the condition variable
/// until the thread has finished searching.
void Thread::wait_for_search_finished() {
void Thread::wait_for_search_finished()
{
std::unique_lock<Mutex> lk(mutex);
cv.wait(lk, [&]{ return !searching; });
std::unique_lock<std::mutex> lk(mutex);
cv.wait(lk, [&] { return !searching; });
}
/// Thread::idle_loop() is where the thread is parked, blocked on the
/// condition variable, when it has no work to do.
void Thread::idle_loop() {
void Thread::idle_loop()
{
// If OS already scheduled us on a different group than 0 then don't overwrite
// the choice, eventually we are one of many one-threaded processes running on
// some Windows NUMA hardware, for instance in fishtest. To make it simple,
// just check if running threads are below a threshold, in this case all this
// NUMA machinery is not needed.
//if (Options["Threads"] > 8)
// WinProcGroup::bindThisThread(idx);
// If OS already scheduled us on a different group than 0 then don't overwrite
// the choice, eventually we are one of many one-threaded processes running on
// some Windows NUMA hardware, for instance in fishtest. To make it simple,
// just check if running threads are below a threshold, in this case all this
// NUMA machinery is not needed.
if (Options["Threads"] > 8)
WinProcGroup::bindThisThread(idx);
while (true)
{
std::unique_lock<Mutex> lk(mutex);
searching = false;
cv.notify_one(); // Wake up anyone waiting for search finished
cv.wait(lk, [&]{ return searching; });
while (true) {
std::unique_lock<std::mutex> lk(mutex);
searching = false;
cv.notify_one(); // Wake up anyone waiting for search finished
cv.wait(lk, [&] { return searching; });
if (exit)
return;
if (exit)
return;
lk.unlock();
lk.unlock();
//search();
}
search();
}
}
/// ThreadPool::set() creates/destroys threads to match the requested number.
/// Created and launched threads will immediately go to sleep in idle_loop.
/// Upon resizing, threads are recreated to allow for binding if necessary.
void ThreadPool::set(size_t requested) {
void ThreadPool::set(size_t requested)
{
if (size() > 0) { // destroy any existing thread(s)
main()->wait_for_search_finished();
if (size() > 0) { // destroy any existing thread(s)
main()->wait_for_search_finished();
while (size() > 0)
delete back(), pop_back();
}
while (size() > 0)
delete back(), pop_back();
}
if (requested > 0) { // create new thread(s)
push_back(new MainThread(0));
if (requested > 0) { // create new thread(s)
push_back(new MainThread(0));
while (size() < requested)
push_back(new Thread(size()));
clear();
while (size() < requested)
push_back(new Thread(size()));
clear();
// Reallocate the key with the new threadpool size
//TT.resize(Options["Hash"]);
}
// Reallocate the hash with the new threadpool size
TT.resize((size_t)Options["Hash"]);
// Init thread number dependent search params.
Search::init();
}
}
/// ThreadPool::clear() sets threadPool data to initial values.
void ThreadPool::clear() {
void ThreadPool::clear()
{
for (Thread* th : *this)
th->clear();
for (Thread *th : *this)
th->clear();
main()->callsCnt = 0;
main()->previousScore = VALUE_INFINITE;
main()->previousTimeReduction = 1.0;
main()->callsCnt = 0;
main()->bestPreviousScore = VALUE_INFINITE;
main()->previousTimeReduction = 1.0;
}
/// ThreadPool::start_thinking() wakes up main thread waiting in idle_loop() and
/// returns immediately. Main thread will wake up other threads and start the search.
#if 0
void ThreadPool::start_thinking(Position& pos, StateListPtr& states,
const Search::LimitsType& limits, bool ponderMode) {
main()->wait_for_search_finished();
main()->stopOnPonderhit = stop = false;
main()->ponder = ponderMode;
Search::Limits = limits;
Search::RootMoves rootMoves;
void ThreadPool::start_thinking(Position *pos, StateListPtr &states,
const Search::LimitsType &limits, bool ponderMode)
{
for (const auto& m : MoveList<LEGAL>(pos))
if ( limits.searchmoves.empty()
|| std::count(limits.searchmoves.begin(), limits.searchmoves.end(), m))
rootMoves.emplace_back(m);
main()->wait_for_search_finished();
if (!rootMoves.empty())
Tablebases::rank_root_moves(pos, rootMoves);
main()->stopOnPonderhit = stop = false;
increaseDepth = true;
main()->ponder = ponderMode;
Search::Limits = limits;
Search::RootMoves rootMoves;
// After ownership transfer 'states' becomes empty, so if we stop the search
// and call 'go' again without setting a new position states.get() == NULL.
assert(states.get() || setupStates.get());
for (const auto &m : MoveList(pos))
if (limits.searchmoves.empty()
|| std::count(limits.searchmoves.begin(), limits.searchmoves.end(), m))
rootMoves.emplace_back(m);
if (states.get())
setupStates = std::move(states); // Ownership transfer, states is now empty
// We use Position::set() to set root position across threads. But there are
// some StateInfo fields (previous, pliesFromNull, capturedPiece) that cannot
// be deduced from a fen string, so set() clears them and to not lose the info
// we need to backup and later restore setupStates->back(). Note that setupStates
// is shared by threads but is accessed in read-only mode.
StateInfo tmp = setupStates->back();
for (Thread* th : *this)
{
th->shuffleExts = th->nodes = th->tbHits = th->nmpMinPly = 0;
th->rootDepth = th->completedDepth = DEPTH_ZERO;
th->rootMoves = rootMoves;
th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th);
}
setupStates->back() = tmp;
main()->start_searching();
}
#ifdef TBPROBE
if (!rootMoves.empty())
Tablebases::rank_root_moves(pos, rootMoves);
#endif
// After ownership transfer 'states' becomes empty, so if we stop the search
// and call 'go' again without setting a new position states.get() == NULL.
assert(states.get() || setupStates.get());
if (states.get())
setupStates = std::move(states); // Ownership transfer, states is now empty
// We use Position::set() to set root position across threads. But there are
// some StateInfo fields (previous, pliesFromNull, capturedPiece) that cannot
// be deduced from a fen string, so set() clears them and to not lose the info
// we need to backup and later restore setupStates->back(). Note that setupStates
// is shared by threads but is accessed in read-only mode.
StateInfo tmp = setupStates->back();
for (Thread *th : *this) {
th->nodes = th->tbHits = th->nmpMinPly = 0;
th->rootDepth = th->completedDepth = 0;
th->rootMoves = rootMoves;
th->rootPos.set(pos->fen(), &setupStates->back(), th);
}
setupStates->back() = tmp;
main()->start_searching();
}

View File

@ -1,16 +1,16 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Fishmill, a UCI Mill Game playing engine derived from Stockfish
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2019-2020 Calcitem <calcitem@outlook.com>
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad (Stockfish author)
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad (Stockfish author)
Copyright (C) 2020 Calcitem <calcitem@outlook.com>
Stockfish is free software: you can redistribute it and/or modify
Fishmill is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
Fishmill is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
@ -28,58 +28,64 @@
#include <thread>
#include <vector>
#include "movepick.h"
#include "position.h"
#include "search.h"
#include "thread_win32_osx.h"
#include "config.h"
class Position;
/// Thread class keeps together all the thread-related stuff. We use
/// per-thread pawn and material key tables so that once we get a
/// per-thread pawn and material hash tables so that once we get a
/// pointer to an entry its life time is unlimited and we don't have
/// to care about someone changing the entry under our feet.
class Thread {
Mutex mutex;
ConditionVariable cv;
size_t idx;
bool exit = false, searching = true; // Set before starting std::thread
NativeThread stdThread;
class Thread
{
std::mutex mutex;
std::condition_variable cv;
size_t idx;
bool exit = false, searching = true; // Set before starting std::thread
NativeThread stdThread;
public:
explicit Thread(size_t);
virtual ~Thread();
//virtual void search();
void clear();
void idle_loop();
void start_searching();
void wait_for_search_finished();
explicit Thread(size_t);
virtual ~Thread();
virtual void search();
void clear();
void idle_loop();
void start_searching();
void wait_for_search_finished();
int best_move_count(Move move) const;
size_t pvIdx, multiPV, pvLast, shuffleExts;
int selDepth, nmpMinPly;
std::atomic<uint64_t> nodes, tbHits, bestMoveChanges;
size_t pvIdx, pvLast;
uint64_t ttHitAverage;
int selDepth, nmpMinPly;
Color nmpColor;
std::atomic<uint64_t> nodes, tbHits, bestMoveChanges;
Position rootPos;
Position rootPos;
Search::RootMoves rootMoves;
Depth rootDepth, completedDepth;
Score contempt;
};
/// MainThread is a derived class specific for main thread
struct MainThread : public Thread {
struct MainThread : public Thread
{
using Thread::Thread;
using Thread::Thread;
//void search() override;
//void check_time();
void search() override;
void check_time();
double previousTimeReduction;
Value previousScore;
int callsCnt;
bool stopOnPonderhit;
std::atomic_bool ponder;
double previousTimeReduction;
Value bestPreviousScore;
Value iterValue[4];
int callsCnt;
bool stopOnPonderhit;
std::atomic_bool ponder;
};
@ -87,28 +93,39 @@ struct MainThread : public Thread {
/// parking and, most importantly, launching a thread. All the access to threads
/// is done through this class.
struct ThreadPool : public std::vector<Thread*> {
struct ThreadPool : public std::vector<Thread *>
{
//void start_thinking(Position&, StateListPtr&, const Search::LimitsType&, bool = false);
void clear();
void set(size_t);
void start_thinking(Position *, StateListPtr &, const Search::LimitsType &, bool = false);
void clear();
void set(size_t);
MainThread* main() const { return static_cast<MainThread*>(front()); }
uint64_t nodes_searched() const { return accumulate(&Thread::nodes); }
uint64_t tb_hits() const { return accumulate(&Thread::tbHits); }
MainThread *main() const
{
return static_cast<MainThread *>(front());
}
uint64_t nodes_searched() const
{
return accumulate(&Thread::nodes);
}
uint64_t tb_hits() const
{
return accumulate(&Thread::tbHits);
}
std::atomic_bool stop;
std::atomic_bool stop, increaseDepth;
private:
//StateListPtr setupStates;
StateListPtr setupStates;
uint64_t accumulate(std::atomic<uint64_t> Thread::* member) const {
uint64_t accumulate(std::atomic<uint64_t> Thread:: *member) const
{
uint64_t sum = 0;
for (Thread* th : *this)
sum += (th->*member).load(std::memory_order_relaxed);
return sum;
}
uint64_t sum = 0;
for (Thread *th : *this)
sum += (th->*member).load(std::memory_order_relaxed);
return sum;
}
};
extern ThreadPool Threads;

View File

@ -1,15 +1,16 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Fishmill, a UCI Mill Game playing engine derived from Stockfish
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad (Stockfish author)
Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad (Stockfish author)
Copyright (C) 2020 Calcitem <calcitem@outlook.com>
Stockfish is free software: you can redistribute it and/or modify
Fishmill is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
Fishmill is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
@ -21,86 +22,47 @@
#ifndef THREAD_WIN32_OSX_H_INCLUDED
#define THREAD_WIN32_OSX_H_INCLUDED
/// STL thread library used by mingw and gcc when cross compiling for Windows
/// relies on libwinpthread. Currently libwinpthread implements mutexes directly
/// on top of Windows semaphores. Semaphores, being kernel objects, require kernel
/// mode transition in order to lock or unlock, which is very slow compared to
/// interlocked operations (about 30% slower on bench test). To work around this
/// issue, we define our wrappers to the low level Win32 calls. We use critical
/// sections to support Windows XP and older versions. Unfortunately, cond_wait()
/// is racy between unlock() and WaitForSingleObject() but they have the same
/// speed performance as the SRW locks.
#include <condition_variable>
#include <mutex>
#include <thread>
#if defined(_WIN32) && !defined(_MSC_VER)
#ifndef NOMINMAX
# define NOMINMAX // Disable macros min() and max()
#endif
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#undef WIN32_LEAN_AND_MEAN
#undef NOMINMAX
/// Mutex and ConditionVariable struct are wrappers of the low level locking
/// machinery and are modeled after the corresponding C++11 classes.
struct Mutex {
Mutex() { InitializeCriticalSection(&cs); }
~Mutex() { DeleteCriticalSection(&cs); }
void lock() { EnterCriticalSection(&cs); }
void unlock() { LeaveCriticalSection(&cs); }
private:
CRITICAL_SECTION cs;
};
typedef std::condition_variable_any ConditionVariable;
#else // Default case: use STL classes
typedef std::mutex Mutex;
typedef std::condition_variable ConditionVariable;
#endif
/// On OSX threads other than the main thread are created with a reduced stack
/// size of 512KB by default, this is dangerously low for deep searches, so
/// adjust it to TH_STACK_SIZE. The implementation calls pthread_create() with
/// proper stack size parameter.
/// size of 512KB by default, this is too low for deep searches, which require
/// somewhat more than 1MB stack, so adjust it to TH_STACK_SIZE.
/// The implementation calls pthread_create() with the stack size parameter
/// equal to the linux 8MB default, on platforms that support it.
#if defined(__APPLE__)
#if defined(__APPLE__) || defined(__MINGW32__) || defined(__MINGW64__)
#include <pthread.h>
static const size_t TH_STACK_SIZE = 2 * 1024 * 1024;
static const size_t TH_STACK_SIZE = 8 * 1024 * 1024;
template <class T, class P = std::pair<T*, void(T::*)()>>
void* start_routine(void* ptr)
template <class T, class P = std::pair<T *, void(T:: *)()>>
void *start_routine(void *ptr)
{
P* p = reinterpret_cast<P*>(ptr);
(p->first->*(p->second))(); // Call member function pointer
delete p;
return NULL;
P *p = reinterpret_cast<P *>(ptr);
(p->first->*(p->second))(); // Call member function pointer
delete p;
return NULL;
}
class NativeThread {
class NativeThread
{
pthread_t thread;
pthread_t thread;
public:
template<class T, class P = std::pair<T*, void(T::*)()>>
explicit NativeThread(void(T::*fun)(), T* obj) {
pthread_attr_t attr_storage, *attr = &attr_storage;
pthread_attr_init(attr);
pthread_attr_setstacksize(attr, TH_STACK_SIZE);
pthread_create(&thread, attr, start_routine<T>, new P(obj, fun));
}
void join() { pthread_join(thread, NULL); }
template<class T, class P = std::pair<T *, void(T:: *)()>>
explicit NativeThread(void(T:: *fun)(), T *obj)
{
pthread_attr_t attr_storage, *attr = &attr_storage;
pthread_attr_init(attr);
pthread_attr_setstacksize(attr, TH_STACK_SIZE);
pthread_create(&thread, attr, start_routine<T>, new P(obj, fun));
}
void join()
{
pthread_join(thread, NULL);
}
};
#else // Default case: use STL classes

View File

@ -133,7 +133,7 @@ namespace {
else if (token == "infinite") limits.infinite = 1;
else if (token == "ponder") ponderMode = true;
//Threads.start_thinking(pos, states, limits, ponderMode); // TODO
Threads.start_thinking(&pos, states, limits, ponderMode); // TODO
}