Browse Source

feat: concurrent test helper

master
Dnomd343 2 months ago
parent
commit
51f0096a84
  1. 1
      src/core_test/CMakeLists.txt
  2. 8
      src/core_test/cases/all_cases.cc
  3. 8
      src/core_test/cases/basic_ranges.cc
  4. 8
      src/core_test/cases/helper/cases.h
  5. 16
      src/core_test/codec/short_code.cc
  6. 51
      src/core_test/helper/concurrent.h
  7. 36
      src/core_test/helper/internal/concurrent.cc
  8. 65
      src/core_test/utility/concurrent.h

1
src/core_test/CMakeLists.txt

@ -13,6 +13,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR})
# ------------------------------------------------------------------------------------ # # ------------------------------------------------------------------------------------ #
add_library(test_helper add_library(test_helper
helper/internal/concurrent.cc
helper/internal/parallel.cc helper/internal/parallel.cc
helper/internal/hash.cc helper/internal/hash.cc
) )

8
src/core_test/cases/all_cases.cc

@ -106,11 +106,9 @@ TEST_FF(AllCases, all_cases) {
} }
TEST_FF(AllCases, all_cases_race) { TEST_FF(AllCases, all_cases_race) {
racer_.Start([] { racer_.Execute([] {
AllCases::instance().build(); AllCases::instance().build();
}); });
EXPECT_FALSE(Available());
racer_.Join();
EXPECT_TRUE(Available()); EXPECT_TRUE(Available());
Verify(); Verify();
} }
@ -139,13 +137,11 @@ TEST_FF(AllCases, all_cases_async) {
TEST_FF(AllCases, all_cases_async_race) { TEST_FF(AllCases, all_cases_async_race) {
counter_.store(0); counter_.store(0);
racer_.Start([this] { racer_.Execute([this] {
AllCases::instance().build_async(executor_.Entry(), [this] { AllCases::instance().build_async(executor_.Entry(), [this] {
counter_.fetch_add(1); counter_.fetch_add(1);
}); });
}); });
EXPECT_FALSE(Available());
racer_.Join();
EXPECT_TRUE(Available()); EXPECT_TRUE(Available());
EXPECT_EQ(counter_.load(), racer_.RaceNum()); EXPECT_EQ(counter_.load(), racer_.RaceNum());
Verify(); Verify();

8
src/core_test/cases/basic_ranges.cc

@ -74,11 +74,9 @@ TEST_FF(BasicRanges, basic_ranges) {
} }
TEST_FF(BasicRanges, basic_ranges_race) { TEST_FF(BasicRanges, basic_ranges_race) {
racer_.Start([] { racer_.Execute([] {
BasicRanges::instance().build(); BasicRanges::instance().build();
}); });
EXPECT_FALSE(Available());
racer_.Join();
EXPECT_TRUE(Available()); EXPECT_TRUE(Available());
Verify(); Verify();
} }
@ -107,13 +105,11 @@ TEST_FF(BasicRanges, basic_ranges_async) {
TEST_FF(BasicRanges, basic_ranges_async_race) { TEST_FF(BasicRanges, basic_ranges_async_race) {
counter_.store(0); counter_.store(0);
racer_.Start([this] { racer_.Execute([this] {
BasicRanges::instance().build_async(executor_.Entry(), [this] { BasicRanges::instance().build_async(executor_.Entry(), [this] {
counter_.fetch_add(1); counter_.fetch_add(1);
}); });
}); });
EXPECT_FALSE(Available());
racer_.Join();
EXPECT_TRUE(Available()); EXPECT_TRUE(Available());
EXPECT_EQ(counter_.load(), racer_.RaceNum()); EXPECT_EQ(counter_.load(), racer_.RaceNum());
Verify(); Verify();

8
src/core_test/cases/helper/cases.h

@ -4,7 +4,7 @@
#include <algorithm> #include <algorithm>
#include "group/group.h" #include "group/group.h"
#include "utility/concurrent.h" #include "helper/concurrent.h"
// ----------------------------------------------------------------------------------------- // // ----------------------------------------------------------------------------------------- //
@ -33,13 +33,13 @@ constexpr auto Heads = std::to_array({
class Concurrent { class Concurrent {
protected: protected:
// Execute same task concurrently. // Execute same task concurrently.
co::Racer racer_ {256}; helper::Racer racer_ {};
// Execute assigned tasks one by one. // Execute assigned tasks one by one.
co::Executor serial_ {1}; helper::Executor serial_ {1};
// Execute assigned tasks on all cores. // Execute assigned tasks on all cores.
co::Executor executor_ {0}; helper::Executor executor_ {0};
// Atomic helpers for multi-thread testing. // Atomic helpers for multi-thread testing.
std::atomic<int> counter_ {0}; std::atomic<int> counter_ {0};

16
src/core_test/codec/short_code.cc

@ -5,7 +5,7 @@
#include "helper/expect.h" #include "helper/expect.h"
#include "helper/parallel.h" #include "helper/parallel.h"
#include "utility/exposer.h" #include "utility/exposer.h"
#include "utility/concurrent.h" #include "helper/concurrent.h"
#include "all_cases/all_cases.h" #include "all_cases/all_cases.h"
#include "short_code/short_code.h" #include "short_code/short_code.h"
@ -23,8 +23,6 @@ using klotski::cases::ALL_CASES_NUM;
using klotski::cases::ALL_CASES_NUM_; using klotski::cases::ALL_CASES_NUM_;
using klotski::codec::SHORT_CODE_LIMIT; using klotski::codec::SHORT_CODE_LIMIT;
constexpr auto TEST_THREAD_NUM = 256;
/// Forcibly modify private variables to reset state. /// Forcibly modify private variables to reset state.
EXPOSE_VAR(AllCases, bool, available_) EXPOSE_VAR(AllCases, bool, available_)
EXPOSE_VAR(BasicRanges, bool, available_) EXPOSE_VAR(BasicRanges, bool, available_)
@ -154,7 +152,7 @@ TEST(ShortCode, initialize) {
} }
TEST(ShortCode, speed_up) { TEST(ShortCode, speed_up) {
co::Racer racer {TEST_THREAD_NUM}; helper::Racer racer {};
static auto EXPECT_STAGE_0 = +[]() { static auto EXPECT_STAGE_0 = +[]() {
EXPECT_FALSE(exposer::ShortCode_fast_()); EXPECT_FALSE(exposer::ShortCode_fast_());
@ -182,18 +180,18 @@ TEST(ShortCode, speed_up) {
speed_up_reset(); speed_up_reset();
EXPECT_STAGE_0(); EXPECT_STAGE_0();
racer.Race([] { ShortCode::speed_up(false); }); racer.Execute([] { ShortCode::speed_up(false); });
EXPECT_STAGE_1(); EXPECT_STAGE_1();
racer.Race([] { ShortCode::speed_up(true); }); racer.Execute([] { ShortCode::speed_up(true); });
EXPECT_STAGE_2(); EXPECT_STAGE_2();
racer.Race([] { ShortCode::speed_up(true); }); racer.Execute([] { ShortCode::speed_up(true); });
EXPECT_STAGE_2(); EXPECT_STAGE_2();
racer.Race([] { ShortCode::speed_up(false); }); racer.Execute([] { ShortCode::speed_up(false); });
EXPECT_STAGE_2(); EXPECT_STAGE_2();
speed_up_reset(); speed_up_reset();
EXPECT_STAGE_0(); EXPECT_STAGE_0();
racer.Race([] { ShortCode::speed_up(true); }); racer.Execute([] { ShortCode::speed_up(true); });
EXPECT_STAGE_2(); EXPECT_STAGE_2();
} }

51
src/core_test/helper/concurrent.h

@ -0,0 +1,51 @@
#pragma once
/// The concurrency test helper class implements the Executor and Racer based
/// on the BS::thread_pool. The Executor allows you to submit multiple tasks
/// and dispatch them to different threads. The Racer schedules the specified
/// function to a certain number of threads at one time.
#include <functional>
#include <BS_thread_pool.hpp>
namespace helper {
// ----------------------------------------------------------------------------------------- //
class Executor final {
public:
/// Specify the number of threads, default to the number of CPU cores.
explicit Executor(const int num = 0) : pool_(num) {}
/// Get the executor entry for submitting tasks.
std::function<void(std::function<void()> &&func)> Entry();
~Executor();
private:
BS::thread_pool pool_;
};
// ----------------------------------------------------------------------------------------- //
class Racer final {
public:
/// Create race tester with a specified number of concurrency.
explicit Racer(const int num = 256) : race_num_(num), pool_(num) {}
/// Start the racing test process with specified number.
void Execute(std::function<void()> &&item);
/// Get the number of concurrency in Racer.
int RaceNum() const;
~Racer();
private:
const int race_num_;
BS::thread_pool pool_;
};
// ----------------------------------------------------------------------------------------- //
} // namespace helper

36
src/core_test/helper/internal/concurrent.cc

@ -0,0 +1,36 @@
#include "helper/concurrent.h"
using helper::Racer;
using helper::Executor;
// ----------------------------------------------------------------------------------------- //
std::function<void(std::function<void()> &&)> Executor::Entry() {
return [this](auto &&func) {
pool_.detach_task(func);
};
}
Executor::~Executor() {
pool_.wait();
}
// ----------------------------------------------------------------------------------------- //
void Racer::Execute(std::function<void()> &&item) {
auto wrapper = [item = std::move(item)](const int) {
item(); // execute racing function
};
pool_.detach_sequence(0, race_num_, wrapper);
pool_.wait();
}
int Racer::RaceNum() const {
return race_num_; // number of racing threads
}
Racer::~Racer() {
pool_.wait();
}
// ----------------------------------------------------------------------------------------- //

65
src/core_test/utility/concurrent.h

@ -1,65 +0,0 @@
#pragma once
/// The concurrency test helper class implements the Executor and Racer based
/// on the BS::thread_pool. The Executor allows you to submit multiple tasks
/// and dispatch them to different threads. The Racer schedules the specified
/// function to a certain number of threads at one time.
#include <functional>
#include "BS_thread_pool.hpp"
namespace co {
class Executor final {
public:
explicit Executor(const int num) : pool_(num) {}
std::function<void(std::function<void()> &&)> Entry() {
return [this](auto &&func) {
pool_.detach_task(func);
};
}
~Executor() {
pool_.wait();
}
private:
BS::thread_pool pool_;
};
class Racer final {
public:
explicit Racer(const int num) : race_num_(num), pool_(num) {}
int RaceNum() const {
return race_num_; // number of racing threads
}
void Race(std::function<void()> &&item) {
Start(std::move(item));
Join();
}
void Start(std::function<void()> &&item) {
auto wrapper = [item = std::move(item)](const int) {
item(); // execute racing function
};
pool_.detach_sequence(0, race_num_, wrapper);
}
void Join() {
pool_.wait();
}
~Racer() {
Join();
}
private:
const int race_num_;
BS::thread_pool pool_;
};
} // namespace co
Loading…
Cancel
Save