From 4a4a9d6b77fbed0d76a4c886019765e0e19aa3a2 Mon Sep 17 00:00:00 2001 From: Alex Tsvetanov Date: Fri, 2 May 2025 01:10:48 +0300 Subject: [PATCH 1/2] Remove OpenMP support --- .github/workflows/ci.yaml | 14 ++++++- benchmarks/main.cpp | 56 ++++++++++++++++--------- include/pubsub.h | 88 ++++++++++++++++++++------------------- 3 files changed, 95 insertions(+), 63 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 28dc2a7..1ebb011 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -42,6 +42,10 @@ jobs: - name: Run tests run: ctest --test-dir build --output-on-failure --build-config Release + - name: Run benchmarks + run: > + ./build/benchmarks/benchmark_pubsub --benchmark_counters_tabular=true + windows: name: Windows Verification runs-on: windows-latest @@ -87,6 +91,10 @@ jobs: - name: Run tests run: ctest --test-dir build --output-on-failure --build-config Release + + - name: Run benchmarks + run: > + ./build/benchmarks/Release/benchmark_pubsub.exe --benchmark_counters_tabular=true macos: name: macOS Verification runs-on: macos-latest @@ -121,4 +129,8 @@ jobs: run: cmake --build build --config Release - name: Run tests - run: ctest --test-dir build --output-on-failure --build-config Release \ No newline at end of file + run: ctest --test-dir build --output-on-failure --build-config Release + + - name: Run benchmarks + run: > + ./build/benchmarks/benchmark_pubsub --benchmark_counters_tabular=true diff --git a/benchmarks/main.cpp b/benchmarks/main.cpp index fc44465..1ccde43 100644 --- a/benchmarks/main.cpp +++ b/benchmarks/main.cpp @@ -4,11 +4,17 @@ #include #include #include +#include #include "pubsub.h" -#ifdef WITH_OMP -#include +#ifdef __linux__ + #include + #include + #include +#elif defined(_WIN32) + #include + #include #endif // ========== Event Definition ========== @@ -37,23 +43,37 @@ std::unique_ptr create_publisher_with_heavy_subs(int num_subs std::vector subscriber_counts = {1, 10, 100, 500, 1000}; std::unordered_map> heavy_publishers; -// ========== Benchmark Macro ========== -#define DEFINE_HEAVY_EMIT_BENCH(name, emit_method) \ - static void name(benchmark::State& state) { \ - int subs = state.range(0); \ - auto it = heavy_publishers.find(subs); \ - if (it == heavy_publishers.end()) state.SkipWithError("Missing pub"); \ - auto& pub = it->second; \ - for (auto _ : state) { \ - pub->emit_method; \ - } \ - } \ - BENCHMARK(name)->Args({1})->Args({10})->Args({100})->Args({500})->Args({1000}); +// ========== Updated Benchmark Macro with Memory + Time Metrics ========== +#define DEFINE_HEAVY_EMIT_BENCH(name, emit_method) \ + static void name(benchmark::State& state) { \ + std::unordered_map>::iterator it; \ + int subs = state.range(0); \ + it = heavy_publishers.find(subs); \ + if (it == heavy_publishers.end()) state.SkipWithError("Missing pub"); \ + auto& pub = it->second; \ + for (auto _ : state) { \ + auto start_time = std::chrono::high_resolution_clock::now(); \ + benchmark::DoNotOptimize(pub->emit_method); \ + auto end_time = std::chrono::high_resolution_clock::now(); \ + auto duration = end_time - start_time; \ + state.counters["time_per_sub_ns"] = \ + std::chrono::duration_cast(duration).count() / static_cast(subs); \ + state.counters["subs_per_sec"] = \ + benchmark::Counter(subs, benchmark::Counter::kIsRate); \ + } \ + state.SetComplexityN(state.range(0)); \ + } \ + BENCHMARK(name)->MeasureProcessCPUTime() \ + ->UseRealTime() \ + ->Repetitions(10) \ + ->Args({1})->Args({10})->Args({100})->Args({500})->Args({1000}) \ + ->Complexity(benchmark::oN) \ + ; // ========== Emit Variants ========== DEFINE_HEAVY_EMIT_BENCH(BM_PubSub_Emit_Sync, emit(42)) -DEFINE_HEAVY_EMIT_BENCH(BM_PubSub_Emit_StdAsync, emit_thread_async(42)) +DEFINE_HEAVY_EMIT_BENCH(BM_PubSub_Emit_StdAsync_NoWait, emit_thread_async(42)) #if defined(__cpp_lib_execution) DEFINE_HEAVY_EMIT_BENCH(BM_PubSub_Emit_StdExec_seq, emit_async(std::execution::seq, 42)) @@ -62,10 +82,6 @@ DEFINE_HEAVY_EMIT_BENCH(BM_PubSub_Emit_StdExec_par_unseq, emit_async(st DEFINE_HEAVY_EMIT_BENCH(BM_PubSub_Emit_StdExec_unseq, emit_async(std::execution::unseq, 42)) #endif -#ifdef WITH_OMP -DEFINE_HEAVY_EMIT_BENCH(BM_PubSub_Emit_OpenMP, emit_omp_async(42)) -#endif - #ifdef WITH_TBB DEFINE_HEAVY_EMIT_BENCH(BM_PubSub_Emit_TBB, emit_tbb_async(42)) #endif @@ -77,6 +93,8 @@ int main(int argc, char** argv) { } benchmark::Initialize(&argc, argv); + if (benchmark::ReportUnrecognizedArguments(argc, argv)) return 1; + benchmark::RunSpecifiedBenchmarks(); return 0; } diff --git a/include/pubsub.h b/include/pubsub.h index dabb5e4..7765a96 100644 --- a/include/pubsub.h +++ b/include/pubsub.h @@ -12,10 +12,6 @@ #include #include -#ifdef WITH_OMP - #include -#endif - #ifdef WITH_TBB #include #endif @@ -154,10 +150,17 @@ namespace pubsub { * @brief Emit an event synchronously. */ template - void emit(Args... args) { + [[nodiscard]] bool emit(Args... args) { + bool success = true; for (const auto& cb : callbacks) { - cb(args...); + try { + cb(args...); + } + catch(...) { + success = false; + } } + return success; } /** @@ -167,34 +170,32 @@ namespace pubsub { * a queue and calls them when a thread in the pool is free */ template - void emit_thread_async(Args... args) { - for (const auto& cb : callbacks) { - (void)std::async(std::launch::async, cb, args...); - } - } - -#ifdef WITH_OMP - /** - * @brief Emit an event asynchronously using OpenMP. - */ - template - void emit_omp_async(Args... args) { - #pragma omp parallel for + [[nodiscard]] bool emit_thread_async(Args... args) { + bool success = true; + std::vector> futures; + futures.reserve(callbacks.size()); for (const auto& cb : callbacks) { - cb(args...); + futures.emplace_back(std::async(std::launch::async, cb, args...)); } + return success; } -#endif #ifdef WITH_TBB /** * @brief Emit an event asynchronously using oneTBB. */ template - void emit_tbb_async(Args... args) { + [[nodiscard]] bool emit_tbb_async(Args... args) { + bool success = true; tbb::parallel_for_each(callbacks.begin(), callbacks.end(), [&](const auto& cb) { - cb(args...); + try { + cb(args...); + } + catch(...) { + success = false; + } }); + return success; } #endif @@ -203,12 +204,19 @@ namespace pubsub { * @brief Emit an event asynchronously using . */ template> - void emit_async(ExecutionPolicy policy, Args... args) { + [[nodiscard]] bool emit_async(ExecutionPolicy policy, Args... args) { + bool success = true; std::for_each(policy, callbacks.begin(), callbacks.end(), [&](const auto& cb) { - cb(args...); + try { + cb(args...); + } + catch(...) { + success = false; + } } ); + return success; } #endif }; @@ -258,36 +266,28 @@ namespace pubsub { * @brief Emit an event synchronously to all listeners. */ template requires IsEvent - void emit(Args... args) { - get_handler()->emit(args...); + bool emit(Args... args) { + return get_handler()->emit(args...); } /** * @brief Emit an event asynchronously to all listeners. */ template requires IsEvent - void emit_thread_async(Args... args) { - get_handler()->emit_thread_async(args...); + bool emit_thread_async(Args... args) { + return get_handler()->emit_thread_async(args...); } -#ifdef WITH_OMP - /** - * @brief Emit an event asynchronously using OpenMP to all listeners. - */ - template requires IsEvent - void emit_omp_async(Args... args) { - get_handler()->emit_omp_async(args...); - } -#endif - #ifdef WITH_TBB /** * @brief Emit an event asynchronously using oneTBB to all listeners. */ template requires IsEvent - void emit_tbb_async(Args... args) { - get_handler()->emit_tbb_async(args...); + bool emit_tbb_async(Args... args) { + return get_handler()->emit_tbb_async(args...); } +#else + #warning TBB not available #endif #if defined(__cpp_lib_execution) @@ -295,9 +295,11 @@ namespace pubsub { * @brief Emit an event asynchronously to all listeners. */ template requires IsEvent - void emit_async(Args... args) { - get_handler()->emit_async(args...); + bool emit_async(Args... args) { + return get_handler()->emit_async(args...); } +#else + #warning std::execution not available #endif /** From 42c28b050c36554634055a449da90dfdb0e104ed Mon Sep 17 00:00:00 2001 From: Alex Tsvetanov Date: Fri, 2 May 2025 01:13:15 +0300 Subject: [PATCH 2/2] Enable benchmarks --- .github/workflows/ci.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 1ebb011..001ac18 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -34,6 +34,7 @@ jobs: cmake -S . -B build -G "${{ matrix.generator }}" -DCMAKE_CXX_COMPILER=${{ env.CXX }} + -DENABLE_BM=ON shell: bash - name: Build @@ -84,6 +85,7 @@ jobs: -G "${{ matrix.generator }}" -DCMAKE_CXX_COMPILER="${{ matrix.compiler }}" -DCMAKE_TOOLCHAIN_FILE="${{ env.CMAKE_TOOLCHAIN_FILE }}" + -DENABLE_BM=ON shell: bash - name: Build @@ -123,6 +125,7 @@ jobs: cmake -S . -B build -G "${{ matrix.generator }}" -DCMAKE_CXX_COMPILER=${{ env.CXX }} + -DENABLE_BM=ON shell: bash - name: Build