1
1
#pragma once
2
2
3
+ #include < array>
4
+ #include < atomic>
5
+ #include < exception>
6
+
3
7
#include " prometheus/client_metric.h"
4
- #include " prometheus/gauge.h"
5
8
#include " prometheus/metric_type.h"
6
9
7
10
namespace prometheus {
@@ -17,7 +20,17 @@ namespace prometheus {
17
20
// / - errors
18
21
// /
19
22
// / Do not use a counter to expose a value that can decrease - instead use a
20
- // / Gauge.
23
+ // / Gauge. If an montonically increasing counter is applicable a counter shall
24
+ // / be prefered to a Gauge because of a better update performance.
25
+ // /
26
+ // / The implementation exhibits a performance which is near a sequential
27
+ // / implementation and scales linearly with increasing number of updater threads
28
+ // / in a multi-threaded environment invoking Increment(). However, this
29
+ // / excellent update-side scalability comes at read-side expense invoking
30
+ // / Collect(). Increment() can therefor be used in the fast-path of the code,
31
+ // / where the count is updated extremely frequently. The Collect() function on
32
+ // / the other hand shall read the counter at a low sample rate, e.g.
33
+ // / milliseconds.
21
34
// /
22
35
// / The class is thread-safe. No concurrent call to any API of this type causes
23
36
// / a data race.
@@ -29,20 +42,55 @@ class Counter {
29
42
Counter () = default ;
30
43
31
44
// / \brief Increment the counter by 1.
32
- void Increment ();
45
+ void Increment () { IncrementUnchecked ( 1.0 ); }
33
46
34
47
// / \brief Increment the counter by a given amount.
35
48
// /
36
49
// / The counter will not change if the given amount is negative.
37
- void Increment (double );
50
+ void Increment (const double value) {
51
+ if (value < 0.0 ) {
52
+ return ;
53
+ }
54
+ IncrementUnchecked (value);
55
+ }
38
56
39
57
// / \brief Get the current value of the counter.
40
58
double Value () const ;
41
59
42
60
ClientMetric Collect () const ;
43
61
44
62
private:
45
- Gauge gauge_{0.0 };
63
+ int ThreadId () {
64
+ thread_local int id{-1 };
65
+
66
+ if (id == -1 ) {
67
+ id = AssignThreadId ();
68
+ }
69
+ return id;
70
+ }
71
+
72
+ int AssignThreadId () {
73
+ const int id{count_.fetch_add (1 )};
74
+
75
+ if (id >= per_thread_counter_.size ()) {
76
+ std::terminate ();
77
+ }
78
+
79
+ return id;
80
+ }
81
+
82
+ void IncrementUnchecked (const double v) {
83
+ CacheLine& c = per_thread_counter_[ThreadId ()];
84
+ const double new_value{c.v .load (std::memory_order_relaxed) + v};
85
+ c.v .store (new_value, std::memory_order_relaxed);
86
+ }
87
+
88
+ struct alignas (128 ) CacheLine {
89
+ std::atomic<double > v{0.0 };
90
+ };
91
+
92
+ std::atomic<int > count_{0 };
93
+ std::array<CacheLine, 256 > per_thread_counter_{};
46
94
};
47
95
48
96
} // namespace prometheus
0 commit comments