include/ylt/metric/dynamic_metric.hpp (120 lines of code) (raw):
#pragma once
#include <thread>
#include "metric.hpp"
#include "thread_local_value.hpp"
#include "ylt/util/map_sharded.hpp"
namespace ylt::metric {
class dynamic_metric : public metric_t {
public:
static inline auto g_user_metric_label_count =
new thread_local_value<int64_t>(std::thread::hardware_concurrency());
using metric_t::metric_t;
};
template <typename core_type, uint8_t N>
class dynamic_metric_impl : public dynamic_metric {
template <size_t seed = 131>
struct my_hash {
using is_transparent = void;
std::size_t operator()(
const std::span<const std::string, N>& s) const noexcept {
unsigned int hash = 0;
for (const auto& str : s) {
for (auto ch : str) {
hash = hash * seed + ch;
}
}
return hash;
}
std::size_t operator()(
const std::span<std::string_view, N>& s) const noexcept {
unsigned int hash = 0;
for (const auto& str : s) {
for (auto ch : str) {
hash = hash * seed + ch;
}
}
return hash;
}
};
struct my_equal {
bool operator()(const std::span<const std::string, N>& s1,
const std::span<const std::string, N>& s2) const noexcept {
if constexpr (N > 0) {
for (int i = 0; i < N; ++i) {
if (s1[i] != s2[i]) {
return false;
}
}
}
return true;
}
};
using key_type = std::array<std::string, N>;
struct metric_pair {
public:
key_type label;
core_type value;
template <typename T, typename... Args>
metric_pair(T&& first, Args&&... args)
: label(std::forward<T>(first)), value(std::forward<Args>(args)...) {
g_user_metric_label_count->inc();
if (ylt_label_max_age.count()) {
tp = std::chrono::steady_clock::now();
}
}
std::chrono::steady_clock::time_point get_created_time() const {
return tp;
}
private:
std::chrono::steady_clock::time_point tp;
};
struct value_type : public std::shared_ptr<metric_pair> {
value_type() : std::shared_ptr<metric_pair>(nullptr) {}
template <typename... Args>
value_type(Args&&... args)
: std::shared_ptr<metric_pair>(
std::make_shared<metric_pair>(std::forward<Args>(args)...)){};
};
public:
using dynamic_metric::dynamic_metric;
size_t size() const { return map_.size(); }
size_t empty() const { return !size(); }
size_t label_value_count() const { return size(); }
std::vector<std::shared_ptr<metric_pair>> copy() const {
return map_.template copy<std::shared_ptr<metric_pair>>();
}
void clean_expired_label() override {
erase_if([now = std::chrono::steady_clock::now()](auto& pair) mutable {
bool r = std::chrono::duration_cast<std::chrono::seconds>(
now - pair.second->get_created_time())
.count() >= ylt_label_max_age.count();
return r;
});
}
protected:
template <typename Key, typename... Args>
std::pair<std::shared_ptr<metric_pair>, bool> try_emplace(Key&& key,
Args&&... args) {
std::span<const std::string, N> view = key;
return map_.try_emplace_with_op(
view,
[](auto result) {
if (result.second) {
*const_cast<std::span<const std::string, N>*>(
&result.first->first) = result.first->second->label;
}
},
std::forward<Key>(key), std::forward<Args>(args)...);
}
std::shared_ptr<metric_pair> find(std::span<const std::string, N> key) const {
return map_.find(key);
}
size_t erase(std::span<const std::string, N> key) { return map_.erase(key); }
size_t erase_if(auto&& op) { return map_.erase_if(op); }
private:
util::map_sharded_t<std::unordered_map<std::span<const std::string, N>,
value_type, my_hash<131>, my_equal>,
my_hash<137>>
map_{std::min<unsigned>(128u, std::thread::hardware_concurrency())};
};
} // namespace ylt::metric