diff --git a/src/core/all_cases/internal/all_cases.cc b/src/core/all_cases/internal/all_cases.cc index b9be571..5f82087 100644 --- a/src/core/all_cases/internal/all_cases.cc +++ b/src/core/all_cases/internal/all_cases.cc @@ -65,7 +65,7 @@ void AllCases::build() { x = range_reverse(x); } for (const auto head : get_heads()) { - build_cases(ranges, reversed, get_cases()[head], head); + build_cases(ranges, reversed, get_cases().ranges(head), head); } available_ = true; KLSK_MEM_BARRIER; @@ -92,7 +92,7 @@ void AllCases::build_async(Executor &&executor, Notifier &&callback) { Worker worker {executor}; for (const auto head : get_heads()) { worker.post([head, reversed] { - build_cases(BasicRanges::instance().fetch(), *reversed, get_cases()[head], head); + build_cases(BasicRanges::instance().fetch(), *reversed, get_cases().ranges(head), head); }); } diff --git a/src/core/benchmark/ranges.cc b/src/core/benchmark/ranges.cc index 3c9cfc5..e2f90c4 100644 --- a/src/core/benchmark/ranges.cc +++ b/src/core/benchmark/ranges.cc @@ -31,8 +31,34 @@ static void RangesUnionExport(benchmark::State &state) { } } -BENCHMARK(SpawnRanges)->Unit(benchmark::kMillisecond); +static void RangesSize(benchmark::State &state) { + auto &all_cases = AllCases::instance().fetch(); + // std::cout << all_cases.size() << std::endl; + for (auto _ : state) { + volatile auto k1 = all_cases.size(); + volatile auto k2 = all_cases.size(); + volatile auto k3 = all_cases.size(); + volatile auto k4 = all_cases.size(); + volatile auto k5 = all_cases.size(); + volatile auto k6 = all_cases.size(); + volatile auto k7 = all_cases.size(); + volatile auto k8 = all_cases.size(); + + volatile auto p1 = all_cases.size(); + volatile auto p2 = all_cases.size(); + volatile auto p3 = all_cases.size(); + volatile auto p4 = all_cases.size(); + volatile auto p5 = all_cases.size(); + volatile auto p6 = all_cases.size(); + volatile auto p7 = all_cases.size(); + volatile auto p8 = all_cases.size(); + } +} + +// BENCHMARK(SpawnRanges)->Unit(benchmark::kMillisecond); // BENCHMARK(RangesUnionExport)->Unit(benchmark::kMillisecond); +BENCHMARK(RangesSize); + BENCHMARK_MAIN(); diff --git a/src/core/group/internal/group.cc b/src/core/group/internal/group.cc index d231a79..53ebe89 100644 --- a/src/core/group/internal/group.cc +++ b/src/core/group/internal/group.cc @@ -68,11 +68,11 @@ RangesUnion Group::cases() const { RangesUnion data; for (auto raw_code : codes) { auto common_code = raw_code.to_common_code().unwrap(); - data[common_code >> 32].emplace_back(static_cast(common_code)); + data.ranges(common_code >> 32).emplace_back(static_cast(common_code)); } for (int head = 0; head < 16; ++head) { - std::stable_sort(data[head].begin(), data[head].end()); + std::stable_sort(data.ranges(head).begin(), data.ranges(head).end()); } return data; } diff --git a/src/core/group/internal/group_cases.cc b/src/core/group/internal/group_cases.cc index 6499986..e937e8d 100644 --- a/src/core/group/internal/group_cases.cc +++ b/src/core/group/internal/group_cases.cc @@ -119,15 +119,15 @@ CommonCode GroupCases::fast_obtain_code(CaseInfo info) { auto case_id = info.case_id(); for (;;) { - if (case_id >= cases[head].size()) { - case_id -= cases[head].size(); + if (case_id >= cases.ranges(head).size()) { + case_id -= cases.ranges(head).size(); ++head; } else { break; } } - auto range = cases[head][case_id]; + auto range = cases.ranges(head)[case_id]; return CommonCode::unsafe_create(head << 32 | range); } @@ -173,15 +173,15 @@ CommonCode GroupCases::tiny_obtain_code(CaseInfo info) { auto case_id = info.case_id(); for (;;) { - if (case_id >= cases[head].size()) { - case_id -= cases[head].size(); + if (case_id >= cases.ranges(head).size()) { + case_id -= cases.ranges(head).size(); ++head; } else { break; } } - auto range = cases[head][case_id]; + auto range = cases.ranges(head)[case_id]; return CommonCode::unsafe_create(head << 32 | range); } diff --git a/src/core/group/internal/group_union.cc b/src/core/group/internal/group_union.cc index 75afa5c..6590352 100644 --- a/src/core/group/internal/group_union.cc +++ b/src/core/group/internal/group_union.cc @@ -7,7 +7,7 @@ using klotski::cases::RangesUnion; using klotski::cases::BASIC_RANGES_NUM; -#define RANGE_DERIVE(HEAD) ranges.derive(HEAD, cases[HEAD]) +#define RANGE_DERIVE(HEAD) ranges.derive(HEAD, cases.ranges(HEAD)) RangesUnion GroupUnion::cases() const { auto [n, n_2x1, n_1x1] = BLOCK_NUM[type_id_]; @@ -19,10 +19,10 @@ RangesUnion GroupUnion::cases() const { ranges.reverse(); RangesUnion cases; - cases[0x0].reserve(s_a); cases[0x1].reserve(s_b); cases[0x2].reserve(s_a); - cases[0x4].reserve(s_c); cases[0x5].reserve(s_d); cases[0x6].reserve(s_c); - cases[0x8].reserve(s_c); cases[0x9].reserve(s_d); cases[0xA].reserve(s_c); - cases[0xC].reserve(s_a); cases[0xD].reserve(s_b); cases[0xE].reserve(s_a); + cases.ranges(0x0).reserve(s_a); cases.ranges(0x1).reserve(s_b); cases.ranges(0x2).reserve(s_a); + cases.ranges(0x4).reserve(s_c); cases.ranges(0x5).reserve(s_d); cases.ranges(0x6).reserve(s_c); + cases.ranges(0x8).reserve(s_c); cases.ranges(0x9).reserve(s_d); cases.ranges(0xA).reserve(s_c); + cases.ranges(0xC).reserve(s_a); cases.ranges(0xD).reserve(s_b); cases.ranges(0xE).reserve(s_a); RANGE_DERIVE(0x0); RANGE_DERIVE(0x1); RANGE_DERIVE(0x2); RANGE_DERIVE(0x4); RANGE_DERIVE(0x5); RANGE_DERIVE(0x6); diff --git a/src/core/ranges/internal/ranges.cc b/src/core/ranges/internal/ranges.cc index ae1c025..85cdcd9 100644 --- a/src/core/ranges/internal/ranges.cc +++ b/src/core/ranges/internal/ranges.cc @@ -23,23 +23,44 @@ Ranges& Ranges::operator+=(const Ranges &ranges) { RangesUnion& RangesUnion::operator+=(const RangesUnion &ranges_union) { for (const auto head : heads) { - (*this)[head] += ranges_union[head]; + // (*this)[head] += ranges_union[head]; + std::array::operator[](head) += ranges_union.std::array::operator[](head); } return *this; } std::vector RangesUnion::codes() const { - size_type size = 0; - for (const auto head : heads) { - size += (*this)[head].size(); - } - std::vector codes; - codes.reserve(size); + codes.reserve(size()); for (const auto head : heads) { - for (const auto range : (*this)[head]) { + // for (const auto range : (*this)[head]) { + for (const auto range : ranges(head)) { codes.emplace_back(CommonCode::unsafe_create(head << 32 | range)); } } + // TODO: try using std::views return codes; } + +// TODO: move to `.inl` file +size_t RangesUnion::size() const { + size_type size = 0; + for (const auto head : heads) { + size += std::array::operator[](head).size(); + // size += (*this)[head].size(); + } + return size; +} + +uint32_t RangesUnion::operator[](size_type index) const { + size_t head = 0; + for (;;) { + if (index >= std::array::operator[](head).size()) { + index -= std::array::operator[](head).size(); + ++head; + } else { + break; + } + } + return std::array::operator[](head)[index]; +} diff --git a/src/core/ranges/ranges.h b/src/core/ranges/ranges.h index 5b9b6f0..bc79ccc 100644 --- a/src/core/ranges/ranges.h +++ b/src/core/ranges/ranges.h @@ -41,6 +41,18 @@ public: /// Export the RangesUnion as CommonCode list. [[nodiscard]] std::vector codes() const; + + [[nodiscard]] const Ranges& ranges(const size_t head) const { + return std::array::operator[](head); + } + + Ranges& ranges(const size_t head) { + return std::array::operator[](head); + } + + [[nodiscard]] size_t size() const; + + [[nodiscard]] uint32_t operator[](size_type) const; }; } // namespace klotski::cases diff --git a/src/core/short_code/internal/convert.cc b/src/core/short_code/internal/convert.cc index 0b87697..7a43ff2 100644 --- a/src/core/short_code/internal/convert.cc +++ b/src/core/short_code/internal/convert.cc @@ -52,7 +52,7 @@ static uint32_t check_range(uint32_t head, uint32_t range) noexcept { uint32_t ShortCode::fast_encode(uint64_t common_code) { auto head = common_code >> 32; - auto &ranges = (*cases_)[head]; // match available ranges + const auto &ranges = (*cases_).ranges(head); // match available ranges // TODO: try to narrow the scope by prefix auto target = std::lower_bound(ranges.begin(), ranges.end(), (uint32_t)common_code); return ALL_CASES_OFFSET[head] + (target - ranges.begin()); @@ -62,7 +62,7 @@ uint64_t ShortCode::fast_decode(uint32_t short_code) { auto offset = std::upper_bound(ALL_CASES_OFFSET.begin(), ALL_CASES_OFFSET.end(), short_code) - 1; uint64_t head = offset - ALL_CASES_OFFSET.begin(); // return (head << 32) | AllCases::instance().fetch()[head][short_code - *offset]; - return (head << 32) | (*cases_)[head][short_code - *offset]; + return (head << 32) | (*cases_).ranges(head)[short_code - *offset]; } uint32_t ShortCode::tiny_encode(uint64_t common_code) { diff --git a/src/core_test/cases/all_cases.cc b/src/core_test/cases/all_cases.cc index 42f4aec..4758b56 100644 --- a/src/core_test/cases/all_cases.cc +++ b/src/core_test/cases/all_cases.cc @@ -51,15 +51,15 @@ protected: static void Verify() { const auto &all_cases = AllCases::instance().fetch(); for (int head = 0; head < 16; ++head) { - EXPECT_EQ(all_cases[head].size(), ALL_CASES_NUM[head]); // verify all cases size - EXPECT_EQ(helper::xxh3(all_cases[head]), ALL_CASES_XXH3[head]); // verify all cases checksum + EXPECT_EQ(all_cases.ranges(head).size(), ALL_CASES_NUM[head]); // verify all cases size + EXPECT_EQ(helper::xxh3(all_cases.ranges(head)), ALL_CASES_XXH3[head]); // verify all cases checksum } } }; TEST_FF(AllCases, content) { for (auto head : Heads) { - auto &cases = AllCases::instance().fetch()[head]; + auto &cases = AllCases::instance().fetch().ranges(head); EXPECT_SORTED_AND_UNIQUE(cases); EXPECT_EQ(cases.size(), ALL_CASES_NUM[head]); // size verify EXPECT_EQ(helper::xxh3(cases), ALL_CASES_XXH3[head]); // checksum verify diff --git a/src/core_test/cases/ranges.cc b/src/core_test/cases/ranges.cc index 5b4e38d..542eda9 100644 --- a/src/core_test/cases/ranges.cc +++ b/src/core_test/cases/ranges.cc @@ -16,7 +16,7 @@ TEST(Ranges, check) { for (const auto head : Heads) { for (auto range : BasicRanges::instance().fetch()) { if (Ranges::check(head, range_reverse(range)) == 0) { - all_cases[head].emplace_back(range); // found valid cases + all_cases.ranges(head).emplace_back(range); // found valid cases } } } @@ -45,14 +45,14 @@ TEST(Ranges, derive) { RangesUnion cases; for (const auto head : Heads) { - ranges.derive(head, cases[head]); - EXPECT_SORTED_AND_UNIQUE(cases[head]); // sorted and unique - EXPECT_COMMON_CODES(head, cases[head]); // verify common codes + ranges.derive(head, cases.ranges(head)); + EXPECT_SORTED_AND_UNIQUE(cases.ranges(head)); // sorted and unique + EXPECT_COMMON_CODES(head, cases.ranges(head)); // verify common codes } ranges.reverse(); for (const auto head : Heads) { - EXPECT_SUBSET(ranges, cases[head]); // derive ranges is subset + EXPECT_SUBSET(ranges, cases.ranges(head)); // derive ranges is subset } } } @@ -83,7 +83,7 @@ TEST(Ranges, combine) { RangesUnion all_cases; all_ranges.reserve(BASIC_RANGES_NUM_); // pre reserve for (const auto head : Heads) { - all_cases[head].reserve(ALL_CASES_NUM[head]); // pre reserve + all_cases.ranges(head).reserve(ALL_CASES_NUM[head]); // pre reserve } for (auto [n, n_2x1, n_1x1] : BLOCK_NUM) { @@ -92,21 +92,21 @@ TEST(Ranges, combine) { all_ranges += ranges; ranges.reverse(); // reverse ranges for derive for (const auto head : Heads) { - ranges.derive(head, all_cases[head]); // derive from sub ranges + ranges.derive(head, all_cases.ranges(head)); // derive from sub ranges } } std::ranges::stable_sort(all_ranges.begin(), all_ranges.end()); for (const auto head : Heads) { - std::ranges::stable_sort(all_cases[head].begin(), all_cases[head].end()); + std::ranges::stable_sort(all_cases.ranges(head).begin(), all_cases.ranges(head).end()); } EXPECT_EQ(all_ranges, BasicRanges::instance().fetch()); // verify all ranges EXPECT_EQ(all_cases, AllCases::instance().fetch()); // verify all cases all_ranges.reverse(); // reverse ranges for derive for (const auto head : Heads) { - all_cases[head].clear(); - all_ranges.derive(head, all_cases[head]); // derive from all ranges + all_cases.ranges(head).clear(); + all_ranges.derive(head, all_cases.ranges(head)); // derive from all ranges } EXPECT_EQ(all_cases, AllCases::instance().fetch()); // verify content } diff --git a/src/core_test/cases/ranges_union.cc b/src/core_test/cases/ranges_union.cc index 4c7ff25..dc9e0c5 100644 --- a/src/core_test/cases/ranges_union.cc +++ b/src/core_test/cases/ranges_union.cc @@ -27,14 +27,14 @@ TEST(RangesUnion, append) { RangesUnion ru; for (const auto head : Heads) { - r.derive(head, ru[head]); + r.derive(head, ru.ranges(head)); } auto &tmp = cases += ru; EXPECT_EQ(tmp, cases); // reference of cases } for (const auto head : Heads) { - std::stable_sort(cases[head].begin(), cases[head].end()); + std::stable_sort(cases.ranges(head).begin(), cases.ranges(head).end()); } EXPECT_EQ(cases, AllCases::instance().fetch()); } diff --git a/src/core_test/mover/mover.cc b/src/core_test/mover/mover.cc index b9f0622..63998c6 100644 --- a/src/core_test/mover/mover.cc +++ b/src/core_test/mover/mover.cc @@ -38,7 +38,7 @@ TEST(Core, core) { // codes.reserve(klotski::cases::ALL_CASES_NUM_); for (uint64_t head = 0; head < 16; ++head) { - for (const auto range : AllCases::instance().fetch()[head]) { + for (const auto range : AllCases::instance().fetch().ranges(head)) { auto common_code = CommonCode::unsafe_create(head << 32 | range); auto raw_code = common_code.to_raw_code().unwrap(); @@ -73,7 +73,7 @@ TEST(Core, mask) { raw_codes.reserve(klotski::cases::ALL_CASES_NUM_); for (uint64_t head = 0; head < 16; ++head) { - for (const auto range : AllCases::instance().fetch()[head]) { + for (const auto range : AllCases::instance().fetch().ranges(head)) { auto common_code = CommonCode::unsafe_create(head << 32 | range); auto raw_code = common_code.to_raw_code().unwrap(); raw_codes.emplace_back(raw_code); @@ -173,7 +173,7 @@ TEST(Core, next_cases) { std::vector raw_codes; for (uint64_t head = 0; head < 16; ++head) { - for (const auto range : AllCases::instance().fetch()[head]) { + for (const auto range : AllCases::instance().fetch().ranges(head)) { auto common_code = CommonCode::unsafe_create(head << 32 | range); auto raw_code = common_code.to_raw_code();