diff --git a/src/core/all_cases/all_cases.cc b/src/core/all_cases/all_cases.cc index 9129871..08a6272 100644 --- a/src/core/all_cases/all_cases.cc +++ b/src/core/all_cases/all_cases.cc @@ -1,7 +1,4 @@ #include -#include - -#include "utility.h" #include "all_cases.h" namespace klotski { @@ -53,8 +50,9 @@ static int check_range(int head, uint32_t range) noexcept { } /// Build all valid ranges of the specified head. -void AllCases::BuildCases(int head, const Ranges &basic_ranges, Ranges &release) noexcept { +void AllCases::BuildCases(int head, Ranges &release) noexcept { release.reserve(ALL_CASES_NUM[head]); + auto &basic_ranges = BasicRanges::Instance().Fetch(); for (uint32_t index = 0; index < basic_ranges.size(); ++index) { auto offset = check_range(head, basic_ranges[index]); if (offset) { // invalid case @@ -71,7 +69,8 @@ void AllCases::BuildCases(int head, const Ranges &basic_ranges, Ranges &release) } } -void AllCases::Build() noexcept { +/// Execute the build process with parallel support and ensure thread safety. +void AllCases::BuildParallel(Executor &&executor) noexcept { if (available_) { return; // reduce consumption of mutex } @@ -79,50 +78,32 @@ void AllCases::Build() noexcept { if (available_) { return; // data is already available } - auto basic_ranges = BasicRanges::Instance().Fetch(); - for (auto head : case_heads()) { - BuildCases(head, basic_ranges, GetCases()[head]); - } - available_ = true; -} - -void AllCases::BuildParallel(Executor &&executor) noexcept { - - // TODO: mutex protect - - auto basic_ranges = BasicRanges::Instance().Fetch(); - - std::vector> fs; - + std::vector> futures; for (auto head : case_heads()) { - - auto p = std::make_shared>(); - fs.emplace_back(p->get_future()); - - executor([head, &basic_ranges, p = std::move(p)]() { - - printf("thread %d -> head = %d\n", std::this_thread::get_id(), head); -// std::cout << "thread " << std::thread::id() << " -> head = " << head << std::endl; - - BuildCases(head, basic_ranges, GetCases()[head]); - - p->set_value(); - + // TODO: using std::move_only_function in C++23 + // -> avoid using std::shared_ptr> + auto promise = std::make_shared>(); + futures.emplace_back(promise->get_future()); + executor([head, promise = std::move(promise)]() { + BuildCases(head, GetCases()[head]); + promise->set_value(); // subtask completed notification }); } - -// printf("all task running\n"); - - for (auto &x : fs) { - x.get(); + for (auto &x : futures) { + x.get(); // wait until all subtasks completed } + available_ = true; +} -// printf("all task complete\n"); - +/// Execute the build process and ensure thread safety. +void AllCases::Build() noexcept { + BuildParallel([](auto &&func) { + func(); + }); } -MultiRanges& AllCases::GetCases() noexcept { - static MultiRanges cases; +RangesUnion& AllCases::GetCases() noexcept { + static RangesUnion cases; return cases; } @@ -131,7 +112,7 @@ AllCases& AllCases::Instance() noexcept { return instance; } -const MultiRanges& AllCases::Fetch() noexcept { +const RangesUnion& AllCases::Fetch() noexcept { this->Build(); return GetCases(); } diff --git a/src/core/all_cases/all_cases.h b/src/core/all_cases/all_cases.h index d7829bb..f36062a 100644 --- a/src/core/all_cases/all_cases.h +++ b/src/core/all_cases/all_cases.h @@ -35,18 +35,19 @@ /// not consume too much time, but it can almost double the speed of the case /// checking subsequent. +#include #include #include #include #include +#include "utility.h" namespace klotski { namespace cases { typedef uint32_t Range; typedef std::vector Ranges; -typedef std::array MultiRanges; - +typedef std::array RangesUnion; typedef std::function&&)> Executor; constexpr auto BASIC_RANGES_NUM = 7311921; @@ -68,11 +69,7 @@ public: const Ranges& Fetch() noexcept; bool IsAvailable() const noexcept; - BasicRanges(BasicRanges&&) = delete; - BasicRanges(const BasicRanges&) = delete; - BasicRanges& operator=(BasicRanges&&) = delete; - BasicRanges& operator=(const BasicRanges&) = delete; - + DISALLOW_COPY_AND_ASSIGN(BasicRanges); static BasicRanges& Instance() noexcept; private: @@ -89,14 +86,10 @@ class AllCases { public: void Build() noexcept; bool IsAvailable() const noexcept; - const MultiRanges& Fetch() noexcept; + const RangesUnion& Fetch() noexcept; void BuildParallel(Executor &&executor) noexcept; - AllCases(AllCases&&) = delete; - AllCases(const AllCases&) = delete; - AllCases& operator=(AllCases&&) = delete; - AllCases& operator=(const AllCases&) = delete; - + DISALLOW_COPY_AND_ASSIGN(AllCases); static AllCases& Instance() noexcept; private: @@ -104,8 +97,8 @@ private: bool available_ = false; AllCases() = default; - static MultiRanges& GetCases() noexcept; - static void BuildCases(int head, const Ranges &basic_ranges, Ranges &release) noexcept; + static RangesUnion& GetCases() noexcept; + static void BuildCases(int head, Ranges &release) noexcept; }; } // namespace cases diff --git a/src/core/all_cases/basic_ranges.cc b/src/core/all_cases/basic_ranges.cc index 6791799..0247e9a 100644 --- a/src/core/all_cases/basic_ranges.cc +++ b/src/core/all_cases/basic_ranges.cc @@ -1,7 +1,5 @@ #include #include - -#include "utility.h" #include "all_cases.h" namespace klotski { @@ -9,11 +7,11 @@ namespace cases { typedef std::vector::iterator RangeIter; typedef std::tuple RangeType; -typedef std::array RangeTypes; +typedef std::array RangeTypeUnion; /// Calculate all possible basic-ranges permutations. -consteval static RangeTypes range_types() { - RangeTypes data; +consteval static RangeTypeUnion range_types() { + RangeTypeUnion data; for (int i = 0, n = 0; n <= 7; ++n) // 1x2 and 2x1 -> 0 ~ 7 for (int n_2x1 = 0; n_2x1 <= n; ++n_2x1) // 2x1 -> 0 ~ n for (int n_1x1 = 0; n_1x1 <= (14 - n * 2); ++n_1x1) // 1x1 -> 0 ~ (14 - 2n) diff --git a/src/core/main.cc b/src/core/main.cc index 92756a4..4da17cc 100644 --- a/src/core/main.cc +++ b/src/core/main.cc @@ -1,53 +1,20 @@ #include -#include -#include - #include "all_cases/all_cases.h" using klotski::cases::AllCases; using klotski::cases::BasicRanges; -void wkk(std::function &&f) { - f(); -} - int main() { - std::vector threads; - BasicRanges::Instance().Build(); auto start = clock(); -// std::cout << BasicRanges::Instance().IsAvailable() << std::endl; -// BasicRanges::Instance().Build(); -// std::cout << BasicRanges::Instance().IsAvailable() << std::endl; -// for (auto x : BasicRanges::Instance().Fetch()) { -// printf("%08X\n", x); -// } - -// BasicRanges::Instance().Build(); - - auto exec = [&threads](std::function &&f) { -// std::cout << "start exec" << std::endl; - -// f(); - auto kk = std::thread(std::move(f)); -// threads.emplace_back(std::move(kk)); - kk.detach(); - -// std::cout << "thread created" << std::endl; - }; - -// AllCases::Instance().Build(); - AllCases::Instance().BuildParallel(exec); + AllCases::Instance().Build(); +// AllCases::Instance().BuildParallel([](auto f) {f();}); std::cerr << ((clock() - start) * 1000 / CLOCKS_PER_SEC) << "ms" << std::endl; -// for (auto &x : threads) { -// x.join(); -// } - // for (uint64_t head = 0; head < 15; ++head) { // for (auto range : AllCases::Instance().Fetch()[head]) { // printf("%09llX\n", head << 32 | range); diff --git a/src/core/utils/utility.h b/src/core/utils/utility.h index 0394fe7..e3fa8c6 100644 --- a/src/core/utils/utility.h +++ b/src/core/utils/utility.h @@ -2,6 +2,12 @@ #include +#define DISALLOW_COPY_AND_ASSIGN(T) \ + T(T&&) = delete; \ + T(const T&) = delete; \ + T& operator=(T&&) = delete; \ + T& operator=(const T&) = delete; + namespace klotski { inline int low_zero_num(uint32_t bin) {