23class MinMaxAvg final {
25 std::is_integral_v<ValueType> && !std::is_same_v<ValueType,
bool>,
26 "only integral value types are supported in MinMaxAvg"
29 std::is_same_v<AverageType, ValueType> || std::is_floating_point_v<AverageType>,
30 "MinMaxAvg average type must either be equal to value type or "
31 "be a floating point type"
34 std::atomic<ValueType>::is_always_lock_free && std::atomic<size_t>::is_always_lock_free,
35 "refusing to use locking atomics"
45 constexpr MinMaxAvg()
noexcept : minimum_(0), maximum_(0), sum_(0), count_(0) {}
48 MinMaxAvg(
const MinMaxAvg& other)
noexcept { *
this = other; }
50 MinMaxAvg& operator=(
const MinMaxAvg& rhs)
noexcept {
55 const auto count = rhs.count_.load(std::memory_order_acquire);
56 minimum_ = rhs.minimum_.load(std::memory_order_relaxed);
57 maximum_ = rhs.maximum_.load(std::memory_order_relaxed);
58 sum_ = rhs.sum_.load(std::memory_order_relaxed);
66 const auto count = count_.load(std::memory_order_acquire);
68 current.minimum = minimum_.load(std::memory_order_relaxed);
69 current.maximum = maximum_.load(std::memory_order_relaxed);
71 count ?
static_cast<AverageType>(sum_.load(std::memory_order_relaxed)) /
static_cast<AverageType>(count)
76 void Account(ValueType value) {
77 ValueType current_minimum = minimum_.load(std::memory_order_relaxed);
78 while (current_minimum > value || !count_.load(std::memory_order_relaxed)) {
79 if (minimum_.compare_exchange_weak(current_minimum, value, std::memory_order_relaxed)) {
83 ValueType current_maximum = maximum_.load(std::memory_order_relaxed);
84 while (current_maximum < value || !count_.load(std::memory_order_relaxed)) {
85 if (maximum_.compare_exchange_weak(current_maximum, value, std::memory_order_relaxed)) {
89 sum_.fetch_add(value, std::memory_order_relaxed);
90 count_.fetch_add(1, std::memory_order_release);
93 template <
class Duration = std::chrono::seconds>
95 const MinMaxAvg& other,
96 [[maybe_unused]] Duration this_epoch_duration = Duration(),
97 [[maybe_unused]] Duration before_this_epoch_duration = Duration()
99 ValueType current_minimum = minimum_.load(std::memory_order_relaxed);
100 while (current_minimum > other.minimum_.load(std::memory_order_relaxed) ||
101 !count_.load(std::memory_order_relaxed))
103 if (minimum_.compare_exchange_weak(
105 other.minimum_.load(std::memory_order_relaxed),
106 std::memory_order_relaxed
112 ValueType current_maximum = maximum_.load(std::memory_order_relaxed);
113 while (current_maximum < other.maximum_.load(std::memory_order_relaxed) ||
114 !count_.load(std::memory_order_relaxed))
116 if (maximum_.compare_exchange_weak(
118 other.maximum_.load(std::memory_order_relaxed),
119 std::memory_order_relaxed
125 sum_.fetch_add(other.sum_.load(std::memory_order_relaxed), std::memory_order_relaxed);
126 count_.fetch_add(other.count_.load(std::memory_order_acquire), std::memory_order_release);
130 minimum_.store(0, std::memory_order_relaxed);
131 maximum_.store(0, std::memory_order_relaxed);
132 sum_.store(0, std::memory_order_relaxed);
136 bool IsEmpty()
const noexcept {
return count_.load() == 0; }
139 std::atomic<ValueType> minimum_;
140 std::atomic<ValueType> maximum_;
141 std::atomic<ValueType> sum_;
142 std::atomic<ssize_t> count_;