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_) {
73 value.store(0, std::memory_order_relaxed);
75 for (
auto& value : extra_values_) {
76 value.store(0, std::memory_order_relaxed);
78 count_.store(0, std::memory_order_release);
81 Percentile(
const Percentile<M, Counter, ExtraBuckets, ExtraBucketSize>& other)
noexcept { *
this = other; }
83 Percentile& operator=(
const Percentile& rhs)
noexcept {
89 for (std::size_t i = 0; i < values_.size(); i++) {
90 const auto value = rhs.values_[i].load(std::memory_order_relaxed);
91 values_[i].store(value, std::memory_order_relaxed);
95 for (std::size_t i = 0; i < extra_values_.size(); i++) {
96 const auto value = rhs.extra_values_[i].load(std::memory_order_relaxed);
97 extra_values_[i].store(value, std::memory_order_relaxed);
109 if (value < values_.size()) {
110 values_[value].fetch_add(1, std::memory_order_relaxed);
112 if (!extra_values_.empty()) {
113 std::size_t extra_bucket = (value - values_.size() + ExtraBucketSize / 2) / ExtraBucketSize;
114 if (extra_bucket >= extra_values_.size()) {
115 extra_bucket = extra_values_.size() - 1;
117 extra_values_[extra_bucket].fetch_add(1, std::memory_order_relaxed);
119 values_.back().fetch_add(1, std::memory_order_relaxed);
122 count_.fetch_add(1, std::memory_order_release);
136 const std::size_t want_sum = count_.load(std::memory_order_acquire) * percent;
137 std::size_t max_value = 0;
138 for (std::size_t i = 0; i < values_.size(); i++) {
139 const auto value = values_[i].load(std::memory_order_relaxed);
141 if (sum * 100 > want_sum) {
150 for (size_t i = 0; i < extra_values_.size(); i++) {
151 const auto value = extra_values_[i].load(std::memory_order_relaxed);
153 if (sum * 100 > want_sum) {
154 return ExtraBucketToValue(i);
158 max_value = ExtraBucketToValue(i);
165 template <
class Duration = std::chrono::seconds>
167 const Percentile<M, Counter, ExtraBuckets, ExtraBucketSize>& other,
168 [[maybe_unused]] Duration this_epoch_duration = Duration(),
169 [[maybe_unused]] Duration before_this_epoch_duration = Duration()
172 for (size_t i = 0; i < values_.size(); i++) {
173 const auto value = other.values_[i].load(std::memory_order_relaxed);
175 values_[i].fetch_add(value, std::memory_order_relaxed);
178 for (size_t i = 0; i < extra_values_.size(); i++) {
179 const auto value = other.extra_values_[i].load(std::memory_order_relaxed);
181 extra_values_[i].fetch_add(value, std::memory_order_relaxed);
183 count_.fetch_add(sum, std::memory_order_release);
188 for (
auto& value : values_) {
189 value.store(0, std::memory_order_relaxed);
191 for (
auto& value : extra_values_) {
192 value.store(0, std::memory_order_relaxed);
198 Counter
Count()
const noexcept {
return count_; }
201 size_t ExtraBucketToValue(std::size_t bucket)
const noexcept {
return values_.size() + bucket * ExtraBucketSize; }
204 std::atomic<Counter>::is_always_lock_free,
205 "`std::atomic<Counter>` is not lock-free. Please choose some "
206 "other `Counter` type"
209 std::array<std::atomic<Counter>, M> values_;
210 std::array<std::atomic<Counter>, ExtraBuckets> extra_values_;
211 std::atomic<Counter> count_;
214std::string GetPercentileFieldName(
double perc);
216template <size_t M,
typename Counter, size_t ExtraBuckets, size_t ExtraBucketSize>
219 const Percentile<M, Counter, ExtraBuckets, ExtraBucketSize>& perc,
220 std::initializer_list<
double> percents = {0, 50, 90, 95, 98, 99, 99.6, 99.9, 100}
222 for (
const double percent : percents) {
224 .ValueWithLabels(perc.GetPercentile(percent), {
"percentile", statistics::GetPercentileFieldName(percent)});