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
54
55
56
57
58
59
60
61
62
63
66 typename Counter = std::uint32_t,
67 std::size_t ExtraBuckets = 0,
68 std::size_t ExtraBucketSize = 500>
69class Percentile final {
71 Percentile()
noexcept {
72 for (
auto& value : values_) value.store(0, std::memory_order_relaxed);
73 for (
auto& value : extra_values_) value.store(0, std::memory_order_relaxed);
74 count_.store(0, std::memory_order_release);
77 Percentile(
const Percentile<M, Counter, ExtraBuckets, ExtraBucketSize>& other)
noexcept { *
this = other; }
79 Percentile& operator=(
const Percentile& rhs)
noexcept {
80 if (
this == &rhs)
return *
this;
83 for (std::size_t i = 0; i < values_.size(); i++) {
84 const auto value = rhs.values_[i].load(std::memory_order_relaxed);
85 values_[i].store(value, std::memory_order_relaxed);
89 for (std::size_t i = 0; i < extra_values_.size(); i++) {
90 const auto value = rhs.extra_values_[i].load(std::memory_order_relaxed);
91 extra_values_[i].store(value, std::memory_order_relaxed);
103 if (value < values_.size()) {
104 values_[value].fetch_add(1, std::memory_order_relaxed);
106 if (!extra_values_.empty()) {
107 std::size_t extra_bucket = (value - values_.size() + ExtraBucketSize / 2) / ExtraBucketSize;
108 if (extra_bucket >= extra_values_.size()) {
109 extra_bucket = extra_values_.size() - 1;
111 extra_values_[extra_bucket].fetch_add(1, std::memory_order_relaxed);
113 values_.back().fetch_add(1, std::memory_order_relaxed);
116 count_.fetch_add(1, std::memory_order_release);
125 if (count_ == 0)
return 0;
128 std::size_t want_sum = count_.load(std::memory_order_acquire) * percent;
129 std::size_t max_value = 0;
130 for (std::size_t i = 0; i < values_.size(); i++) {
131 const auto value = values_[i].load(std::memory_order_relaxed);
133 if (sum * 100 > want_sum)
return i;
135 if (value) max_value = i;
138 for (size_t i = 0; i < extra_values_.size(); i++) {
139 const auto value = extra_values_[i].load(std::memory_order_relaxed);
141 if (sum * 100 > want_sum)
return ExtraBucketToValue(i);
143 if (value) max_value = ExtraBucketToValue(i);
149 template <
class Duration = std::chrono::seconds>
151 const Percentile<M, Counter, ExtraBuckets, ExtraBucketSize>& other,
152 [[maybe_unused]] Duration this_epoch_duration = Duration(),
153 [[maybe_unused]] Duration before_this_epoch_duration = Duration()
156 for (size_t i = 0; i < values_.size(); i++) {
157 const auto value = other.values_[i].load(std::memory_order_relaxed);
159 values_[i].fetch_add(value, std::memory_order_relaxed);
162 for (size_t i = 0; i < extra_values_.size(); i++) {
163 const auto value = other.extra_values_[i].load(std::memory_order_relaxed);
165 extra_values_[i].fetch_add(value, std::memory_order_relaxed);
167 count_.fetch_add(sum, std::memory_order_release);
172 for (
auto& value : values_) value.store(0, std::memory_order_relaxed);
173 for (
auto& value : extra_values_) value.store(0, std::memory_order_relaxed);
178 Counter
Count()
const noexcept {
return count_; }
181 size_t ExtraBucketToValue(std::size_t bucket)
const noexcept {
return values_.size() + bucket * ExtraBucketSize; }
184 std::atomic<Counter>::is_always_lock_free,
185 "`std::atomic<Counter>` is not lock-free. Please choose some "
186 "other `Counter` type"
189 std::array<std::atomic<Counter>, M> values_;
190 std::array<std::atomic<Counter>, ExtraBuckets> extra_values_;
191 std::atomic<Counter> count_;
194std::string GetPercentileFieldName(
double perc);
196template <size_t M,
typename Counter, size_t ExtraBuckets, size_t ExtraBucketSize>
199 const Percentile<M, Counter, ExtraBuckets, ExtraBucketSize>& perc,
200 std::initializer_list<
double> percents = {0, 50, 90, 95, 98, 99, 99.6, 99.9, 100}
202 for (
double percent : percents) {
203 writer.ValueWithLabels(
204 perc.GetPercentile(percent), {
"percentile", statistics::GetPercentileFieldName(percent)}