From c6214a4036f151046bde6a6995bed8e098bb79e9 Mon Sep 17 00:00:00 2001 From: Dnomd343 Date: Fri, 29 Sep 2023 15:25:09 +0800 Subject: [PATCH] update: improve all cases build interface --- src/core/all_cases/all_cases.cc | 38 +++++++++++++++++++++++++++------ src/core/all_cases/all_cases.h | 3 +++ src/core/ffi/all_cases.cc | 36 +++++++++++-------------------- src/core/ffi/klotski.h | 18 ++++++++++++++-- src/core/main.cc | 5 ++++- src/main.c | 4 ++-- 6 files changed, 68 insertions(+), 36 deletions(-) diff --git a/src/core/all_cases/all_cases.cc b/src/core/all_cases/all_cases.cc index 08a6272..9f0f48e 100644 --- a/src/core/all_cases/all_cases.cc +++ b/src/core/all_cases/all_cases.cc @@ -69,6 +69,37 @@ void AllCases::BuildCases(int head, Ranges &release) noexcept { } } +/// Execute the build process and ensure thread safety. +void AllCases::Build() noexcept { + BuildParallel([](auto &&func) { + func(); + }); +} + +/// Execute the build process in parallel without blocking. +void AllCases::BuildParallelAsync(Executor &&executor, Notifier &&callback) noexcept { + if (available_) { + return; // reduce consumption of mutex + } + building_.lock(); + if (available_) { + building_.unlock(); + return; // data is already available + } + auto counter = std::make_shared>(0); + auto all_done = std::make_shared(std::move(callback)); + for (auto head : case_heads()) { + executor([this, head, counter, all_done]() { + BuildCases(head, GetCases()[head]); + if (counter->fetch_add(1) == case_heads().size() - 1) { + available_ = true; + building_.unlock(); // release building mutex + all_done->operator()(); // trigger callback + } + }); + } +} + /// Execute the build process with parallel support and ensure thread safety. void AllCases::BuildParallel(Executor &&executor) noexcept { if (available_) { @@ -95,13 +126,6 @@ void AllCases::BuildParallel(Executor &&executor) noexcept { available_ = true; } -/// Execute the build process and ensure thread safety. -void AllCases::Build() noexcept { - BuildParallel([](auto &&func) { - func(); - }); -} - RangesUnion& AllCases::GetCases() noexcept { static RangesUnion cases; return cases; diff --git a/src/core/all_cases/all_cases.h b/src/core/all_cases/all_cases.h index c20e9e6..3e30c72 100644 --- a/src/core/all_cases/all_cases.h +++ b/src/core/all_cases/all_cases.h @@ -49,6 +49,8 @@ namespace cases { typedef uint32_t Range; typedef std::vector Ranges; typedef std::array RangesUnion; + +typedef std::function Notifier; typedef std::function&&)> Executor; constexpr auto BASIC_RANGES_NUM = 7311921; @@ -87,6 +89,7 @@ class AllCases { public: void Build() noexcept; void BuildParallel(Executor &&executor) noexcept; + void BuildParallelAsync(Executor &&executor, Notifier &&callback) noexcept; const RangesUnion& Fetch() noexcept; [[nodiscard]] bool IsAvailable() const noexcept; diff --git a/src/core/ffi/all_cases.cc b/src/core/ffi/all_cases.cc index 70e9b93..384394e 100644 --- a/src/core/ffi/all_cases.cc +++ b/src/core/ffi/all_cases.cc @@ -1,8 +1,8 @@ #include "klotski.h" #include "all_cases.h" -#include #include +#include using klotski::cases::AllCases; using klotski::cases::BasicRanges; @@ -37,7 +37,7 @@ void all_cases_build_async(executor_t executor, notifier_t callback) { }, (void*)callback); } -void all_cases_parallel_build(executor_t executor) { +void all_cases_build_parallel(executor_t executor) { typedef std::function Runner; AllCases::Instance().BuildParallel([executor](Runner &&runner) { executor([](void *fn) { @@ -47,29 +47,17 @@ void all_cases_parallel_build(executor_t executor) { }); } -void all_cases_parallel_build_async(executor_t executor, notifier_t callback) { - - typedef std::pair pp_t; - - auto pp = new pp_t; - pp->first = executor; - pp->second = callback; - - - auto lambda = [](void *arg) { - - std::cout << "enter lambda" << std::endl; - - auto *pp = (pp_t*)arg; - - all_cases_parallel_build(pp->first); - - ((notifier_t)pp->second)(); - +void all_cases_build_parallel_async(executor_t executor, notifier_t callback) { + typedef std::function Runner; + auto all_done = [callback]() { + callback(); }; - - executor(lambda, (void*)pp); - + AllCases::Instance().BuildParallelAsync([executor](Runner &&runner) { + executor([](void *fn) { + (*(Runner*)fn)(); + delete (Runner*)fn; + }, (void*)new Runner{std::move(runner)}); + }, std::move(all_done)); } int is_all_cases_available() { diff --git a/src/core/ffi/klotski.h b/src/core/ffi/klotski.h index aa3b28a..bd6ebf6 100644 --- a/src/core/ffi/klotski.h +++ b/src/core/ffi/klotski.h @@ -32,14 +32,28 @@ EXTERN void all_cases_prebuild_async(executor_t executor, notifier_t callback); /// not completed, non-0 otherwise. EXTERN int is_all_cases_prebuild_available(); +/// Perform the build of all_cases, it is blocking, and will return directly +/// if completed. EXTERN void all_cases_build(); +/// Execute the asynchronous build of all_cases, the task will be sent to the +/// executor, and the callback will be called after completion. Even if the +/// data is ready, the callback will still be triggered. EXTERN void all_cases_build_async(executor_t executor, notifier_t callback); -EXTERN void all_cases_parallel_build(executor_t executor); +/// Build all_cases in parallel, the tasks will be split and sent to the +/// executor, you can put them on different threads to work, but note that the +/// task can only be executed once, otherwise it will lead to unknown +/// consequences, the function will be blocked until all mission completed. +EXTERN void all_cases_build_parallel(executor_t executor); -EXTERN void all_cases_parallel_build_async(executor_t executor, notifier_t callback); +/// Similar to `all_cases_build_parallel`, but it is non-blocking. The callback +/// will be triggered after the build is completed. Note that the callback will +/// still be triggered even if the data is ready. +EXTERN void all_cases_build_parallel_async(executor_t executor, notifier_t callback); +/// Returns whether the all_cases is ready, 0 means not completed, non-0 means +/// the data is ready. EXTERN int is_all_cases_available(); //extern const uint32_t ALL_CASES_SIZE; diff --git a/src/core/main.cc b/src/core/main.cc index 4da17cc..0abd5ed 100644 --- a/src/core/main.cc +++ b/src/core/main.cc @@ -10,8 +10,11 @@ int main() { auto start = clock(); - AllCases::Instance().Build(); +// BasicRanges::Instance().Build(); + +// AllCases::Instance().Build(); // AllCases::Instance().BuildParallel([](auto f) {f();}); +// AllCases::Instance().BuildParallelAsync([](auto f) {f();}, []() {}); std::cerr << ((clock() - start) * 1000 / CLOCKS_PER_SEC) << "ms" << std::endl; diff --git a/src/main.c b/src/main.c index 42d64c1..7aa155a 100644 --- a/src/main.c +++ b/src/main.c @@ -63,11 +63,11 @@ int main() { // sleep(3); // printf("build begin\n"); -// all_cases_parallel_build(executor); +// all_cases_build_parallel(executor); // printf("build complete\n"); printf("build begin\n"); - all_cases_parallel_build_async(executor, callback); + all_cases_build_parallel_async(executor, callback); printf("build func exited\n"); printf("build available -> %d\n", is_all_cases_available()); printf("start sleep 3s\n");