11#include <userver/utils/statistics/writer.hpp>
13USERVER_NAMESPACE_BEGIN
15namespace utils::statistics {
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54template <size_t M,
typename Counter = uint32_t, size_t ExtraBuckets = 0,
55 size_t ExtraBucketSize = 500>
56class Percentile final {
59 for (
auto& value : values_) value.store(0, std::memory_order_relaxed);
60 for (
auto& value : extra_values_) value.store(0, std::memory_order_relaxed);
61 count_.store(0, std::memory_order_release);
64 Percentile(
const Percentile<M, Counter, ExtraBuckets, ExtraBucketSize>&
69 Percentile& operator=(
const Percentile& rhs)
noexcept {
70 if (
this == &rhs)
return *
this;
73 for (size_t i = 0; i < values_.size(); i++) {
74 const auto value = rhs.values_[i].load(std::memory_order_relaxed);
75 values_[i].store(value, std::memory_order_relaxed);
79 for (size_t i = 0; i < extra_values_.size(); i++) {
80 const auto value = rhs.extra_values_[i].load(std::memory_order_relaxed);
81 extra_values_[i].store(value, std::memory_order_relaxed);
89
90
92 if (value < values_.size()) {
93 values_[value].fetch_add(1, std::memory_order_relaxed);
95 if (!extra_values_.empty()) {
97 (value - values_.size() + ExtraBucketSize / 2) / ExtraBucketSize;
98 if (extra_bucket >= extra_values_.size()) {
99 extra_bucket = extra_values_.size() - 1;
101 extra_values_[extra_bucket].fetch_add(1, std::memory_order_relaxed);
103 values_.back().fetch_add(1, std::memory_order_relaxed);
106 count_.fetch_add(1, std::memory_order_release);
110
111
112
113
114
116 if (count_ == 0)
return 0;
119 size_t want_sum = count_.load(std::memory_order_acquire) * percent;
120 size_t max_value = 0;
121 for (size_t i = 0; i < values_.size(); i++) {
122 const auto value = values_[i].load(std::memory_order_relaxed);
124 if (sum * 100 > want_sum)
return i;
126 if (value) max_value = i;
129 for (size_t i = 0; i < extra_values_.size(); i++) {
130 const auto value = extra_values_[i].load(std::memory_order_relaxed);
132 if (sum * 100 > want_sum)
return ExtraBucketToValue(i);
134 if (value) max_value = ExtraBucketToValue(i);
140 template <
class Duration = std::chrono::seconds>
141 void Add(
const Percentile<M, Counter, ExtraBuckets, ExtraBucketSize>& other,
142 [[maybe_unused]] Duration this_epoch_duration = Duration(),
143 [[maybe_unused]] Duration before_this_epoch_duration = Duration()) {
145 for (size_t i = 0; i < values_.size(); i++) {
146 const auto value = other.values_[i].load(std::memory_order_relaxed);
148 values_[i].fetch_add(value, std::memory_order_relaxed);
151 for (size_t i = 0; i < extra_values_.size(); i++) {
152 const auto value = other.extra_values_[i].load(std::memory_order_relaxed);
154 extra_values_[i].fetch_add(value, std::memory_order_relaxed);
156 count_.fetch_add(sum, std::memory_order_release);
160 for (
auto& value : values_) value.store(0, std::memory_order_relaxed);
161 for (
auto& value : extra_values_) value.store(0, std::memory_order_relaxed);
166
167 Counter
Count()
const {
return count_; }
170 size_t ExtraBucketToValue(size_t bucket)
const {
171 return values_.size() + bucket * ExtraBucketSize;
174 std::array<std::atomic<Counter>, M> values_;
175 std::array<std::atomic<Counter>, ExtraBuckets> extra_values_;
176 std::atomic<Counter> count_;
179std::string GetPercentileFieldName(
double perc);
181template <size_t M,
typename Counter, size_t ExtraBuckets,
182 size_t ExtraBucketSize>
185 const Percentile<M, Counter, ExtraBuckets, ExtraBucketSize>& perc,
186 std::initializer_list<
double> percents = {0, 50, 90, 95, 98, 99, 99.6, 99.9,
188 for (
double percent : percents) {
189 writer.ValueWithLabels(
190 perc.GetPercentile(percent),
191 {
"percentile", statistics::GetPercentileFieldName(percent)});