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.
33+ // / 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,48 @@ class Counter {
2942 Counter () = default ;
3043
3144 // / \brief Increment the counter by 1.
32- void Increment ();
45+ void Increment () { Increment ( 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 v) {
51+ CacheLine& c{per_thread_counter_[ThreadId ()]};
52+ const double new_value{c.v .load (std::memory_order_relaxed) + v};
53+ c.v .store (new_value, std::memory_order_relaxed);
54+ }
3855
3956 // / \brief Get the current value of the counter.
4057 double Value () const ;
4158
4259 ClientMetric Collect () const ;
4360
4461 private:
45- Gauge gauge_{0.0 };
62+ int ThreadId () {
63+ thread_local int id{-1 };
64+
65+ if (id == -1 ) {
66+ id = AssignThreadId ();
67+ }
68+ return id;
69+ }
70+
71+ int AssignThreadId () {
72+ const int id{count_.fetch_add (1 )};
73+
74+ if (id >= per_thread_counter_.size ()) {
75+ std::terminate ();
76+ }
77+
78+ return id;
79+ }
80+
81+ struct alignas (128 ) CacheLine {
82+ std::atomic<double > v{0.0 };
83+ };
84+
85+ std::atomic<int > count_{0 };
86+ std::array<CacheLine, 256 > per_thread_counter_{};
4687};
4788
4889} // namespace prometheus
0 commit comments