Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
172 changes: 145 additions & 27 deletions library/src/calc_tables.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,150 @@
*/

#include "calc_tables.hpp"
#include <atomic>
#include <memory>
#include <mutex>
#include <thread>
#include <vector>

#include <pbn.hpp>
#include <solve_board.hpp>
#include <api/solve_board.hpp>
#include <solver_if.hpp>
#include <system/memory.hpp>
#include <system/parallel_boards.hpp>
#include <system/scheduler.hpp>
#include <system/system.hpp>


extern Memory memory;
extern Scheduler scheduler;
extern System sysdep;

namespace {

struct CalcRunParam
{
Boards* bop = nullptr;
SolvedBoards* solvedp = nullptr;
std::atomic<int> error{RETURN_NO_FAULT};
};

struct CalcThreadPool
{
std::mutex mu;
std::vector<std::unique_ptr<SolverContext>> slots;
};

CalcThreadPool& calc_thread_pool()
{
static CalcThreadPool pool;
return pool;
}

auto solver_config_for_thread(const unsigned thr_id) -> SolverConfig
{
SolverConfig cfg;
if (thr_id < memory.NumThreads() && memory.ThreadSize(thr_id) == "S")
cfg.tt_kind_ = TTKind::Small;
return cfg;
}

void ensure_calc_contexts(const int num_threads)
{
if (num_threads <= 0)
return;

auto& pool = calc_thread_pool();
std::lock_guard<std::mutex> lock(pool.mu);
const unsigned n = static_cast<unsigned>(num_threads);
if (pool.slots.size() < n)
pool.slots.resize(n);
for (unsigned k = 0; k < n; ++k)
{
if (!pool.slots[k])
pool.slots[k] = std::make_unique<SolverContext>(solver_config_for_thread(k));
}
}

void calc_chunk_common(const int thr_id, CalcRunParam& param)
{
SolverContext& ctx = *calc_thread_pool().slots[static_cast<unsigned>(thr_id)];

while (true)
{
const schedType st = scheduler.GetNumber(thr_id);
const int index = st.number;
if (index == -1)
break;
Comment on lines +80 to +85

if (st.repeatOf != -1)
{
START_THREAD_TIMER(thr_id);
for (int k = 0; k < DDS_HANDS; k++)
{
param.bop->deals[index].first = k;
param.solvedp->solved_board[index].score[k] =
param.solvedp->solved_board[st.repeatOf].score[k];
}
END_THREAD_TIMER(thr_id);
continue;
}

START_THREAD_TIMER(thr_id);
const int res = calc_single_common_internal(
ctx, *param.bop, *param.solvedp, index);
END_THREAD_TIMER(thr_id);

if (res != RETURN_NO_FAULT)
{
int expected = RETURN_NO_FAULT;
param.error.compare_exchange_strong(
expected, res, std::memory_order_relaxed);
break;
}
}
}

auto run_calc_threads(CalcRunParam& param) -> int
{
const int num_threads = sysdep.get_num_threads();
ensure_calc_contexts(num_threads);
if (num_threads <= 1)
{
calc_chunk_common(0, param);
return RETURN_NO_FAULT;
}

std::vector<std::thread> threads;
threads.reserve(static_cast<unsigned>(num_threads));
try
{
for (int k = 0; k < num_threads; ++k)
threads.emplace_back(calc_chunk_common, k, std::ref(param));
}
catch (...)
{
for (auto& th : threads)
{
if (th.joinable())
th.join();
}
throw;
}

for (auto& th : threads)
th.join();

return RETURN_NO_FAULT;
}

} // namespace

auto clear_calc_thread_contexts() -> void
{
std::lock_guard<std::mutex> lock(calc_thread_pool().mu);
calc_thread_pool().slots.clear();
}

// Legacy overload (creates temporary context)
auto calc_all_boards_n(
Expand Down Expand Up @@ -106,7 +236,7 @@ auto calc_all_boards_n(
return RETURN_NO_FAULT;
}

// Legacy overload: parallel across boards, one SolverContext per worker.
// Legacy overload: parallel across boards via scheduler + persistent contexts.
auto calc_all_boards_n(
Boards * bop,
SolvedBoards * solvedp,
Expand All @@ -116,46 +246,34 @@ auto calc_all_boards_n(
if (n > MAXNOOFBOARDS)
return RETURN_TOO_MANY_BOARDS;

CalcRunParam param;
param.bop = bop;
param.solvedp = solvedp;
param.error.store(RETURN_NO_FAULT, std::memory_order_relaxed);

scheduler.RegisterRun(RunMode::DDS_RUN_CALC, *bop);

for (int k = 0; k < MAXNOOFBOARDS; k++)
solvedp->solved_board[k].cards = 0;

START_BLOCK_TIMER;

const int nthreads = resolve_worker_count(max_threads, n);

int err = RETURN_NO_FAULT;
if (nthreads <= 1)
{
SolverContext ctx;
for (int bno = 0; bno < n; ++bno)
{
err = calc_single_common_internal(ctx, *bop, *solvedp, bno);
if (err != RETURN_NO_FAULT)
break;
}
}
else
{
std::vector<SolverContext> contexts(static_cast<unsigned>(nthreads));
err = parallel_all_boards_n(n, nthreads,
[&](const int worker_id, const int bno) -> int {
return calc_single_common_internal(
contexts[static_cast<unsigned>(worker_id)], *bop, *solvedp, bno);
});
}
(void)max_threads;
const int ret_run = run_calc_threads(param);

Comment on lines 259 to 263
END_BLOCK_TIMER;

if (err != RETURN_NO_FAULT)
return err;
if (ret_run != RETURN_NO_FAULT)
return ret_run;

solvedp->no_of_boards = n;

#ifdef DDS_SCHEDULER
scheduler.PrintTiming();
#endif

return RETURN_NO_FAULT;
const int err = param.error.load(std::memory_order_relaxed);
return err != RETURN_NO_FAULT ? err : RETURN_NO_FAULT;
}


Expand Down
3 changes: 3 additions & 0 deletions library/src/calc_tables.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,6 @@ auto detect_calc_duplicates(
const Boards& bds,
std::vector<int>& uniques,
std::vector<int>& crossrefs) -> void;

/** Drop persistent calc worker SolverContext instances (e.g. on FreeMemory). */
auto clear_calc_thread_contexts() -> void;
20 changes: 9 additions & 11 deletions library/src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,17 +89,14 @@ void STDCALL SetResources(
int memMaxMB = min(memMaxGivenMB, memMaxFreeMB);
memMaxMB = min(memMaxMB, memMax32bMB);

// Internal parallel execution has been removed.
// Legacy API calls execute sequentially, so use a single internal thread.
if (maxThreadsIn > 1) {
std::fprintf(
stderr,
"DDS warning: SetResources maxThreadsIn=%d requested, but internal batch threading is disabled; using 1 thread.\n",
maxThreadsIn);
}
(void) maxThreadsIn;
(void) ncores;
const int thrMax = 1;
// Limit worker count by memory budget and hardware.
int thrMax;
if (maxThreadsIn <= 0)
thrMax = ncores;
else
thrMax = std::min(maxThreadsIn, ncores);
if (thrMax < 1)
thrMax = 1;

// For simplicity we won't vary the amount of memory per thread
// in the small and large versions.
Expand Down Expand Up @@ -399,6 +396,7 @@ void STDCALL GetDDSInfo(DDSInfo * info)
*/
void STDCALL FreeMemory()
{
clear_calc_thread_contexts();
for (unsigned thrId = 0; thrId < memory.NumThreads(); thrId++)
memory.ReturnThread(thrId);
}
Expand Down
Loading