From 7199ab5f998aa56d78c78c5cca6b9efae556638e Mon Sep 17 00:00:00 2001 From: Artur Mukhamadiev Date: Mon, 23 Jun 2025 18:18:18 +0300 Subject: [PATCH] 10ms max found on i5 12600KF --- CMakeLists.txt | 8 ++- bench/CMakeLists.txt | 4 +- bench/main.cc | 1 - src/logger.cc | 32 +++++++++-- tests/CMakeLists.txt | 4 +- tests/main.cc | 129 ++++++++++++++++++++++++++++--------------- 6 files changed, 118 insertions(+), 60 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 44d118d..ce27070 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,12 +7,14 @@ add_library(${PROJECT_NAME} SHARED) target_include_directories(${PROJECT_NAME} PUBLIC include) -file(GLOB prj_src src/*) +file(GLOB prj_src src/*.cc) target_sources(${PROJECT_NAME} PRIVATE ${prj_src}) -# target_compile_options(${PROJECT_NAME} PRIVATE -fsanitize=thread) -# target_link_options(${PROJECT_NAME} PRIVATE -fsanitize=thread) +target_compile_definitions(${PROJECT_NAME} PRIVATE INLOOP_TIME) + +target_compile_options(${PROJECT_NAME} PRIVATE -fsanitize=thread) +target_link_options(${PROJECT_NAME} PRIVATE -fsanitize=thread) add_subdirectory(bench) add_subdirectory(tests) \ No newline at end of file diff --git a/bench/CMakeLists.txt b/bench/CMakeLists.txt index d389f97..2d3a969 100644 --- a/bench/CMakeLists.txt +++ b/bench/CMakeLists.txt @@ -12,5 +12,5 @@ target_link_libraries(${NAME} PRIVATE ${PROJECT_NAME} ) -# target_compile_options(${NAME} PRIVATE -fsanitize=thread) -# target_link_options(${NAME} PRIVATE -fsanitize=thread) \ No newline at end of file +target_compile_options(${NAME} PRIVATE -fsanitize=thread) +target_link_options(${NAME} PRIVATE -fsanitize=thread) \ No newline at end of file diff --git a/bench/main.cc b/bench/main.cc index 3623c28..fc6b6d6 100644 --- a/bench/main.cc +++ b/bench/main.cc @@ -1,7 +1,6 @@ #include #include #include -#include #include #include "benchmark/benchmark.h" #include "glog/logging.h" diff --git a/src/logger.cc b/src/logger.cc index c606400..52d8278 100644 --- a/src/logger.cc +++ b/src/logger.cc @@ -1,5 +1,9 @@ #include "logger.hh" #include +#ifdef INLOOP_TIME +#include +#endif +#include #include #include namespace vptyp { @@ -50,7 +54,7 @@ class Logger::Worker { state = RUNNING; while (state == RUNNING) { unroll(); - sleep(1); + std::this_thread::yield(); } } @@ -74,14 +78,29 @@ void Logger::Worker::unroll() { parent.active.store(toBeActive, std::memory_order_release); // so we setting up happens before relation with counters + +#ifdef INLOOP_TIME + auto start_time = std::chrono::high_resolution_clock::now(); +#endif while (parent.refs.load(std::memory_order_acquire) > 0) { std::this_thread::yield(); } +#ifdef INLOOP_TIME + auto end_time = std::chrono::high_resolution_clock::now(); - // // it's needed thread fence to guarantee use count change - // // __tsan_acquire(tmp.use_count()); - // std::atomic_thread_fence(std::memory_order_acquire); + auto duration = std::chrono::duration_cast( + end_time - start_time); + static std::atomic max_wait_time{0}; + int64_t current_time = duration.count(); + int64_t current_max = max_wait_time.load(); + + if (current_time > current_max && + max_wait_time.compare_exchange_weak(current_max, current_time)) { + std::cout << "New maximum time spent waiting in while loop: " + << current_time << " nanoseconds" << std::endl; + } +#endif // at this place we are guarantee that tmp is only ours or not? std::string output; bool haveToPush{false}; @@ -95,8 +114,9 @@ void Logger::Worker::unroll() { haveToPush = true; } - if (haveToPush) - out << output << std::endl; + if (haveToPush) { + // out << output << std::endl; + } } } // namespace vptyp \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4d806a7..53d7714 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -12,5 +12,5 @@ target_link_libraries(${NAME} PRIVATE ${PROJECT_NAME} ) -# target_compile_options(${NAME} PRIVATE -fsanitize=thread) -# target_link_options(${NAME} PRIVATE -fsanitize=thread) \ No newline at end of file +target_compile_options(${NAME} PRIVATE -fsanitize=thread) +target_link_options(${NAME} PRIVATE -fsanitize=thread) \ No newline at end of file diff --git a/tests/main.cc b/tests/main.cc index af3adbb..5dcf83d 100644 --- a/tests/main.cc +++ b/tests/main.cc @@ -4,12 +4,12 @@ #include #include -TEST(SingleThread, Configuration) { - vptyp::Logger l(std::cout); - EXPECT_NO_THROW(l.configure({"apprx", "size", "time"})); - EXPECT_NO_THROW(l.add("apprx", 123)); - EXPECT_ANY_THROW(l.add("not in configuration", 123)); -} +// TEST(SingleThread, Configuration) { +// vptyp::Logger l; +// EXPECT_NO_THROW(l.configure({"apprx", "size", "time"})); +// EXPECT_NO_THROW(l.add("apprx", 123)); +// EXPECT_ANY_THROW(l.add("not in configuration", 123)); +// } void outBufferCheck(std::ostringstream& s, std::queue>& prev) { @@ -33,54 +33,54 @@ void outBufferCheck(std::ostringstream& s, s.emplace(nextAwaiting.first); prev.pop(); } - std::cout << nextAwaiting.first << std::endl; + // std::cout << nextAwaiting.first << std::endl; } - std::cout << "n" << std::endl; + // std::cout << "n" << std::endl; } EXPECT_EQ(prev.empty(), true); } -TEST(SingleThread, Add) { - std::ostringstream s; - std::queue> prev; - { - vptyp::Logger l(s); +// TEST(SingleThread, Add) { +// std::ostringstream s; +// std::queue> prev; +// { +// vptyp::Logger l(s); - l.configure({"apprx", "size", "time"}); +// l.configure({"apprx", "size", "time"}); - auto decorator = [&l, &prev](std::string field, int value) { - l.add(field, value); - prev.push({field, value}); - }; +// auto decorator = [&l, &prev](std::string field, int value) { +// l.add(field, value); +// prev.push({field, value}); +// }; - decorator("size", 1); - decorator("time", 1); - sleep(2); // twice of logger sleep - decorator("apprx", 12); - decorator("size", 2); - sleep(2); - } +// decorator("size", 1); +// decorator("time", 1); +// sleep(2); // twice of logger sleep +// decorator("apprx", 12); +// decorator("size", 2); +// sleep(2); +// } - // results - outBufferCheck(s, prev); -} +// // results +// outBufferCheck(s, prev); +// } -TEST(MultiThread, Configure) { - vptyp::Logger l(std::cout); - std::atomic howManyConfigured; - auto fConfig = [&l, &howManyConfigured] { - bool res{false}; - EXPECT_NO_THROW(res = l.configure({"apprx", "size", "time"})); - howManyConfigured.fetch_add(static_cast(res)); - }; - // simulate race condition configure - std::vector threads(10); - for (auto& thread : threads) { - thread = std::jthread(fConfig); - } - threads.clear(); - EXPECT_EQ(howManyConfigured.load(), 1); -} +// TEST(MultiThread, Configure) { +// vptyp::Logger l(std::cout); +// std::atomic howManyConfigured; +// auto fConfig = [&l, &howManyConfigured] { +// bool res{false}; +// EXPECT_NO_THROW(res = l.configure({"apprx", "size", "time"})); +// howManyConfigured.fetch_add(static_cast(res)); +// }; +// // simulate race condition configure +// std::vector threads(10); +// for (auto& thread : threads) { +// thread = std::jthread(fConfig); +// } +// threads.clear(); +// EXPECT_EQ(howManyConfigured.load(), 1); +// } TEST(MultiThread, Add) { std::ostringstream s; @@ -91,13 +91,13 @@ TEST(MultiThread, Add) { l.configure({"apprx", "size", "time"}); auto decorator = [&l, &prev, &mtx](std::string field, int value) { - for (auto i = 0; i < 5; ++i) { + for (auto i = 0; i < 1 << 20; ++i) { l.add(field, value); { std::lock_guard g(mtx); prev.push({field, value}); } - sleep(1); + std::this_thread::yield(); } }; @@ -114,6 +114,43 @@ TEST(MultiThread, Add) { outBufferCheck(s, prev); } +TEST(MultiThread, Add16Threads) { + std::ostringstream s; + std::queue> prev; + std::mutex mtx; + { + vptyp::Logger l(s); + l.configure({"field0", "field1", "field2", "field3", "field4", "field5", + "field6", "field7", "field8", "field9", "field10", "field11", + "field12", "field13", "field14", "field15"}); + + auto decorator = [&l, &prev, &mtx](std::string field, int value) { + for (auto i = 0; i < 1 << 20; ++i) { + l.add(field, value); + { + std::lock_guard g(mtx); + prev.push({field, value}); + } + std::this_thread::yield(); + } + }; + + std::vector threads(16); + + // Create 16 threads, each with its own unique field + for (int i = 0; i < 16; ++i) { + std::string field = "field" + std::to_string(i); + threads[i] = std::jthread(decorator, field, rand() % 100); + } + + threads.clear(); + sleep(1); + } + + // results + outBufferCheck(s, prev); +} + int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS();