memory_orders on load operations

:Release Notes:
removed jthread, either way explicit join
additional bench test (separate mutex and unordered map)

:Detailed Notes:
-

:Testing Performed:
just tests/bench run - output is correct, no data races detected
(-fsanitize=thread)

:QA Notes:
-

:Issues Addressed:
-
This commit is contained in:
Artur Mukhamadiev 2025-06-23 15:17:49 +03:00
parent b958d407f1
commit 4c008aa77c
4 changed files with 38 additions and 10 deletions

4
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,4 @@
{
"editor.defaultFormatter": "xaver.clang-format",
"editor.formatOnSave": true
}

View File

@ -94,23 +94,35 @@ static void BM_taylor_atomic_upd(benchmark::State& state) {
}
}
void updValue(double res, const std::string& key) {
void updMapValue(double res, const std::string& key) {
static std::unordered_map<std::string, double> d = {{"key", 10.0},
{"assembly", 11.0},
{"draw", 123.3},
{"d2", 0.0},
{"d55", 0.23}};
d[key] = res;
}
static void BM_taylor_map_upd(benchmark::State& state) {
for (auto _ : state) {
double res = calc_exp_taylor();
updMapValue(res, "assembly");
}
}
void updValue(double res) {
static double d;
static std::mutex mtx;
{
std::lock_guard lock(mtx);
d[key] = res;
d = res;
}
}
static void BM_taylor_mutex_upd(benchmark::State& state) {
for (auto _ : state) {
double res = calc_exp_taylor();
updValue(res, "assembly");
updValue(res);
}
}
@ -119,6 +131,7 @@ BENCHMARK(BM_taylor_logger);
BENCHMARK(BM_taylor_glog);
BENCHMARK(BM_taylor_atomic_upd);
BENCHMARK(BM_taylor_mutex_upd);
BENCHMARK(BM_taylor_map_upd);
std::string caesar_encoder(const std::string& input) {
static constexpr int small = 'a';

View File

@ -22,10 +22,13 @@ class Logger {
public:
virtual ~Logger();
Logger();
explicit Logger(std::ostream& out);
Logger() = delete;
Logger(Logger&) = delete;
Logger(Logger&&) = delete;
Logger& operator=(Logger&) = delete;
Logger& operator=(Logger&&) = delete;
/// @return success or not (already configured)
bool configure(const std::vector<std::string>& d);
@ -33,11 +36,11 @@ class Logger {
template <typename Metric,
typename = std::enable_if_t<std::is_arithmetic_v<Metric>>>
void add(const std::string& field, Metric metric) {
auto locked = active.load();
auto locked = active.load(std::memory_order_acquire);
// auto locked = active;
auto it = locked->find(field);
if (it == locked->end()) {
throw configErrorMsg;
throw configErrorMsg; // additional 30ns on bench ?
}
it->second = metric;
}
@ -51,7 +54,8 @@ class Logger {
std::unique_ptr<Worker> worker;
std::shared_ptr<map_type> m1, m2;
std::atomic<std::shared_ptr<map_type>> active; // impl may use mutex!
// std::shared_ptr<map_type> active;
// std::shared_ptr<map_type> active; // data race without atomic on swap
// operation
};
} // namespace vptyp

View File

@ -1,4 +1,6 @@
#include "logger.hh"
#include <atomic>
#include <memory>
#include <thread>
namespace vptyp {
@ -18,6 +20,11 @@ bool Logger::configure(const std::vector<std::string>& d) {
return true;
}
Logger::Logger()
: m1(std::make_shared<map_type>()), m2(std::make_shared<map_type>()) {
worker = std::make_unique<Logger::Worker>(*this, std::cout);
}
Logger::Logger(std::ostream& out)
: m1(std::make_shared<map_type>()), m2(std::make_shared<map_type>()) {
worker = std::make_unique<Logger::Worker>(*this, out);
@ -31,7 +38,7 @@ class Logger::Worker {
public:
explicit Worker(Logger& father, std::ostream& out)
: parent(father), out(out) {
thread = std::jthread([this] { routine(); });
thread = std::thread([this] { routine(); });
}
~Worker() {
@ -54,14 +61,14 @@ class Logger::Worker {
std::atomic<State> state;
Logger& parent;
std::ostream& out;
std::jthread thread;
std::thread thread; // jthread not needed, as we anyway must wait for join
};
void Logger::Worker::unroll() {
if (!parent.isConfigured())
return;
auto tmp = parent.active.load();
auto tmp = parent.active.load(std::memory_order_acquire);
// auto tmp = parent.active;
auto toBeActive = tmp == parent.m1 ? parent.m2 : parent.m1;
while (!parent.active.compare_exchange_weak(tmp, toBeActive)) {