11#pragma once
22
3+ #include < array>
4+ #include < atomic>
5+ #include < exception>
6+
37#include " prometheus/client_metric.h"
4- #include " prometheus/gauge.h"
58#include " prometheus/metric_type.h"
69
710namespace prometheus {
@@ -17,7 +20,17 @@ namespace prometheus {
1720// / - errors
1821// /
1922// / 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., in the
33+ // / order of milliseconds.
2134// /
2235// / The class is thread-safe. No concurrent call to any API of this type causes
2336// / a data race.
@@ -29,20 +42,55 @@ class Counter {
2942 Counter () = default ;
3043
3144 // / \brief Increment the counter by 1.
32- void Increment ();
45+ void Increment () { IncrementUnchecked ( 1.0 ); }
3346
3447 // / \brief Increment the counter by a given amount.
3548 // /
3649 // / 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+ }
3856
3957 // / \brief Get the current value of the counter.
4058 double Value () const ;
4159
4260 ClientMetric Collect () const ;
4361
4462 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_{};
4694};
4795
4896} // namespace prometheus
0 commit comments