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)
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)

View File

@ -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)
target_compile_options(${NAME} PRIVATE -fsanitize=thread)
target_link_options(${NAME} PRIVATE -fsanitize=thread)

View File

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

View File

@ -1,5 +1,9 @@
#include "logger.hh"
#include <atomic>
#ifdef INLOOP_TIME
#include <chrono>
#endif
#include <iostream>
#include <memory>
#include <thread>
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<std::chrono::nanoseconds>(
end_time - start_time);
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?
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

View File

@ -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)
target_compile_options(${NAME} PRIVATE -fsanitize=thread)
target_link_options(${NAME} PRIVATE -fsanitize=thread)

View File

@ -4,12 +4,12 @@
#include <queue>
#include <thread>
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<std::pair<std::string, int64_t>>& 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<std::pair<std::string, int64_t>> prev;
{
vptyp::Logger l(s);
// TEST(SingleThread, Add) {
// std::ostringstream s;
// std::queue<std::pair<std::string, int64_t>> 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<size_t> howManyConfigured;
auto fConfig = [&l, &howManyConfigured] {
bool res{false};
EXPECT_NO_THROW(res = l.configure({"apprx", "size", "time"}));
howManyConfigured.fetch_add(static_cast<size_t>(res));
};
// simulate race condition configure
std::vector<std::jthread> 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<size_t> howManyConfigured;
// auto fConfig = [&l, &howManyConfigured] {
// bool res{false};
// EXPECT_NO_THROW(res = l.configure({"apprx", "size", "time"}));
// howManyConfigured.fetch_add(static_cast<size_t>(res));
// };
// // simulate race condition configure
// std::vector<std::jthread> 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<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) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();