Browse Source

feat: multi thread executor support

master
Dnomd343 1 year ago
parent
commit
f1422bf28e
  1. 73
      src/core/all_cases/all_cases.cc
  2. 23
      src/core/all_cases/all_cases.h
  3. 15
      src/core/all_cases/basic_ranges.cc
  4. 39
      src/core/main.cc

73
src/core/all_cases/all_cases.cc

@ -1,3 +1,6 @@
#include <future>
#include <iostream>
#include "utility.h"
#include "all_cases.h"
@ -50,7 +53,7 @@ static int check_range(int head, uint32_t range) noexcept {
}
/// Build all valid ranges of the specified head.
void AllCases::BuildCases(int head, Ranges &basic_ranges, Ranges &release) noexcept {
void AllCases::BuildCases(int head, const Ranges &basic_ranges, Ranges &release) noexcept {
release.reserve(ALL_CASES_NUM[head]);
for (uint32_t index = 0; index < basic_ranges.size(); ++index) {
auto offset = check_range(head, basic_ranges[index]);
@ -69,27 +72,57 @@ void AllCases::BuildCases(int head, Ranges &basic_ranges, Ranges &release) noexc
}
void AllCases::Build() noexcept {
if (available_) { // data already available
return;
if (available_) {
return; // reduce consumption of mutex
}
if (building_.try_lock()) { // mutex lock success
if (available_) {
building_.unlock();
return;
}
auto basic_ranges = BasicRanges::Instance().Fetch();
for (auto head : case_heads()) {
std::lock_guard<std::mutex> guard(building_);
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<std::future<void>> fs;
for (auto head : case_heads()) {
auto p = std::make_shared<std::promise<void>>();
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]);
}
available_ = true;
} else {
building_.lock(); // blocking waiting
p->set_value();
});
}
// printf("all task running\n");
for (auto &x : fs) {
x.get();
}
building_.unlock(); // release mutex
// printf("all task complete\n");
}
AllRanges& AllCases::GetCases() noexcept {
static std::array<Ranges, 16> cases;
MultiRanges& AllCases::GetCases() noexcept {
static MultiRanges cases;
return cases;
}
@ -98,10 +131,14 @@ AllCases& AllCases::Instance() noexcept {
return instance;
}
AllRanges& AllCases::Fetch() noexcept {
const MultiRanges& AllCases::Fetch() noexcept {
this->Build();
return GetCases();
}
bool AllCases::IsAvailable() const noexcept {
return available_;
}
} // namespace cases
} // namespace klotski

23
src/core/all_cases/all_cases.h

@ -45,7 +45,9 @@ namespace cases {
typedef uint32_t Range;
typedef std::vector<Range> Ranges;
typedef std::array<Ranges, 16> AllRanges;
typedef std::array<Ranges, 16> MultiRanges;
typedef std::function<void(std::function<void()>&&)> Executor;
constexpr auto BASIC_RANGES_NUM = 7311921;
@ -85,11 +87,15 @@ private:
class AllCases {
public:
// void Build();
void Build() noexcept;
AllRanges& Fetch() noexcept;
bool IsAvailable() const noexcept;
const MultiRanges& Fetch() noexcept;
void BuildParallel(Executor &&executor) noexcept;
AllCases(AllCases&&) = delete;
AllCases(const AllCases&) = delete;
AllCases& operator=(AllCases&&) = delete;
AllCases& operator=(const AllCases&) = delete;
static AllCases& Instance() noexcept;
@ -97,11 +103,10 @@ private:
std::mutex building_;
bool available_ = false;
static AllRanges& GetCases() noexcept;
static void BuildCases(int head, Ranges &basic_ranges, Ranges &release) noexcept;
AllCases() = default;
static MultiRanges& GetCases() noexcept;
static void BuildCases(int head, const Ranges &basic_ranges, Ranges &release) noexcept;
};
void demo();
} // namespace cases
} // namespace klotski

15
src/core/all_cases/basic_ranges.cc

@ -89,17 +89,12 @@ void BasicRanges::Build() noexcept {
if (available_) {
return; // reduce consumption of mutex
}
if (building_.try_lock()) { // mutex lock success
if (available_) {
building_.unlock();
return;
}
BuildRanges(GetRanges());
available_ = true;
} else {
building_.lock(); // blocking waiting
std::lock_guard<std::mutex> guard(building_);
if (available_) {
return; // data is already available
}
building_.unlock(); // release mutex
BuildRanges(GetRanges());
available_ = true;
}
Ranges& BasicRanges::GetRanges() noexcept {

39
src/core/main.cc

@ -1,12 +1,20 @@
#include <iostream>
#include <thread>
#include <future>
#include "all_cases/all_cases.h"
using klotski::cases::AllCases;
using klotski::cases::BasicRanges;
void wkk(std::function<void()> &&f) {
f();
}
int main() {
std::vector<std::thread> threads;
BasicRanges::Instance().Build();
auto start = clock();
@ -18,14 +26,33 @@ int main() {
// printf("%08X\n", x);
// }
AllCases::Instance().Build();
for (uint64_t head = 0; head < 15; ++head) {
for (auto range : AllCases::Instance().Fetch()[head]) {
printf("%09llX\n", head << 32 | range);
}
}
// BasicRanges::Instance().Build();
auto exec = [&threads](std::function<void()> &&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);
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);
// }
// }
return 0;
}

Loading…
Cancel
Save