Compare commits
1 Commits
master
...
overhead_t
| Author | SHA1 | Date | |
|---|---|---|---|
| 7199ab5f99 |
@ -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)
|
||||
@ -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)
|
||||
@ -1,7 +1,6 @@
|
||||
#include <cmath>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include "benchmark/benchmark.h"
|
||||
#include "glog/logging.h"
|
||||
|
||||
@ -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
|
||||
@ -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)
|
||||
129
tests/main.cc
129
tests/main.cc
@ -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();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user