userver: userver/utils/statistics/striped_rate_counter.hpp Source File
Loading...
Searching...
No Matches
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 USERVER_NAMESPACE::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