diff --git a/library/src/calc_tables.cpp b/library/src/calc_tables.cpp index b0446fe8..a86b5f79 100644 --- a/library/src/calc_tables.cpp +++ b/library/src/calc_tables.cpp @@ -8,6 +8,10 @@ */ #include "calc_tables.hpp" +#include +#include +#include +#include #include #include @@ -15,13 +19,139 @@ #include #include #include -#include #include #include extern Memory memory; extern Scheduler scheduler; +extern System sysdep; + +namespace { + +struct CalcRunParam +{ + Boards* bop = nullptr; + SolvedBoards* solvedp = nullptr; + std::atomic error{RETURN_NO_FAULT}; +}; + +struct CalcThreadPool +{ + std::mutex mu; + std::vector> 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 lock(pool.mu); + const unsigned n = static_cast(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(solver_config_for_thread(k)); + } +} + +void calc_chunk_common(const int thr_id, CalcRunParam& param) +{ + SolverContext& ctx = *calc_thread_pool().slots[static_cast(thr_id)]; + + while (true) + { + const schedType st = scheduler.GetNumber(thr_id); + const int index = st.number; + if (index == -1) + break; + + 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 threads; + threads.reserve(static_cast(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 lock(calc_thread_pool().mu); + calc_thread_pool().slots.clear(); +} // Legacy overload (creates temporary context) auto calc_all_boards_n( @@ -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, @@ -116,38 +246,25 @@ 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 contexts(static_cast(nthreads)); - err = parallel_all_boards_n(n, nthreads, - [&](const int worker_id, const int bno) -> int { - return calc_single_common_internal( - contexts[static_cast(worker_id)], *bop, *solvedp, bno); - }); - } + (void)max_threads; + const int ret_run = run_calc_threads(param); END_BLOCK_TIMER; - if (err != RETURN_NO_FAULT) - return err; + if (ret_run != RETURN_NO_FAULT) + return ret_run; solvedp->no_of_boards = n; @@ -155,7 +272,8 @@ auto calc_all_boards_n( 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; } diff --git a/library/src/calc_tables.hpp b/library/src/calc_tables.hpp index 70a23592..88f4ede5 100644 --- a/library/src/calc_tables.hpp +++ b/library/src/calc_tables.hpp @@ -54,3 +54,6 @@ auto detect_calc_duplicates( const Boards& bds, std::vector& uniques, std::vector& crossrefs) -> void; + +/** Drop persistent calc worker SolverContext instances (e.g. on FreeMemory). */ +auto clear_calc_thread_contexts() -> void; diff --git a/library/src/init.cpp b/library/src/init.cpp index 1b206925..6c1f73d4 100644 --- a/library/src/init.cpp +++ b/library/src/init.cpp @@ -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. @@ -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); }