Compare commits

...

1 Commits

Author SHA1 Message Date
7199ab5f99 10ms max found on i5 12600KF 2025-07-03 21:33:30 +03:00
6 changed files with 118 additions and 60 deletions

View File

@ -7,12 +7,14 @@ add_library(${PROJECT_NAME} SHARED)
target_include_directories(${PROJECT_NAME} PUBLIC include) 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_sources(${PROJECT_NAME} PRIVATE ${prj_src})
# target_compile_options(${PROJECT_NAME} PRIVATE -fsanitize=thread) target_compile_definitions(${PROJECT_NAME} PRIVATE INLOOP_TIME)
# target_link_options(${PROJECT_NAME} PRIVATE -fsanitize=thread)
target_compile_options(${PROJECT_NAME} PRIVATE -fsanitize=thread)
target_link_options(${PROJECT_NAME} PRIVATE -fsanitize=thread)
add_subdirectory(bench) add_subdirectory(bench)
add_subdirectory(tests) add_subdirectory(tests)

View File

@ -12,5 +12,5 @@ target_link_libraries(${NAME} PRIVATE
${PROJECT_NAME} ${PROJECT_NAME}
) )
# target_compile_options(${NAME} PRIVATE -fsanitize=thread) target_compile_options(${NAME} PRIVATE -fsanitize=thread)
# target_link_options(${NAME} PRIVATE -fsanitize=thread) target_link_options(${NAME} PRIVATE -fsanitize=thread)

View File

@ -1,7 +1,6 @@
#include <cmath> #include <cmath>
#include <functional> #include <functional>
#include <mutex> #include <mutex>
#include <thread>
#include <unordered_map> #include <unordered_map>
#include "benchmark/benchmark.h" #include "benchmark/benchmark.h"
#include "glog/logging.h" #include "glog/logging.h"

View File

@ -1,5 +1,9 @@
#include "logger.hh" #include "logger.hh"
#include <atomic> #include <atomic>
#ifdef INLOOP_TIME
#include <chrono>
#endif
#include <iostream>
#include <memory> #include <memory>
#include <thread> #include <thread>
namespace vptyp { namespace vptyp {
@ -50,7 +54,7 @@ class Logger::Worker {
state = RUNNING; state = RUNNING;
while (state == RUNNING) { while (state == RUNNING) {
unroll(); unroll();
sleep(1); std::this_thread::yield();
} }
} }
@ -74,14 +78,29 @@ void Logger::Worker::unroll() {
parent.active.store(toBeActive, std::memory_order_release); parent.active.store(toBeActive, std::memory_order_release);
// so we setting up happens before relation with counters // 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) { while (parent.refs.load(std::memory_order_acquire) > 0) {
std::this_thread::yield(); 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 auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(
// // __tsan_acquire(tmp.use_count()); end_time - start_time);
// std::atomic_thread_fence(std::memory_order_acquire);
static std::atomic<int64_t> 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? // at this place we are guarantee that tmp is only ours or not?
std::string output; std::string output;
bool haveToPush{false}; bool haveToPush{false};
@ -95,8 +114,9 @@ void Logger::Worker::unroll() {
haveToPush = true; haveToPush = true;
} }
if (haveToPush) if (haveToPush) {
out << output << std::endl; // out << output << std::endl;
}
} }
} // namespace vptyp } // namespace vptyp

View File

@ -12,5 +12,5 @@ target_link_libraries(${NAME} PRIVATE
${PROJECT_NAME} ${PROJECT_NAME}
) )
# target_compile_options(${NAME} PRIVATE -fsanitize=thread) target_compile_options(${NAME} PRIVATE -fsanitize=thread)
# target_link_options(${NAME} PRIVATE -fsanitize=thread) target_link_options(${NAME} PRIVATE -fsanitize=thread)

View File

