Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
6c0054b
Rewrite how the board is stored and moves are made
razvanfilea Nov 20, 2019
1c41cd3
Remove 'ToList' MoveGen template arg, make some fixes
razvanfilea Nov 22, 2019
2bfdff3
Fix a critical error with Board::doMove()
razvanfilea Nov 24, 2019
9db8df9
Fix getPhase always returning ENDGAME
razvanfilea Nov 24, 2019
8fd4e8c
Improve the Evaluation function performance
razvanfilea Nov 24, 2019
bee2ee1
Fix Board::hasValidState and update Pawn PSQT
razvanfilea Nov 26, 2019
68d9853
Switch to the previous Multi Threading model
razvanfilea Nov 28, 2019
7154003
Remove KING_DANGER MoveGen
razvanfilea Nov 28, 2019
eb1963c
Add age attribute to the TranspositionTable
razvanfilea Nov 29, 2019
2da9711
Fix crash when generating legal moves
razvanfilea Nov 30, 2019
f922ceb
Rewrite how the UI processes positions
razvanfilea Nov 30, 2019
220b4d7
Rename NegaMax class to Search
razvanfilea Nov 30, 2019
ee721fd
Work on the ThreadPool
razvanfilea Nov 30, 2019
9a9e5ac
Solve a bug with Castling and disable EnPassant for now
razvanfilea Nov 30, 2019
faba2c2
Add Generated Nodes to Stats
razvanfilea Dec 1, 2019
d73bb5c
Remake the Settings Screen
razvanfilea Dec 1, 2019
9b46c3e
Clean ThreadSafeQueue and ThreadPool
razvanfilea Dec 1, 2019
d5d5ae0
Revert to the old Pawn PSQT
razvanfilea Dec 8, 2019
1b60b38
Re-enable EnPassant
razvanfilea Dec 9, 2019
c0be29b
Add Platform Specific Implementations for BSF and BSR
razvanfilea Dec 10, 2019
8a64bbc
Remove StackVector
razvanfilea Dec 10, 2019
7889cfa
Fix compile errors
razvanfilea Dec 10, 2019
e35ab03
Remake the Pawn Evaluation add Connected Bonus
razvanfilea Dec 11, 2019
459400d
Merge Rays.h into Bitboard.h
razvanfilea Dec 11, 2019
9be2e38
Fix Pawn Move Generation from last commit
razvanfilea Dec 12, 2019
9a37891
Remove Light Theme, add Rook on Queen File Evaluation
razvanfilea Dec 15, 2019
b81a38c
Rewrite Piece class to only use 1 byte
razvanfilea Dec 15, 2019
3a122a9
New icon
razvanfilea Dec 15, 2019
99a93b5
Release 1.0
razvanfilea Dec 15, 2019
b8651a6
Fix issues with the Pull Request
razvanfilea Dec 15, 2019
8c8a8d9
Delete MoveOrdering.h
razvanfilea Dec 15, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Work on the ThreadPool
  • Loading branch information
razvanfilea committed Nov 30, 2019
commit ee721fd091c21c3f404e081c0abf8705d4b2be9a
26 changes: 16 additions & 10 deletions ChessAndroid/app/src/main/cpp/chess/algorithm/Search.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@

#include "../Stats.h"
#include "../data/Board.h"
#include "../threads/NegaMaxThreadPool.h"

bool Search::s_QuiescenceSearchEnabled{};
ThreadPool Search::s_ThreadPool(1);
TranspositionTable Search::s_SearchCache(1);
bool Search::s_QuiescenceSearchEnabled{};
short Search::s_BestMoveFound{};

RootMove Search::findBestMove(const Board &board, const Settings &settings)
Expand All @@ -29,7 +29,10 @@ RootMove Search::findBestMove(const Board &board, const Settings &settings)
// If the Transposition Table wasn't resized, increment its age
if (!s_SearchCache.setSize(settings.getCacheTableSizeMb()))
s_SearchCache.incrementAge();
NegaMaxThreadPool::updateThreadCount(threadCount);

// Update ThreadPool size if needed
if (s_ThreadPool.threadCount() != threadCount)
s_ThreadPool.updateThreadCount(threadCount);

if (board.getPhase() == Phase::ENDING)
++depth;
Expand All @@ -44,16 +47,16 @@ short Search::getBestMoveFound()

RootMove Search::negaMaxRoot(const std::vector<RootMove> &validMoves, const unsigned jobCount, const short ply)
{
std::vector<ThreadPool::TaskFuture<void>> futures;
std::vector<std::future<void>> futures;
futures.reserve(jobCount);

std::mutex mutex;

RootMove bestMove = validMoves.front();
auto currentMove = validMoves.begin();
const auto lastMove = validMoves.end();
short alpha = VALUE_MIN;

const auto doWork = [&] {
const auto worker = [&] {
mutex.lock();

while (currentMove != lastMove)
Expand All @@ -77,19 +80,22 @@ RootMove Search::negaMaxRoot(const std::vector<RootMove> &validMoves, const unsi
mutex.unlock();
};

futures.emplace_back(NegaMaxThreadPool::submitJob(doWork));
// Initially create only use thread
futures.emplace_back(s_ThreadPool.submitTask(worker));

if (jobCount > 1) {
if (jobCount > 1)
{
// Wait until an alpha bound has been found
while (alpha == VALUE_MIN)
std::this_thread::yield();

// After an alpha bound was found start searching using multiple threads
for (unsigned i = 1u; i < jobCount; ++i)
futures.emplace_back(NegaMaxThreadPool::submitJob(doWork));
futures.emplace_back(s_ThreadPool.submitTask(worker));
}

for (auto &future : futures)
future.get(); // Wait for the Search to finish
future.get(); // Wait for the search to finish

s_BestMoveFound = oppositeColor(validMoves.front().board.colorToMove) ? alpha : -alpha;

Expand Down
30 changes: 0 additions & 30 deletions ChessAndroid/app/src/main/cpp/chess/threads/NegaMaxThreadPool.h

This file was deleted.

209 changes: 70 additions & 139 deletions ChessAndroid/app/src/main/cpp/chess/threads/ThreadPool.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,120 +2,26 @@

#include "ThreadSafeQueue.hpp"

#include <algorithm>
#include <atomic>
#include <cstdint>
#include <functional>
#include <future>
#include <memory>
#include <thread>
#include <type_traits>
#include <utility>
#include <vector>

class ThreadPool
{
class IThreadTask
{
public:
IThreadTask() = default;
virtual ~IThreadTask() = default;
IThreadTask(const IThreadTask&) = delete;
IThreadTask &operator=(const IThreadTask&) = delete;
IThreadTask(IThreadTask&&) noexcept = default;
IThreadTask &operator=(IThreadTask&&) noexcept = default;

/**
* Run the task.
*/
virtual void execute() = 0;
};

template <typename Func>
class ThreadTask : public IThreadTask
{
public:
explicit ThreadTask(Func &&func)
: m_func{ std::move(func) } {}

~ThreadTask() override = default;
ThreadTask(const ThreadTask &rhs) = delete;
ThreadTask &operator=(const ThreadTask &rhs) = delete;
ThreadTask(ThreadTask&&) noexcept = default;
ThreadTask &operator=(ThreadTask&&) noexcept = default;

/**
* Run the task.
*/
void execute() override
{
m_func();
}

private:
Func m_func;
};

public:
/**
* A wrapper around a std::future that adds the behavior of futures returned from std::async.
* Specifically, this object will block and wait for execution to finish before going out of scope.
*/
template <typename T = void>
class TaskFuture
{
public:
explicit TaskFuture(std::future<T> &&future)
: m_future{ std::move(future) } {}

TaskFuture(const TaskFuture &rhs) = delete;
TaskFuture &operator=(const TaskFuture &rhs) = delete;
TaskFuture(TaskFuture &&other) = default;
TaskFuture &operator=(TaskFuture &&other) = default;
~TaskFuture()
{
if (m_future.valid())
m_future.get();
}

auto get()
{
return m_future.get();
}

bool ready(const std::size_t millis) const
{
return m_future.wait_for(std::chrono::milliseconds(millis)) == std::future_status::ready;
}


private:
std::future<T> m_future;
};

public:
/**
* Constructor.
*/
ThreadPool()
: ThreadPool { std::max(std::thread::hardware_concurrency() - 1u, 2u) }
{
/*
* Always create at least two threads.
*/
}

/**
* Constructor.
*/
explicit ThreadPool(const std::size_t numThreads)
: m_done{ false }
explicit ThreadPool(const std::size_t numThreads = std::thread::hardware_concurrency())
: m_Done{ false }
{
m_threads.reserve(numThreads);
m_Threads.reserve(numThreads);
try
{
for (auto i = 0u; i < numThreads; ++i)
m_threads.emplace_back(&ThreadPool::worker, this);
m_Threads.emplace_back(&ThreadPool::worker, this);
m_WaitingThreads = numThreads;
}
catch (...)
{
Expand Down Expand Up @@ -144,7 +50,7 @@ class ThreadPool

std::size_t threadCount() const noexcept
{
return m_threads.size();
return m_Threads.size();
}

void updateThreadCount(const std::size_t numThreads) noexcept(false)
Expand All @@ -153,49 +59,64 @@ class ThreadPool

if (numThreads < currentThreadCount)
{
m_done = true;
for (auto &thread : m_threads)
if (thread.joinable())
thread.join();
// Destroy all the threads and invalidate the queue
{
m_Done = true;
m_Queue.invalidate();
for (auto &thread : m_Threads)
if (thread.joinable())
thread.join();

m_threads.reserve(numThreads);
m_threads.clear();
m_Threads.clear();
}

try
{
m_Threads.reserve(numThreads);
m_Done = false;

m_Queue.~ThreadSafeQueue();
new(&m_Queue) QueueType();

for (auto i = 0u; i < numThreads; ++i)
m_threads.emplace_back(&ThreadPool::worker, this);
}
catch (...)
{
destroy();
throw;
m_Threads.emplace_back(&ThreadPool::worker, this);

m_WaitingThreads = numThreads;
}

m_done = false;
} else if (numThreads > currentThreadCount) {
m_threads.reserve(numThreads);
// Add the extra number of threads necessary
m_Threads.reserve(numThreads);

for (auto i = currentThreadCount; i < numThreads; ++i)
m_threads.emplace_back(&ThreadPool::worker, this);
m_Threads.emplace_back(&ThreadPool::worker, this);

m_WaitingThreads = numThreads;
}


// Else the size should be the same
// Otherwise the size should be the same
}

/**
* Submit a job to be run by the thread pool.
*/
template <typename ResultType, typename Func, typename... Args>
TaskFuture<ResultType> submit(Func &&func, Args &&...args)

template <typename Func, typename... Args>
void submitWork(Func &&func, Args &&...args)
{
auto boundTask = std::bind(std::forward<Func>(func), std::forward<Args>(args)...);
using PackagedTask = std::packaged_task<ResultType()>;
using TaskType = ThreadTask<PackagedTask>;
auto work = [func, args...] { func(args...); };
m_Queue.push(work);
}

template <typename Func, typename... Args>
auto submitTask(Func &&func, Args &&...args) -> std::future<std::invoke_result_t<Func, Args...>>
{
using ReturnType = std::invoke_result_t<Func, Args...>;
auto task = std::make_shared<std::packaged_task<ReturnType()>>(
std::bind(std::forward<Func>(func),
std::forward<Args>(args)...)
);
std::future<ReturnType> result = task->get_future();

auto work = [task] { (*task)(); };
m_Queue.push(work);

PackagedTask task{ std::move(boundTask) };
TaskFuture<ResultType> result{ task.get_future() };
m_workQueue.push(std::make_unique<TaskType>(std::move(task)));
return result;
}

Expand All @@ -205,11 +126,16 @@ class ThreadPool
*/
void worker()
{
while (!m_done)
while (!m_Done)
{
std::unique_ptr<IThreadTask> pTask{ nullptr };
if (m_workQueue.waitPop(pTask))
pTask->execute();
Proc func;
if (m_Queue.waitPop(func))
{
--m_WaitingThreads;
func();
if (++m_WaitingThreads == threadCount())
m_JoinCondition.notify_one();
}
}
}

Expand All @@ -218,15 +144,20 @@ class ThreadPool
*/
void destroy()
{
m_done = true;
m_workQueue.invalidate();
for (auto &thread : m_threads)
m_Done = true;
m_Queue.invalidate();
for (auto &thread : m_Threads)
if (thread.joinable())
thread.join();
}

private:
std::atomic_bool m_done;
ThreadSafeQueue<std::unique_ptr<IThreadTask>> m_workQueue;
std::vector<std::thread> m_threads;
using Proc = std::function<void(void)>;
using QueueType = ThreadSafeQueue<Proc>;

std::atomic_bool m_Done;
QueueType m_Queue;
std::vector<std::thread> m_Threads;
std::atomic_size_t m_WaitingThreads;
std::condition_variable m_JoinCondition;
};
Loading