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 {
32public:
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
43 StripedRateCounter(const StripedRateCounter& other) { val_.Add(other.LoadImpl()); }
44
45 StripedRateCounter& operator=(const StripedRateCounter& other) noexcept {
46 if (this == &other) {
47 return *this;
48 }
49
50 offset_ = other.LoadImpl() - val_.Read();
51 return *this;
52 }
53
54 StripedRateCounter& operator=(Rate desired) noexcept {
55 Store(desired);
56 return *this;
57 }
58
59 void Store(Rate desired) noexcept { offset_ = static_cast<std::uintptr_t>(desired.value) - val_.Read(); }
60
61 Rate Load() const noexcept { return Rate{static_cast<std::uint64_t>(LoadImpl())}; }
62
63 void Add(Rate arg) noexcept { val_.Add(static_cast<std::uintptr_t>(arg.value)); }
64
65 StripedRateCounter& operator++() noexcept {
66 val_.Add(1);
67 return *this;
68 }
69
70 StripedRateCounter& operator+=(Rate arg) noexcept {
71 Add(arg);
72 return *this;
73 }
74
75 StripedRateCounter& operator+=(const StripedRateCounter& arg) noexcept {
76 val_.Add(arg.LoadImpl());
77 return *this;
78 }
79
80private:
81 // Implementation note: any operation that assigns a value (instead of adding)
82 // would induce race condition if implemented using LoadImpl(). Use offset_
83 // directly instead for those.
84 std::uintptr_t LoadImpl() const noexcept { return val_.Read() + offset_.load(std::memory_order_relaxed); }
85
86 USERVER_NAMESPACE::concurrent::StripedCounter val_;
87 std::atomic<std::uintptr_t> offset_{0};
88};
89
90void DumpMetric(Writer& writer, const StripedRateCounter& value);
91
92void ResetMetric(StripedRateCounter& value);
93
94} // namespace utils::statistics
95
96USERVER_NAMESPACE_END