11#include <userver/formats/json/inline.hpp>
12#include <userver/formats/serialize/to.hpp>
13#include <userver/utils/assert.hpp>
14#include <userver/utils/statistics/writer.hpp>
16USERVER_NAMESPACE_BEGIN
18namespace utils::statistics {
22template <
typename ValueType,
typename AverageType = ValueType>
23class MinMaxAvg final {
24 static_assert(std::is_integral_v<ValueType> &&
25 !std::is_same_v<ValueType,
bool>,
26 "only integral value types are supported in MinMaxAvg");
27 static_assert(std::is_same_v<AverageType, ValueType> ||
28 std::is_floating_point_v<AverageType>,
29 "MinMaxAvg average type must either be equal to value type or "
30 "be a floating point type");
31 static_assert(std::atomic<ValueType>::is_always_lock_free &&
32 std::atomic<size_t>::is_always_lock_free,
33 "refusing to use locking atomics");
42 constexpr MinMaxAvg()
noexcept
43 : minimum_(0), maximum_(0), sum_(0), count_(0) {}
46 MinMaxAvg(
const MinMaxAvg& other)
noexcept { *
this = other; }
48 MinMaxAvg& operator=(
const MinMaxAvg& rhs)
noexcept {
49 if (
this == &rhs)
return *
this;
51 const auto count = rhs.count_.load(std::memory_order_acquire);
52 minimum_ = rhs.minimum_.load(std::memory_order_relaxed);
53 maximum_ = rhs.maximum_.load(std::memory_order_relaxed);
54 sum_ = rhs.sum_.load(std::memory_order_relaxed);
62 const auto count = count_.load(std::memory_order_acquire);
64 current.minimum = minimum_.load(std::memory_order_relaxed);
65 current.maximum = maximum_.load(std::memory_order_relaxed);
67 count ?
static_cast<AverageType>(sum_.load(std::memory_order_relaxed)) /
68 static_cast<AverageType>(count)
73 void Account(ValueType value) {
74 ValueType current_minimum = minimum_.load(std::memory_order_relaxed);
75 while (current_minimum > value || !count_.load(std::memory_order_relaxed)) {
76 if (minimum_.compare_exchange_weak(current_minimum, value,
77 std::memory_order_relaxed)) {
81 ValueType current_maximum = maximum_.load(std::memory_order_relaxed);
82 while (current_maximum < value || !count_.load(std::memory_order_relaxed)) {
83 if (maximum_.compare_exchange_weak(current_maximum, value,
84 std::memory_order_relaxed)) {
88 sum_.fetch_add(value, std::memory_order_relaxed);
89 count_.fetch_add(1, std::memory_order_release);
92 template <
class Duration = std::chrono::seconds>
93 void Add(
const MinMaxAvg& other,
94 [[maybe_unused]] Duration this_epoch_duration = Duration(),
95 [[maybe_unused]] Duration before_this_epoch_duration = Duration()) {
96 ValueType current_minimum = minimum_.load(std::memory_order_relaxed);
97 while (current_minimum > other.minimum_.load(std::memory_order_relaxed) ||
98 !count_.load(std::memory_order_relaxed)) {
99 if (minimum_.compare_exchange_weak(
100 current_minimum, other.minimum_.load(std::memory_order_relaxed),
101 std::memory_order_relaxed)) {
105 ValueType current_maximum = maximum_.load(std::memory_order_relaxed);
106 while (current_maximum < other.maximum_.load(std::memory_order_relaxed) ||
107 !count_.load(std::memory_order_relaxed)) {
108 if (maximum_.compare_exchange_weak(
109 current_maximum, other.maximum_.load(std::memory_order_relaxed),
110 std::memory_order_relaxed)) {
114 sum_.fetch_add(other.sum_.load(std::memory_order_relaxed),
115 std::memory_order_relaxed);
116 count_.fetch_add(other.count_.load(std::memory_order_acquire),
117 std::memory_order_release);
121 minimum_.store(0, std::memory_order_relaxed);
122 maximum_.store(0, std::memory_order_relaxed);
123 sum_.store(0, std::memory_order_relaxed);
127 bool IsEmpty()
const noexcept {
return count_.load() == 0; }
130 std::atomic<ValueType> minimum_;
131 std::atomic<ValueType> maximum_;
132 std::atomic<ValueType> sum_;
133 std::atomic<ssize_t> count_;
136template <
typename ValueType,
typename AverageType>
137auto Serialize(
const MinMaxAvg<ValueType, AverageType>& mma,
139 const auto current = mma.GetCurrent();
140 return formats::
json::MakeObject(
"min", current.minimum,
"max",
141 current.maximum,
"avg", current.average);
144template <
typename ValueType,
typename AverageType>
145void DumpMetric(Writer& writer,
146 const MinMaxAvg<ValueType, AverageType>& value) {
147 const auto current = value.GetCurrent();
148 writer[
"min"] = current.minimum;
149 writer[
"max"] = current.maximum;
150 writer[
"avg"] = current.average;