userver: userver/utils/statistics/histogram.hpp Source File
Loading...
Searching...
No Matches
histogram.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/utils/statistics/histogram.hpp
4/// @brief @copybrief utils::statistics::Histogram
5
6#include <cstdint>
7#include <memory>
8
9#include <userver/utils/span.hpp>
10#include <userver/utils/statistics/fwd.hpp>
11#include <userver/utils/statistics/histogram_view.hpp>
12
13USERVER_NAMESPACE_BEGIN
14
15namespace utils::statistics {
16
17namespace impl::histogram {
18struct BoundsBlock;
19} // namespace impl::histogram
20
21/// @brief A histogram with a dynamically-allocated array of buckets.
22///
23/// ## Histogram metrics
24///
25/// * A histogram metric is a list of counters ("buckets") with specified bounds
26/// * The implicit lowest bound is always 0
27/// * Bucket bounds must follow the ascending order
28/// * Values on the bucket borders fall into the lower bucket
29/// * Besides normal buckets, there is also a special "infinity" bucket,
30/// which contains values that are greater than the greatest bucket bound
31/// * Each individual counter has the semantics of utils::statistics::Rate
32/// * For best portability, there should be no more than 50 buckets
33///
34/// ## Histograms vs utils::statistics::Percentile
35///
36/// @see utils::statistics::Percentile is a related metric type
37///
38/// The trade-offs of histograms with `Percentile` are:
39///
40/// 1. `Percentile` metrics are fundamentally non-summable across multiple
41/// hosts. `Histogram`, on the other hand, are summable
42/// 2. A `Histogram` takes up more storage space on the statistics server,
43/// as there are typically 20-50 buckets in a `Histogram`, but only a few
44/// required percentiles in a `Percentile`
45/// 3. `Percentile` metrics have almost infinite precision, limited only
46/// by the number of allocated atomic counters. The precision of `Histogram`
47/// metrics is limited by the initially set bounds
48///
49/// ## Usage of Histogram
50///
51/// Usage example:
52/// @snippet utils/statistics/histogram_test.cpp sample
53///
54/// Contents of a Histogram are read using utils::statistics::HistogramView.
55/// This can be useful for writing custom metric serialization formats or
56/// for testing.
57///
58/// Histogram metrics can be summed using
59/// utils::statistics::HistogramAggregator.
60///
61/// Histogram can be used in utils::statistics::MetricTag:
62/// @snippet utils/statistics/histogram_test.cpp metric tag
63class Histogram final {
64public:
65 /// Sets upper bounds for each non-"infinite" bucket. The lowest bound is
66 /// always 0.
67 explicit Histogram(utils::span<const double> upper_bounds);
68
69 /// Copies an existing histogram.
70 explicit Histogram(HistogramView other);
71
72 Histogram(Histogram&&) noexcept;
73 Histogram(const Histogram&);
74 Histogram& operator=(Histogram&&) noexcept;
75 Histogram& operator=(const Histogram&);
76 ~Histogram();
77
78 /// Atomically increment the bucket corresponding to the given value.
79 void Account(double value, std::uint64_t count = 1) noexcept;
80
81 /// Atomically reset all counters to zero.
82 friend void ResetMetric(Histogram& histogram) noexcept;
83
84 /// Allows reading the histogram.
85 HistogramView GetView() const& noexcept;
86
87 /// @cond
88 // Store Histogram in a variable before taking a view on it.
89 HistogramView GetView() && noexcept = delete;
90 /// @endcond
91
92private:
93 void UpdateBounds();
94
95 std::unique_ptr<impl::histogram::Bucket[]> buckets_;
96 // B+ tree of bucket bounds for optimization of Account.
97 std::unique_ptr<impl::histogram::BoundsBlock[]> bounds_;
98 // Duplicate size here to avoid loading an extra cache line in Account.
99 std::size_t bucket_count_;
100};
101
102/// Metric serialization support for Histogram.
103void DumpMetric(Writer& writer, const Histogram& histogram);
104
105} // namespace utils::statistics
106
107USERVER_NAMESPACE_END