Browse Source

perf: build process of AllCases

master
Dnomd343 1 year ago
parent
commit
b04a4c220b
  1. 67
      src/core/all_cases/all_cases.cc
  2. 23
      src/core/all_cases/all_cases.h
  3. 8
      src/core/all_cases/basic_ranges.cc
  4. 37
      src/core/main.cc
  5. 6
      src/core/utils/utility.h

67
src/core/all_cases/all_cases.cc

@ -1,7 +1,4 @@
#include <future>
#include <iostream>
#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<std::future<void>> fs;
std::vector<std::future<void>> futures;
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]);
p->set_value();
// TODO: using std::move_only_function in C++23
// -> avoid using std::shared_ptr<std::promise<void>>
auto promise = std::make_shared<std::promise<void>>();
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();
}

23
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 <array>
#include <mutex>
#include <vector>
#include <cstdint>
#include <numeric>
#include "utility.h"
namespace klotski {
namespace cases {
typedef uint32_t Range;
typedef std::vector<Range> Ranges;
typedef std::array<Ranges, 16> MultiRanges;
typedef std::array<Ranges, 16> RangesUnion;
typedef std::function<void(std::function<void()>&&)> 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

8
src/core/all_cases/basic_ranges.cc

@ -1,7 +1,5 @@
#include <list>
#include <algorithm>
#include "utility.h"
#include "all_cases.h"
namespace klotski {
@ -9,11 +7,11 @@ namespace cases {
typedef std::vector<Range>::iterator RangeIter;
typedef std::tuple<int, int, int, int> RangeType;
typedef std::array<RangeType, 204> RangeTypes;
typedef std::array<RangeType, 204> 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)

37
src/core/main.cc

@ -1,53 +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();
// 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<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);
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);

6
src/core/utils/utility.h

@ -2,6 +2,12 @@
#include <cstdint>
#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) {

Loading…
Cancel
Save