@ -4,12 +4,12 @@
#include <queue> #include <queue>
#include <thread> #include <thread>
TEST(SingleThread, Configuration) { // TEST(SingleThread, Configuration) {
vptyp::Logger l(std::cout); // vptyp::Logger l;
EXPECT_NO_THROW(l.configure({"apprx", "size", "time"})); // EXPECT_NO_THROW(l.configure({"apprx", "size", "time"}));
EXPECT_NO_THROW(l.add("apprx", 123)); // EXPECT_NO_THROW(l.add("apprx", 123));
EXPECT_ANY_THROW(l.add("not in configuration", 123)); // EXPECT_ANY_THROW(l.add("not in configuration", 123));
} // }
void outBufferCheck(std::ostringstream& s, void outBufferCheck(std::ostringstream& s,
std::queue<std::pair<std::string, int64_t>>& prev) { std::queue<std::pair<std::string, int64_t>>& prev) {
@ -33,54 +33,54 @@ void outBufferCheck(std::ostringstream& s,
s.emplace(nextAwaiting.first); s.emplace(nextAwaiting.first);
prev.pop(); 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); EXPECT_EQ(prev.empty(), true);
} }
TEST(SingleThread, Add) { // TEST(SingleThread, Add) {
std::ostringstream s; // std::ostringstream s;
std::queue<std::pair<std::string, int64_t>> prev; // std::queue<std::pair<std::string, int64_t>> prev;
{ // {
vptyp::Logger l(s); // vptyp::Logger l(s);
l.configure({"apprx", "size", "time"}); // l.configure({"apprx", "size", "time"});
auto decorator = [&l, &prev](std::string field, int value) { // auto decorator = [&l, &prev](std::string field, int value) {
l.add(field, value); // l.add(field, value);
prev.push({field, value}); // prev.push({field, value});
}; // };
decorator("size", 1); // decorator("size", 1);
decorator("time", 1); // decorator("time", 1);
sleep(2); // twice of logger sleep // sleep(2); // twice of logger sleep
decorator("apprx", 12); // decorator("apprx", 12);
decorator("size", 2); // decorator("size", 2);
sleep(2); // sleep(2);
} // }
// results // // results
outBufferCheck(s, prev); // outBufferCheck(s, prev);
} // }
TEST(MultiThread, Configure) { // TEST(MultiThread, Configure) {
vptyp::Logger l(std::cout); // vptyp::Logger l(std::cout);
std::atomic<size_t> howManyConfigured; // std::atomic<size_t> howManyConfigured;
auto fConfig = [&l, &howManyConfigured] { // auto fConfig = [&l, &howManyConfigured] {
bool res{false}; // bool res{false};
EXPECT_NO_THROW(res = l.configure({"apprx", "size", "time"})); // EXPECT_NO_THROW(res = l.configure({"apprx", "size", "time"}));
howManyConfigured.fetch_add(static_cast<size_t>(res)); // howManyConfigured.fetch_add(static_cast<size_t>(res));
}; // };
// simulate race condition configure // // simulate race condition configure
std::vector<std::jthread> threads(10); // std::vector<std::jthread> threads(10);
for (auto& thread : threads) { // for (auto& thread : threads) {
thread = std::jthread(fConfig); // thread = std::jthread(fConfig);
} // }
threads.clear(); // threads.clear();
EXPECT_EQ(howManyConfigured.load(), 1); // EXPECT_EQ(howManyConfigured.load(), 1);
} // }
TEST(MultiThread, Add) { TEST(MultiThread, Add) {
std::ostringstream s; std::ostringstream s;
@ -91,13 +91,13 @@ TEST(MultiThread, Add) {
l.configure({"apprx", "size", "time"}); l.configure({"apprx", "size", "time"});
auto decorator = [&l, &prev, &mtx](std::string field, int value) { 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); l.add(field, value);
{ {
std::lock_guard g(mtx); std::lock_guard g(mtx);
prev.push({field, value}); prev.push({field, value});
} }
sleep(1); std::this_thread::yield();
} }
}; };
@ -114,6 +114,43 @@ TEST(MultiThread, Add) {
outBufferCheck(s, prev); outBufferCheck(s, prev);
} }
TEST(MultiThread, Add16Threads) {
std::ostringstream s;
std::queue<std::pair<std::string, int64_t>> 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<std::jthread> 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) { int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv); testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS(); return RUN_ALL_TESTS();