userver: userver/utils/statistics/striped_rate_counter.hpp Source File
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
striped_rate_counter.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/utils/statistics/striped_rate_counter.hpp
4/// @brief @copybrief utils::statistics::StripedRateCounter
5
6#include <atomic>
7#include <cstdint>
8
9#include <userver/concurrent/striped_counter.hpp>
10#include <userver/utils/statistics/fwd.hpp>
11#include <userver/utils/statistics/rate.hpp>
12
13USERVER_NAMESPACE_BEGIN
14
15namespace utils::statistics {
16
17/// @brief Atomic counter of type Rate with relaxed memory ordering, increased
18/// memory consumption and decreased contention.
19///
20/// This class is represented as Rate metric when serializing to statistics.
21///
22/// Differences from utils::statistics::RateCounter:
23//
24/// 1. StripedRateCounter takes `8 * N_CORES` memory, while RateCounter is just
25/// a single atomic.
26/// 2. StripedRateCounter uses split counters on x86_64, which results
27/// in perfect performance even under heavy concurrent usage.
28///
29/// Use StripedRateCounter instead of RateCounter sparingly, in places where a
30/// lot of threads are supposed to be hammering on the same metrics.
31class StripedRateCounter final {
32 public:
33 using ValueType = Rate;
34
35 StripedRateCounter() = default;
36
37 explicit StripedRateCounter(Rate desired) { Add(desired); }
38
39 explicit StripedRateCounter(Rate::ValueType desired)
40 : StripedRateCounter(Rate{desired}) {}
41
42 StripedRateCounter(const StripedRateCounter& other) {
43 val_.Add(other.LoadImpl());
44 }
45
46 StripedRateCounter& operator=(const StripedRateCounter& other) noexcept {
47 if (this == &other) return *this;
48
49 offset_ = other.LoadImpl() - val_.Read();
50 return *this;
51 }
52
53 StripedRateCounter& operator=(Rate desired) noexcept {
54 Store(desired);
55 return *this;
56 }
57
58 void Store(Rate desired) noexcept {
59 offset_ = static_cast<std::uintptr_t>(desired.value) - val_.Read();
60 }
61
62 Rate Load() const noexcept {
63 return Rate{static_cast<std::uint64_t>(LoadImpl())};
64 }
65
66 void Add(Rate arg) noexcept {
67 val_.Add(static_cast<std::uintptr_t>(arg.value));
68 }
69
70 StripedRateCounter& operator++() noexcept {
71 val_.Add(1);
72 return *this;
73 }
74
75 StripedRateCounter& operator+=(Rate arg) noexcept {
76 Add(arg);
77 return *this;
78 }
79
80 StripedRateCounter& operator+=(const StripedRateCounter& arg) noexcept {
81 val_.Add(arg.LoadImpl());
82 return *this;
83 }
84
85 private:
86 // Implementation note: any operation that assigns a value (instead of adding)
87 // would induce race condition if implemented using LoadImpl(). Use offset_
88 // directly instead for those.
89 std::uintptr_t LoadImpl() const noexcept {
90 return val_.Read() + offset_.load(std::memory_order_relaxed);
91 }
92
93 concurrent::StripedCounter val_;
94 std::atomic<std::uintptr_t> offset_{0};
95};
96
97void DumpMetric(Writer& writer, const StripedRateCounter& value);
98
99void ResetMetric(StripedRateCounter& value);
100
101} // namespace utils::statistics
102
103USERVER_NAMESPACE_END