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