init
This commit is contained in:
commit
82652df451
1
.clang-format
Normal file
1
.clang-format
Normal file
@ -0,0 +1 @@
|
|||||||
|
BasedOnStyle: Chromium
|
||||||
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.cache/*
|
||||||
|
build/*
|
||||||
|
compile_commands.json
|
||||||
15
CMakeLists.txt
Normal file
15
CMakeLists.txt
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.20)
|
||||||
|
project(mLogger)
|
||||||
|
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
|
add_library(${PROJECT_NAME} SHARED)
|
||||||
|
|
||||||
|
target_include_directories(${PROJECT_NAME} PUBLIC include)
|
||||||
|
|
||||||
|
file(GLOB prj_src src/*)
|
||||||
|
|
||||||
|
target_sources(${PROJECT_NAME} PRIVATE ${prj_src})
|
||||||
|
|
||||||
|
add_subdirectory(bench)
|
||||||
|
add_subdirectory(tests)
|
||||||
18
CMakeUserPresets.json
Normal file
18
CMakeUserPresets.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"version": 4,
|
||||||
|
"vendor": {
|
||||||
|
"conan": {}
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"build/Release/generators/CMakePresets.json"
|
||||||
|
],
|
||||||
|
"configurePresets": [
|
||||||
|
{
|
||||||
|
"name": "Build default",
|
||||||
|
"inherits": "conan-release",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "Debug"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
11
bench/CMakeLists.txt
Normal file
11
bench/CMakeLists.txt
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
find_package(benchmark REQUIRED)
|
||||||
|
set(NAME ${PROJECT_NAME}_bench)
|
||||||
|
|
||||||
|
add_executable(${NAME} main.cc)
|
||||||
|
|
||||||
|
target_link_libraries(${NAME} PRIVATE
|
||||||
|
benchmark::benchmark_main
|
||||||
|
benchmark::benchmark
|
||||||
|
${PROJECT_NAME}
|
||||||
|
)
|
||||||
194
bench/main.cc
Normal file
194
bench/main.cc
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
#include <cmath>
|
||||||
|
#include <functional>
|
||||||
|
#include "benchmark/benchmark.h"
|
||||||
|
#include "logger.hh"
|
||||||
|
|
||||||
|
//! AI generated
|
||||||
|
static double taylor_approximation(
|
||||||
|
double x, // Point to evaluate
|
||||||
|
double a, // Expansion point
|
||||||
|
int n, // Number of terms
|
||||||
|
const std::function<double(double)>& func, // f(a)
|
||||||
|
const std::function<double(int, double)>& derivative // f^(k)(a)
|
||||||
|
) {
|
||||||
|
double result = 0.0;
|
||||||
|
double power = 1.0; // (x - a)^0
|
||||||
|
double factorial = 1.0; // 0!
|
||||||
|
|
||||||
|
// Zeroth derivative term (k=0)
|
||||||
|
result += func(a) * power / factorial;
|
||||||
|
|
||||||
|
// Subsequent terms (k=1 to n-1)
|
||||||
|
for (int k = 1; k < n; k++) {
|
||||||
|
factorial *= k; // k!
|
||||||
|
power *= (x - a); // (x - a)^k
|
||||||
|
|
||||||
|
double deriv = derivative(k, a);
|
||||||
|
result += deriv * power / factorial;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static double calc_exp_taylor() {
|
||||||
|
auto exp_deriv = [](int k, double a) -> double {
|
||||||
|
return std::exp(a); // k-th derivative of e^x is e^x
|
||||||
|
};
|
||||||
|
double x = 1.0;
|
||||||
|
double a_exp = 0.0;
|
||||||
|
int terms = 10;
|
||||||
|
return taylor_approximation(
|
||||||
|
x, a_exp, terms, [](double a) { return std::exp(a); }, // f(a)
|
||||||
|
exp_deriv);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void BM_taylor_logger(benchmark::State& state) {
|
||||||
|
vptyp::Logger l;
|
||||||
|
l.configure({"size", "apprx", "garbage", "garbage2"});
|
||||||
|
for (auto _ : state) {
|
||||||
|
double res = calc_exp_taylor();
|
||||||
|
l.add("apprx", res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void BM_wo_logger(benchmark::State& state) {
|
||||||
|
for (auto _ : state)
|
||||||
|
double res = calc_exp_taylor();
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(BM_wo_logger);
|
||||||
|
BENCHMARK(BM_taylor_logger);
|
||||||
|
|
||||||
|
std::string caesar_encoder(const std::string& input) {
|
||||||
|
static constexpr int small = 141;
|
||||||
|
static constexpr int big = 101;
|
||||||
|
std::string encoded;
|
||||||
|
encoded.reserve(input.size());
|
||||||
|
for (auto& c : input) {
|
||||||
|
assert((c - small < 25 && c - small > 0) || (c - big < 25 && c - big > 0));
|
||||||
|
int id, begin;
|
||||||
|
if (c - small < 0) {
|
||||||
|
id = c - big;
|
||||||
|
begin = big;
|
||||||
|
} else {
|
||||||
|
id = c - small;
|
||||||
|
begin = small;
|
||||||
|
}
|
||||||
|
encoded += begin + (id + 3) % 25;
|
||||||
|
}
|
||||||
|
return encoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
int caesar_base(int size = 1000) {
|
||||||
|
std::string a(size, 'a');
|
||||||
|
auto res = caesar_encoder(a);
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void BM_caesar_wo_logger(benchmark::State& state) {
|
||||||
|
for (auto _ : state) {
|
||||||
|
int res = caesar_base();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void BM_caesar_logger(benchmark::State& state) {
|
||||||
|
vptyp::Logger l;
|
||||||
|
l.configure({"size", "apprx", "garbage", "garbage2"});
|
||||||
|
for (auto _ : state) {
|
||||||
|
int res = caesar_base();
|
||||||
|
l.add("size", res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(BM_caesar_wo_logger);
|
||||||
|
BENCHMARK(BM_caesar_logger);
|
||||||
|
|
||||||
|
#include <random>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
static thread_local std::mt19937_64 rng(std::random_device{}());
|
||||||
|
|
||||||
|
template <typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
|
||||||
|
std::vector<T> prepareRandData(int size) {
|
||||||
|
std::uniform_int_distribution<T> dist(1, 1000000);
|
||||||
|
std::vector<T> result;
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
result.push_back(dist(rng));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0>
|
||||||
|
std::vector<T> prepareRandData(int size) {
|
||||||
|
std::uniform_real_distribution<T> dist(1, 1000000);
|
||||||
|
std::vector<T> result;
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
result.push_back(dist(rng));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr int arrSize = 1 << 20;
|
||||||
|
static constexpr int bitmask = arrSize - 1;
|
||||||
|
static void int64_division(benchmark::State& state) {
|
||||||
|
std::vector a = prepareRandData<int64_t>(arrSize);
|
||||||
|
std::vector b = prepareRandData<int64_t>(arrSize);
|
||||||
|
int64_t i = 0, i2 = 0;
|
||||||
|
for (auto _ : state) {
|
||||||
|
benchmark::DoNotOptimize(a[i++ & bitmask] / b[i2++ & bitmask]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void double_division(benchmark::State& state) {
|
||||||
|
std::vector a = prepareRandData<double>(arrSize);
|
||||||
|
std::vector b = prepareRandData<double>(arrSize);
|
||||||
|
int64_t i = 0, i2 = 0;
|
||||||
|
for (auto _ : state) {
|
||||||
|
benchmark::DoNotOptimize(a[i++ & bitmask] / b[i2++ & bitmask]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DoNothing(benchmark::State& state) {
|
||||||
|
while (state.KeepRunning())
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void BM_IntDivision(benchmark::State& state) {
|
||||||
|
volatile int a = 123456789;
|
||||||
|
volatile int b = 123;
|
||||||
|
int result;
|
||||||
|
for (auto _ : state) {
|
||||||
|
result = a / b;
|
||||||
|
benchmark::DoNotOptimize(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BENCHMARK(BM_IntDivision);
|
||||||
|
|
||||||
|
static void BM_FloatDivision(benchmark::State& state) {
|
||||||
|
volatile float a = 123456789.0f;
|
||||||
|
volatile float b = 123.0f;
|
||||||
|
float result;
|
||||||
|
for (auto _ : state) {
|
||||||
|
result = a / b;
|
||||||
|
benchmark::DoNotOptimize(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BENCHMARK(BM_FloatDivision);
|
||||||
|
|
||||||
|
static void BM_DoubleDivision(benchmark::State& state) {
|
||||||
|
volatile double a = 123456789.0;
|
||||||
|
volatile double b = 123.0;
|
||||||
|
double result;
|
||||||
|
for (auto _ : state) {
|
||||||
|
result = a / b;
|
||||||
|
benchmark::DoNotOptimize(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BENCHMARK(BM_DoubleDivision);
|
||||||
|
|
||||||
|
BENCHMARK(DoNothing);
|
||||||
|
|
||||||
|
BENCHMARK(int64_division);
|
||||||
|
BENCHMARK(double_division);
|
||||||
|
|
||||||
|
BENCHMARK_MAIN();
|
||||||
18
conanfile.txt
Normal file
18
conanfile.txt
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
[requires]
|
||||||
|
gtest/1.16.0
|
||||||
|
glog/0.7.1
|
||||||
|
benchmark/1.9.1
|
||||||
|
boost/1.84.0
|
||||||
|
|
||||||
|
[options]
|
||||||
|
boost/*:shared = False
|
||||||
|
boost/*:header_only = False
|
||||||
|
boost/*:without_atomic = False
|
||||||
|
boost/*:without_lockfree = False
|
||||||
|
boost/*:without_* = True
|
||||||
|
|
||||||
|
[generators]
|
||||||
|
CMakeDeps
|
||||||
|
CMakeToolchain
|
||||||
|
[layout]
|
||||||
|
cmake_layout
|
||||||
59
include/logger.hh
Normal file
59
include/logger.hh
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <atomic>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <variant>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace vptyp {
|
||||||
|
|
||||||
|
static constexpr std::string_view configErrorMsg =
|
||||||
|
"Bruh, incorrect configuration";
|
||||||
|
|
||||||
|
class Logger {
|
||||||
|
// helper class for handling worker thread
|
||||||
|
class Worker;
|
||||||
|
|
||||||
|
using map_type =
|
||||||
|
std::unordered_map<std::string, std::variant<int64_t, double>>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual ~Logger();
|
||||||
|
Logger();
|
||||||
|
Logger(Logger&) = delete;
|
||||||
|
Logger(Logger&&) = delete;
|
||||||
|
|
||||||
|
/// must be called only through initializer list
|
||||||
|
/// @return success or not (already configured)
|
||||||
|
bool configure(const std::vector<std::string>& d,
|
||||||
|
std::ostream& out = std::cout);
|
||||||
|
|
||||||
|
template <typename Metric,
|
||||||
|
typename = std::enable_if_t<std::is_arithmetic_v<Metric>>>
|
||||||
|
void add(std::string field, Metric metric) {
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto locked = active.load();
|
||||||
|
auto it = locked->find(field);
|
||||||
|
if (it == locked->end()) {
|
||||||
|
throw configErrorMsg;
|
||||||
|
}
|
||||||
|
it->second = metric;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isConfigured() { return configured > CONFIGURED; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr int CONFIGURED = 2;
|
||||||
|
static constexpr int CONFIG_IN_PROGRESS = 1;
|
||||||
|
static constexpr int NOT_CONFIGURED = 0;
|
||||||
|
std::atomic<int> configured{0};
|
||||||
|
std::unique_ptr<Worker> worker;
|
||||||
|
map_type m1, m2;
|
||||||
|
std::atomic<std::shared_ptr<map_type>> active; // impl using mutexes!
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace vptyp
|
||||||
47
src/logger.cc
Normal file
47
src/logger.cc
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#include "logger.hh"
|
||||||
|
#include <thread>
|
||||||
|
namespace vptyp {
|
||||||
|
|
||||||
|
bool Logger::configure(const std::vector<std::string>& d, std::ostream& out) {
|
||||||
|
int tmp = configured.load();
|
||||||
|
if (tmp || !configured.compare_exchange_weak(tmp, CONFIG_IN_PROGRESS))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (auto& key : d) {
|
||||||
|
m1[key] = 0;
|
||||||
|
}
|
||||||
|
m2 = m1;
|
||||||
|
configured.store(CONFIGURED);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger::Logger() {}
|
||||||
|
Logger::~Logger() = default;
|
||||||
|
|
||||||
|
class Logger::Worker {
|
||||||
|
public:
|
||||||
|
explicit Worker(decltype(Logger::active)& active_ref,
|
||||||
|
Logger& father,
|
||||||
|
std::ostream& out)
|
||||||
|
: active_ref(active_ref), parent(father), out(out) {
|
||||||
|
thread = std::jthread([this] { routine(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
~Worker() { state = STOPPING; }
|
||||||
|
|
||||||
|
void routine() {
|
||||||
|
while (state == RUNNING) {
|
||||||
|
// Do something
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum State { RUNNING, STOPPING } state;
|
||||||
|
Logger& parent;
|
||||||
|
std::ostream& out;
|
||||||
|
decltype(Logger::active)& active_ref;
|
||||||
|
std::jthread thread;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace vptyp
|
||||||
13
tests/CMakeLists.txt
Normal file
13
tests/CMakeLists.txt
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
find_package(GTest REQUIRED)
|
||||||
|
|
||||||
|
find_package(Boost REQUIRED COMPONENTS thread)
|
||||||
|
|
||||||
|
set(NAME ${PROJECT_NAME}_tests)
|
||||||
|
|
||||||
|
add_executable(${NAME} main.cc)
|
||||||
|
|
||||||
|
target_link_libraries(${NAME} PRIVATE
|
||||||
|
GTest::gtest
|
||||||
|
Boost::thread
|
||||||
|
${PROJECT_NAME}
|
||||||
|
)
|
||||||
111
tests/main.cc
Normal file
111
tests/main.cc
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <boost/lockfree/queue.hpp>
|
||||||
|
#include <logger.hh>
|
||||||
|
#include <queue>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SingleThread, Add) {
|
||||||
|
vptyp::Logger l;
|
||||||
|
std::ostringstream s;
|
||||||
|
std::queue<std::pair<std::string, int64_t>> prev;
|
||||||
|
|
||||||
|
l.configure({"apprx", "size", "time"}, s);
|
||||||
|
|
||||||
|
auto decorator = [&l, &prev](std::string field, int value) {
|
||||||
|
l.add(field, value);
|
||||||
|
prev.push({field, value});
|
||||||
|
};
|
||||||
|
|
||||||
|
decorator("size", 1);
|
||||||
|
decorator("time", 1);
|
||||||
|
sleep(1);
|
||||||
|
decorator("apprx", 12);
|
||||||
|
decorator("size", 2);
|
||||||
|
sleep(1);
|
||||||
|
|
||||||
|
// results
|
||||||
|
auto d = s.rdbuf()->view();
|
||||||
|
std::string_view view;
|
||||||
|
while (view = d.substr(0, d.find('\n')), view.size() > 0) {
|
||||||
|
if (prev.empty())
|
||||||
|
break;
|
||||||
|
size_t res{0};
|
||||||
|
while (res != view.npos) {
|
||||||
|
auto nextAwaiting = prev.front();
|
||||||
|
res = view.find(nextAwaiting.first);
|
||||||
|
if (res != view.npos)
|
||||||
|
prev.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EXPECT_EQ(prev.empty(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MultiThread, Configure) {
|
||||||
|
vptyp::Logger l;
|
||||||
|
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) {
|
||||||
|
vptyp::Logger l;
|
||||||
|
std::ostringstream s;
|
||||||
|
std::mutex mtx;
|
||||||
|
std::queue<std::pair<std::string, int64_t>> prev;
|
||||||
|
l.configure({"apprx", "size", "time"}, s);
|
||||||
|
|
||||||
|
auto decorator = [&l, &prev, &mtx](std::string field, int value) {
|
||||||
|
for (auto i = 0; i < 100; ++i) {
|
||||||
|
l.add(field, value);
|
||||||
|
{
|
||||||
|
std::lock_guard g(mtx);
|
||||||
|
prev.push({field, value});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<std::jthread> threads(10);
|
||||||
|
for (auto& thread : threads) {
|
||||||
|
thread = std::jthread(decorator, "apprx", rand() % 100);
|
||||||
|
}
|
||||||
|
threads.clear();
|
||||||
|
sleep(1);
|
||||||
|
|
||||||
|
// results
|
||||||
|
auto d = s.rdbuf()->view();
|
||||||
|
std::string_view view;
|
||||||
|
while (view = d.substr(0, d.find('\n')), view.size() > 0) {
|
||||||
|
if (prev.empty())
|
||||||
|
break;
|
||||||
|
size_t res{0};
|
||||||
|
while (res != view.npos) {
|
||||||
|
auto nextAwaiting = prev.front();
|
||||||
|
res = view.find(nextAwaiting.first);
|
||||||
|
if (res != view.npos)
|
||||||
|
prev.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EXPECT_EQ(prev.empty(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
testing::InitGoogleTest(&argc, argv);
|
||||||
|
return RUN_ALL_TESTS();
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user