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
   64template <std::size_t M, 
typename Counter = std::uint32_t,
 
   65          std::size_t ExtraBuckets = 0, std::size_t ExtraBucketSize = 500>
 
   66class Percentile final {
 
   68  Percentile() 
noexcept {
 
   69    for (
auto& value : values_) value.store(0, std::memory_order_relaxed);
 
   70    for (
auto& value : extra_values_) value.store(0, std::memory_order_relaxed);
 
   71    count_.store(0, std::memory_order_release);
 
   74  Percentile(
const Percentile<M, Counter, ExtraBuckets, ExtraBucketSize>&
 
   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 =
 
  108            (value - values_.size() + ExtraBucketSize / 2) / ExtraBucketSize;
 
  109        if (extra_bucket >= extra_values_.size()) {
 
  110          extra_bucket = extra_values_.size() - 1;
 
  112        extra_values_[extra_bucket].fetch_add(1, std::memory_order_relaxed);
 
  114        values_.back().fetch_add(1, std::memory_order_relaxed);
 
  117    count_.fetch_add(1, std::memory_order_release);
 
  126    if (count_ == 0) 
return 0;
 
  129    std::size_t want_sum = count_.load(std::memory_order_acquire) * percent;
 
  130    std::size_t max_value = 0;
 
  131    for (std::size_t i = 0; i < values_.size(); i++) {
 
  132      const auto value = values_[i].load(std::memory_order_relaxed);
 
  134      if (sum * 100 > want_sum) 
return i;
 
  136      if (value) max_value = i;
 
  139    for (size_t i = 0; i < extra_values_.size(); i++) {
 
  140      const auto value = extra_values_[i].load(std::memory_order_relaxed);
 
  142      if (sum * 100 > want_sum) 
return ExtraBucketToValue(i);
 
  144      if (value) max_value = ExtraBucketToValue(i);
 
  150  template <
class Duration = std::chrono::seconds>
 
  151  void Add(
const Percentile<M, Counter, ExtraBuckets, ExtraBucketSize>& other,
 
  152           [[maybe_unused]] Duration this_epoch_duration = Duration(),
 
  153           [[maybe_unused]] Duration before_this_epoch_duration = Duration()) {
 
  155    for (size_t i = 0; i < values_.size(); i++) {
 
  156      const auto value = other.values_[i].load(std::memory_order_relaxed);
 
  158      values_[i].fetch_add(value, std::memory_order_relaxed);
 
  161    for (size_t i = 0; i < extra_values_.size(); i++) {
 
  162      const auto value = other.extra_values_[i].load(std::memory_order_relaxed);
 
  164      extra_values_[i].fetch_add(value, std::memory_order_relaxed);
 
  166    count_.fetch_add(sum, std::memory_order_release);
 
  171    for (
auto& value : values_) value.store(0, std::memory_order_relaxed);
 
  172    for (
auto& value : extra_values_) value.store(0, std::memory_order_relaxed);
 
  177  Counter 
Count() 
const noexcept { 
return count_; }
 
  180  size_t ExtraBucketToValue(std::size_t bucket) 
const noexcept {
 
  181    return values_.size() + bucket * ExtraBucketSize;
 
  184  static_assert(std::atomic<Counter>::is_always_lock_free,
 
  185                "`std::atomic<Counter>` is not lock-free. Please choose some " 
  186                "other `Counter` type");
 
  188  std::array<std::atomic<Counter>, M> values_;
 
  189  std::array<std::atomic<Counter>, ExtraBuckets> extra_values_;
 
  190  std::atomic<Counter> count_;
 
  193std::string GetPercentileFieldName(
double perc);
 
  195template <size_t M, 
typename Counter, size_t ExtraBuckets,
 
  196          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,
 
  202  for (
double percent : percents) {
 
  203    writer.ValueWithLabels(
 
  204        perc.GetPercentile(percent),
 
  205        {
"percentile", statistics::GetPercentileFieldName(percent)});