Browse Source

test: perf concurrent testing helper

master
Dnomd343 3 months ago
parent
commit
e754aa2e32
  1. 234
      src/core_test/cases/all_cases.cc
  2. 45
      src/core_test/utility/concurrent.h

234
src/core_test/cases/all_cases.cc

@ -3,8 +3,8 @@
#include "hash.h" #include "hash.h"
#include "exposer.h" #include "exposer.h"
#include "all_cases.h" #include "all_cases.h"
#include "concurrent.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "BS_thread_pool.hpp"
using klotski::cases::AllCases; using klotski::cases::AllCases;
using klotski::cases::BasicRanges; using klotski::cases::BasicRanges;
@ -13,8 +13,6 @@ using klotski::cases::ALL_CASES_NUM;
using klotski::cases::ALL_CASES_NUM_; using klotski::cases::ALL_CASES_NUM_;
using klotski::cases::BASIC_RANGES_NUM; using klotski::cases::BASIC_RANGES_NUM;
static constexpr int TEST_THREAD_NUM = 256;
static constexpr uint64_t BASIC_RANGES_XXH3 = 0x82b040060044e336; static constexpr uint64_t BASIC_RANGES_XXH3 = 0x82b040060044e336;
static constexpr std::array<uint64_t, 16> ALL_CASES_XXH3 = { static constexpr std::array<uint64_t, 16> ALL_CASES_XXH3 = {
@ -28,164 +26,182 @@ static constexpr std::array<uint64_t, 16> ALL_CASES_XXH3 = {
PRIVATE_ACCESS(AllCases, available_, bool) PRIVATE_ACCESS(AllCases, available_, bool)
PRIVATE_ACCESS(BasicRanges, available_, bool) PRIVATE_ACCESS(BasicRanges, available_, bool)
/// Reset basic ranges build state, note it is thread-unsafe. class BR_Test final {
void basic_ranges_reset() { public:
access_BasicRanges_available_(BasicRanges::instance()) = false; [[nodiscard]] static bool available() {
} return BasicRanges::instance().is_available();
}
/// Reset all cases build state, note it is thread-unsafe. // TODO: expect member function
void all_cases_reset() {
access_AllCases_available_(AllCases::instance()) = false;
}
/// Verify that whether basic ranges data is correct. /// Reset basic ranges build state, note it is thread-unsafe.
void basic_ranges_verify() { static void reset() {
auto &basic_ranges = BasicRanges::instance().fetch(); access_BasicRanges_available_(BasicRanges::instance()) = false;
EXPECT_EQ(basic_ranges.size(), BASIC_RANGES_NUM); // verify basic ranges size }
EXPECT_EQ(hash::xxh3(basic_ranges), BASIC_RANGES_XXH3); // verify basic ranges checksum
}
/// Verify that whether all cases data is correct. /// Verify that whether basic ranges data is correct.
void all_cases_verify() { static void verify() {
const auto &all_cases = AllCases::instance().fetch(); const auto &basic_ranges = BasicRanges::instance().fetch();
for (int head = 0; head < 16; ++head) { EXPECT_EQ(basic_ranges.size(), BASIC_RANGES_NUM); // verify basic ranges size
EXPECT_EQ(all_cases[head].size(), ALL_CASES_NUM[head]); // verify all cases size EXPECT_EQ(hash::xxh3(basic_ranges), BASIC_RANGES_XXH3); // verify basic ranges checksum
} }
};
auto all_cases_num = 0; class AC_Test final {
std::for_each(all_cases.begin(), all_cases.end(), [&all_cases_num](auto &ranges) { public:
all_cases_num += ranges.size(); [[nodiscard]] static bool available() {
}); return AllCases::instance().is_available();
EXPECT_EQ(all_cases_num, ALL_CASES_NUM_); // verify all cases global size }
for (uint64_t head = 0; head < 16; ++head) { /// Reset all cases build state, note it is thread-unsafe.
EXPECT_EQ(hash::xxh3(all_cases[head]), ALL_CASES_XXH3[head]); // verify all cases checksum static void reset() {
access_AllCases_available_(AllCases::instance()) = false;
} }
}
std::unique_ptr<BS::thread_pool> race_test(int parallel, const std::function<void()> &item) { /// Verify that whether all cases data is correct.
auto pool = std::make_unique<BS::thread_pool>(parallel); static void verify() {
pool->detach_sequence(0, parallel, [&item](const int) { const auto &all_cases = AllCases::instance().fetch();
item(); for (int head = 0; head < 16; ++head) {
}); EXPECT_EQ(all_cases[head].size(), ALL_CASES_NUM[head]); // verify all cases size
return pool; }
}
auto all_cases_num = 0;
std::for_each(all_cases.begin(), all_cases.end(), [&all_cases_num](auto &ranges) {
all_cases_num += ranges.size();
});
EXPECT_EQ(all_cases_num, ALL_CASES_NUM_); // verify all cases global size
for (uint64_t head = 0; head < 16; ++head) {
EXPECT_EQ(hash::xxh3(all_cases[head]), ALL_CASES_XXH3[head]); // verify all cases checksum
}
}
};
TEST(AllCases, basic_ranges) { TEST(AllCases, basic_ranges) {
basic_ranges_reset(); BR_Test::reset();
EXPECT_FALSE(BasicRanges::instance().is_available()); EXPECT_FALSE(BR_Test::available());
BasicRanges::instance().build(); BasicRanges::instance().build();
EXPECT_TRUE(BasicRanges::instance().is_available()); EXPECT_TRUE(BR_Test::available());
BasicRanges::instance().build(); BasicRanges::instance().build();
EXPECT_TRUE(BasicRanges::instance().is_available()); EXPECT_TRUE(BR_Test::available());
basic_ranges_verify(); BR_Test::verify();
} }
TEST(AllCases, basic_ranges_mutex) { TEST(AllCases, basic_ranges_race) {
basic_ranges_reset(); BR_Test::reset();
const auto handle = race_test(TEST_THREAD_NUM, []() { EXPECT_FALSE(BR_Test::available());
auto racer = co::Racer([] {
BasicRanges::instance().build(); BasicRanges::instance().build();
}); });
EXPECT_FALSE(BasicRanges::instance().is_available()); EXPECT_FALSE(BR_Test::available());
handle->wait();
EXPECT_TRUE(BasicRanges::instance().is_available()); racer.Join();
basic_ranges_verify(); EXPECT_TRUE(BR_Test::available());
BR_Test::verify();
} }
TEST(AllCases, all_cases) { TEST(AllCases, all_cases) {
all_cases_reset(); AC_Test::reset();
EXPECT_FALSE(AllCases::instance().is_available()); EXPECT_FALSE(AC_Test::available());
AllCases::instance().build(); AllCases::instance().build();
EXPECT_TRUE(AllCases::instance().is_available()); EXPECT_TRUE(AC_Test::available());
AllCases::instance().build(); AllCases::instance().build();
EXPECT_TRUE(AllCases::instance().is_available()); EXPECT_TRUE(AC_Test::available());
all_cases_verify(); AC_Test::verify();
} }
TEST(AllCases, all_cases_mutex) { TEST(AllCases, all_cases_race) {
all_cases_reset(); AC_Test::reset();
const auto handle = race_test(TEST_THREAD_NUM, []() { EXPECT_FALSE(AC_Test::available());
auto racer = co::Racer([] {
AllCases::instance().build(); AllCases::instance().build();
}); });
EXPECT_FALSE(AllCases::instance().is_available()); EXPECT_FALSE(AC_Test::available());
handle->wait();
EXPECT_TRUE(AllCases::instance().is_available()); racer.Join();
all_cases_verify(); EXPECT_TRUE(AC_Test::available());
AC_Test::verify();
} }
TEST(AllCases, all_cases_parallel) { TEST(AllCases, all_cases_parallel) {
all_cases_reset(); AC_Test::reset();
BS::thread_pool executor; EXPECT_FALSE(AC_Test::available());
EXPECT_FALSE(AllCases::instance().is_available());
AllCases::instance().build_parallel([&executor](auto &&func) { co::Executor executor;
executor.detach_task(func); AllCases::instance().build_parallel(executor.Entry());
}); EXPECT_TRUE(AC_Test::available());
EXPECT_TRUE(AllCases::instance().is_available());
AllCases::instance().build_parallel([&executor](auto &&func) { AllCases::instance().build_parallel(executor.Entry());
executor.detach_task(func); EXPECT_TRUE(AC_Test::available());
}); AC_Test::verify();
EXPECT_TRUE(AllCases::instance().is_available());
all_cases_verify();
} }
TEST(AllCases, all_cases_parallel_mutex) { TEST(AllCases, all_cases_parallel_race) {
all_cases_reset(); AC_Test::reset();
BS::thread_pool executor; EXPECT_FALSE(AC_Test::available());
const auto handle = race_test(TEST_THREAD_NUM, [&executor]() {
AllCases::instance().build_parallel([&executor](auto &&func) { co::Executor executor;
executor.detach_task(func); auto racer = co::Racer([&executor] {
}); AllCases::instance().build_parallel(executor.Entry());
}); });
EXPECT_FALSE(AllCases::instance().is_available()); EXPECT_FALSE(AC_Test::available());
handle->wait();
EXPECT_TRUE(AllCases::instance().is_available()); racer.Join();
all_cases_verify(); EXPECT_TRUE(AC_Test::available());
AC_Test::verify();
} }
TEST(AllCases, all_cases_async) { TEST(AllCases, all_cases_async) {
all_cases_reset(); AC_Test::reset();
EXPECT_FALSE(AC_Test::available());
co::Executor executor;
std::atomic_flag flag; std::atomic_flag flag;
BS::thread_pool executor;
flag.clear(); flag.clear();
AllCases::instance().build_parallel_async([&executor](auto &&func) { AllCases::instance().build_parallel_async(executor.Entry(), [&flag]() {
executor.detach_task(func);
}, [&flag]() { // callback function
flag.test_and_set(); flag.test_and_set();
flag.notify_all(); flag.notify_all();
}); });
EXPECT_FALSE(AllCases::instance().is_available()); EXPECT_FALSE(AC_Test::available());
flag.wait(false); flag.wait(false);
EXPECT_TRUE(AllCases::instance().is_available()); EXPECT_TRUE(AC_Test::available());
flag.clear(); flag.clear();
AllCases::instance().build_parallel_async([&executor](auto &&func) { AllCases::instance().build_parallel_async(executor.Entry(), [&flag]() {
executor.detach_task(func);
}, [&flag]() { // callback function
flag.test_and_set(); flag.test_and_set();
flag.notify_all(); flag.notify_all();
}); });
EXPECT_TRUE(AllCases::instance().is_available()); EXPECT_TRUE(AC_Test::available());
flag.wait(false); flag.wait(false);
EXPECT_TRUE(AllCases::instance().is_available()); EXPECT_TRUE(AC_Test::available());
all_cases_verify(); AC_Test::verify();
} }
TEST(AllCases, all_cases_async_mutex) { TEST(AllCases, all_cases_async_race) {
all_cases_reset(); AC_Test::reset();
BS::thread_pool executor; EXPECT_FALSE(AC_Test::available());
co::Executor executor;
std::atomic<int> callback_num(0); std::atomic<int> callback_num(0);
const auto handle = race_test(TEST_THREAD_NUM, [&executor, &callback_num]() { auto racer = co::Racer([&executor, &callback_num] {
AllCases::instance().build_parallel_async([&executor](auto &&func) { AllCases::instance().build_parallel_async(executor.Entry(), [&callback_num]() {
executor.detach_task(func);
}, [&callback_num]() {
callback_num.fetch_add(1); callback_num.fetch_add(1);
}); });
}); });
EXPECT_FALSE(AllCases::instance().is_available()); EXPECT_FALSE(AC_Test::available());
handle->wait();
EXPECT_TRUE(AllCases::instance().is_available()); racer.Join();
EXPECT_EQ(callback_num.load(), TEST_THREAD_NUM); EXPECT_TRUE(AC_Test::available());
all_cases_verify(); EXPECT_EQ(callback_num.load(), co::Racer::Times);
AC_Test::verify();
} }

45
src/core_test/utility/concurrent.h

@ -0,0 +1,45 @@
#pragma once
#include <memory>
#include <functional>
#include "BS_thread_pool.hpp"
namespace co {
class Racer final {
public:
static constexpr int Times = 256;
explicit Racer(std::function<void()> &&item) : pool_(Times), item_(item) {
pool_.detach_sequence(0, Times, [this](const int) {
item_(); // execute race function
});
}
~Racer() { Join(); }
void Join() { pool_.wait(); }
private:
BS::thread_pool pool_;
std::function<void()> item_;
};
class Executor final {
public:
Executor() = default;
~Executor() { pool_.wait(); }
std::function<void(std::function<void()> &&)> Entry() {
return [this](auto &&func) {
pool_.detach_task(func);
};
}
private:
BS::thread_pool pool_;
};
} // namespace co
Loading…
Cancel
Save