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_;
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}
224 .ValueWithLabels(perc.GetPercentile(percent), {
"percentile", statistics::GetPercentileFieldName(percent)});