Network System 0.1.1
High-performance modular networking library for scalable client-server applications
Loading...
Searching...
No Matches
kcenon::network::metrics::histogram Class Reference

Thread-safe histogram for capturing value distributions. More...

#include <histogram.h>

Collaboration diagram for kcenon::network::metrics::histogram:
Collaboration graph

Public Member Functions

 histogram (histogram_config cfg=histogram_config::default_latency_config())
 Construct histogram with configuration.
 
 ~histogram ()=default
 Destructor.
 
 histogram (const histogram &)=delete
 
auto operator= (const histogram &) -> histogram &=delete
 
 histogram (histogram &&other) noexcept
 
auto operator= (histogram &&other) noexcept -> histogram &
 
void record (double value)
 Record a value observation.
 
auto count () const -> uint64_t
 Get total number of observations.
 
auto sum () const -> double
 Get sum of all observations.
 
auto min () const -> double
 Get minimum observed value.
 
auto max () const -> double
 Get maximum observed value.
 
auto mean () const -> double
 Get mean of all observations.
 
auto percentile (double p) const -> double
 Calculate percentile value.
 
auto p50 () const -> double
 Get 50th percentile (median)
 
auto p95 () const -> double
 Get 95th percentile.
 
auto p99 () const -> double
 Get 99th percentile.
 
auto p999 () const -> double
 Get 99.9th percentile.
 
auto buckets () const -> std::vector< std::pair< double, uint64_t > >
 Get all bucket counts.
 
auto snapshot (const std::map< std::string, std::string > &labels={}) const -> histogram_snapshot
 Create immutable snapshot of current state.
 
void reset ()
 Reset all statistics.
 

Private Member Functions

auto find_bucket (double value) const -> size_t
 Find bucket index for a value.
 
void update_min (double value)
 Update min value atomically.
 
void update_max (double value)
 Update max value atomically.
 
void add_to_sum (double value)
 Add to sum atomically.
 

Private Attributes

std::vector< double > boundaries_
 
std::unique_ptr< std::atomic< uint64_t >[]> bucket_counts_
 
size_t bucket_count_ {0}
 
std::atomic< uint64_t > count_ {0}
 
std::atomic< double > sum_ {0.0}
 
std::atomic< double > min_ {std::numeric_limits<double>::infinity()}
 
std::atomic< double > max_ {-std::numeric_limits<double>::infinity()}
 
std::mutex mutex_
 

Detailed Description

Thread-safe histogram for capturing value distributions.

This histogram implementation uses predefined bucket boundaries to track the distribution of values. It supports:

  • Recording values with thread-safe atomic operations
  • Calculating percentiles (p50, p95, p99, p999)
  • Exporting to Prometheus and JSON formats
Example usage:
h.record(5.5);
h.record(10.2);
h.record(3.1);
double p99 = h.p99();
auto snapshot = h.snapshot();
std::cout << snapshot.to_prometheus("latency_ms") << std::endl;
Thread-safe histogram for capturing value distributions.
Definition histogram.h:106
auto p99() const -> double
Get 99th percentile.
Definition histogram.h:191
auto snapshot(const std::map< std::string, std::string > &labels={}) const -> histogram_snapshot
Create immutable snapshot of current state.
static auto default_latency_config() -> histogram_config
Create default configuration for network latencies.
Definition histogram.h:47
Examples
network_metrics_example.cpp.

Definition at line 105 of file histogram.h.

Constructor & Destructor Documentation

◆ histogram() [1/3]

kcenon::network::metrics::histogram::histogram ( histogram_config cfg = histogram_config::default_latency_config())
explicit

Construct histogram with configuration.

Parameters
cfgConfiguration with bucket boundaries

Definition at line 153 of file histogram.cpp.

154{
155 if (cfg.bucket_boundaries.empty())
156 {
158 }
159
160 boundaries_ = std::move(cfg.bucket_boundaries);
161 std::sort(boundaries_.begin(), boundaries_.end());
162
163 // Add +Inf bucket
164 boundaries_.push_back(std::numeric_limits<double>::infinity());
165
166 // Initialize bucket counts
167 bucket_count_ = boundaries_.size();
168 bucket_counts_ = std::make_unique<std::atomic<uint64_t>[]>(bucket_count_);
169 for (size_t i = 0; i < bucket_count_; ++i)
170 {
171 bucket_counts_[i].store(0, std::memory_order_relaxed);
172 }
173}
std::vector< double > boundaries_
Definition histogram.h:221
std::unique_ptr< std::atomic< uint64_t >[]> bucket_counts_
Definition histogram.h:222

References boundaries_, kcenon::network::metrics::histogram_config::bucket_boundaries, bucket_count_, bucket_counts_, and kcenon::network::metrics::histogram_config::default_latency_config().

Here is the call graph for this function:

◆ ~histogram()

kcenon::network::metrics::histogram::~histogram ( )
default

Destructor.

◆ histogram() [2/3]

kcenon::network::metrics::histogram::histogram ( const histogram & )
delete

◆ histogram() [3/3]

kcenon::network::metrics::histogram::histogram ( histogram && other)
noexcept

Definition at line 175 of file histogram.cpp.

176 : boundaries_(std::move(other.boundaries_))
177 , bucket_counts_(std::move(other.bucket_counts_))
178 , bucket_count_(other.bucket_count_)
179 , count_(other.count_.load(std::memory_order_relaxed))
180 , sum_(other.sum_.load(std::memory_order_relaxed))
181 , min_(other.min_.load(std::memory_order_relaxed))
182 , max_(other.max_.load(std::memory_order_relaxed))
183{
184 other.bucket_count_ = 0;
185}
std::atomic< uint64_t > count_
Definition histogram.h:224

Member Function Documentation

◆ add_to_sum()

void kcenon::network::metrics::histogram::add_to_sum ( double value)
private

Add to sum atomically.

Parameters
valueValue to add

Definition at line 390 of file histogram.cpp.

391{
392 double current = sum_.load(std::memory_order_relaxed);
393 while (!sum_.compare_exchange_weak(current, current + value, std::memory_order_relaxed,
394 std::memory_order_relaxed))
395 {
396 // current is updated by compare_exchange_weak
397 }
398}

References sum_.

Referenced by record().

Here is the caller graph for this function:

◆ buckets()

auto kcenon::network::metrics::histogram::buckets ( ) const -> std::vector<std::pair<double, uint64_t>>
nodiscard

Get all bucket counts.

Returns
Vector of (boundary, cumulative_count) pairs

Definition at line 305 of file histogram.cpp.

306{
307 std::lock_guard<std::mutex> lock(mutex_);
308
309 std::vector<std::pair<double, uint64_t>> result;
310 result.reserve(bucket_count_);
311
312 uint64_t cumulative = 0;
313 for (size_t i = 0; i < bucket_count_; ++i)
314 {
315 cumulative += bucket_counts_[i].load(std::memory_order_relaxed);
316 result.emplace_back(boundaries_[i], cumulative);
317 }
318
319 return result;
320}

References boundaries_, bucket_count_, bucket_counts_, and mutex_.

◆ count()

auto kcenon::network::metrics::histogram::count ( ) const -> uint64_t
nodiscard

Get total number of observations.

Returns
Count of recorded values

Definition at line 214 of file histogram.cpp.

215{
216 return count_.load(std::memory_order_relaxed);
217}

References count_.

Referenced by mean().

Here is the caller graph for this function:

◆ find_bucket()

auto kcenon::network::metrics::histogram::find_bucket ( double value) const -> size_t
nodiscardprivate

Find bucket index for a value.

Parameters
valueThe value to classify
Returns
Index of the bucket containing this value

Definition at line 360 of file histogram.cpp.

361{
362 auto it = std::lower_bound(boundaries_.begin(), boundaries_.end(), value);
363 if (it == boundaries_.end())
364 {
365 return boundaries_.size() - 1;
366 }
367 return static_cast<size_t>(std::distance(boundaries_.begin(), it));
368}

Referenced by record().

Here is the caller graph for this function:

◆ max()

auto kcenon::network::metrics::histogram::max ( ) const -> double
nodiscard

Get maximum observed value.

Returns
Maximum value, or -inf if no observations

Definition at line 229 of file histogram.cpp.

230{
231 return max_.load(std::memory_order_relaxed);
232}

References max_.

◆ mean()

auto kcenon::network::metrics::histogram::mean ( ) const -> double
nodiscard

Get mean of all observations.

Returns
Mean value, or 0 if no observations

Definition at line 234 of file histogram.cpp.

235{
236 uint64_t c = count();
237 if (c == 0)
238 {
239 return 0.0;
240 }
241 return sum() / static_cast<double>(c);
242}
auto sum() const -> double
Get sum of all observations.
auto count() const -> uint64_t
Get total number of observations.

References count(), and sum().

Here is the call graph for this function:

◆ min()

auto kcenon::network::metrics::histogram::min ( ) const -> double
nodiscard

Get minimum observed value.

Returns
Minimum value, or +inf if no observations

Definition at line 224 of file histogram.cpp.

225{
226 return min_.load(std::memory_order_relaxed);
227}

References min_.

◆ operator=() [1/2]

auto kcenon::network::metrics::histogram::operator= ( const histogram & ) -> histogram &=delete
delete

◆ operator=() [2/2]

auto kcenon::network::metrics::histogram::operator= ( histogram && other) -> histogram&
noexcept

Definition at line 187 of file histogram.cpp.

188{
189 if (this != &other)
190 {
191 boundaries_ = std::move(other.boundaries_);
192 bucket_counts_ = std::move(other.bucket_counts_);
193 bucket_count_ = other.bucket_count_;
194 count_.store(other.count_.load(std::memory_order_relaxed), std::memory_order_relaxed);
195 sum_.store(other.sum_.load(std::memory_order_relaxed), std::memory_order_relaxed);
196 min_.store(other.min_.load(std::memory_order_relaxed), std::memory_order_relaxed);
197 max_.store(other.max_.load(std::memory_order_relaxed), std::memory_order_relaxed);
198 other.bucket_count_ = 0;
199 }
200 return *this;
201}

◆ p50()

auto kcenon::network::metrics::histogram::p50 ( ) const -> double
inlinenodiscard

Get 50th percentile (median)

Returns
Estimated median value

Definition at line 179 of file histogram.h.

179{ return percentile(0.50); }
auto percentile(double p) const -> double
Calculate percentile value.

◆ p95()

auto kcenon::network::metrics::histogram::p95 ( ) const -> double
inlinenodiscard

Get 95th percentile.

Returns
Estimated p95 value

Definition at line 185 of file histogram.h.

185{ return percentile(0.95); }

◆ p99()

auto kcenon::network::metrics::histogram::p99 ( ) const -> double
inlinenodiscard

Get 99th percentile.

Returns
Estimated p99 value

Definition at line 191 of file histogram.h.

191{ return percentile(0.99); }

◆ p999()

auto kcenon::network::metrics::histogram::p999 ( ) const -> double
inlinenodiscard

Get 99.9th percentile.

Returns
Estimated p999 value

Definition at line 197 of file histogram.h.

197{ return percentile(0.999); }

◆ percentile()

auto kcenon::network::metrics::histogram::percentile ( double p) const -> double
nodiscard

Calculate percentile value.

Parameters
pPercentile (0.0 to 1.0)
Returns
Estimated value at the given percentile

Uses linear interpolation within buckets for estimation.

Definition at line 244 of file histogram.cpp.

245{
246 std::lock_guard<std::mutex> lock(mutex_);
247
248 uint64_t total = count_.load(std::memory_order_relaxed);
249 if (total == 0)
250 {
251 return 0.0;
252 }
253
254 // Clamp percentile to valid range
255 p = std::clamp(p, 0.0, 1.0);
256
257 // Target count for this percentile
258 double target = p * static_cast<double>(total);
259
260 // Calculate cumulative counts
261 std::vector<uint64_t> cumulative;
262 cumulative.reserve(bucket_count_);
263 uint64_t running_sum = 0;
264 for (size_t i = 0; i < bucket_count_; ++i)
265 {
266 running_sum += bucket_counts_[i].load(std::memory_order_relaxed);
267 cumulative.push_back(running_sum);
268 }
269
270 // Find the bucket containing the target count
271 size_t bucket_idx = 0;
272 for (size_t i = 0; i < cumulative.size(); ++i)
273 {
274 if (static_cast<double>(cumulative[i]) >= target)
275 {
276 bucket_idx = i;
277 break;
278 }
279 }
280
281 // Linear interpolation within the bucket
282 double lower_bound = (bucket_idx == 0) ? 0.0 : boundaries_[bucket_idx - 1];
283 double upper_bound = boundaries_[bucket_idx];
284
285 // Handle infinity bucket
286 if (std::isinf(upper_bound))
287 {
288 return lower_bound;
289 }
290
291 uint64_t lower_count = (bucket_idx == 0) ? 0 : cumulative[bucket_idx - 1];
292 uint64_t upper_count = cumulative[bucket_idx];
293
294 if (upper_count == lower_count)
295 {
296 return lower_bound;
297 }
298
299 double fraction = (target - static_cast<double>(lower_count))
300 / (static_cast<double>(upper_count) - static_cast<double>(lower_count));
301
302 return lower_bound + (fraction * (upper_bound - lower_bound));
303}

◆ record()

void kcenon::network::metrics::histogram::record ( double value)

Record a value observation.

Parameters
valueThe value to record

Thread-safe. Values are placed in the appropriate bucket based on configured boundaries.

Examples
network_metrics_example.cpp.

Definition at line 203 of file histogram.cpp.

204{
205 size_t bucket_idx = find_bucket(value);
206 bucket_counts_[bucket_idx].fetch_add(1, std::memory_order_relaxed);
207
208 count_.fetch_add(1, std::memory_order_relaxed);
209 add_to_sum(value);
210 update_min(value);
211 update_max(value);
212}
auto find_bucket(double value) const -> size_t
Find bucket index for a value.
void update_max(double value)
Update max value atomically.
void add_to_sum(double value)
Add to sum atomically.
void update_min(double value)
Update min value atomically.

References add_to_sum(), bucket_counts_, count_, find_bucket(), update_max(), and update_min().

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ reset()

void kcenon::network::metrics::histogram::reset ( )

Reset all statistics.

Thread-safe. Clears all observations.

Definition at line 345 of file histogram.cpp.

346{
347 std::lock_guard<std::mutex> lock(mutex_);
348
349 count_.store(0, std::memory_order_relaxed);
350 sum_.store(0.0, std::memory_order_relaxed);
351 min_.store(std::numeric_limits<double>::infinity(), std::memory_order_relaxed);
352 max_.store(-std::numeric_limits<double>::infinity(), std::memory_order_relaxed);
353
354 for (size_t i = 0; i < bucket_count_; ++i)
355 {
356 bucket_counts_[i].store(0, std::memory_order_relaxed);
357 }
358}

References bucket_count_, bucket_counts_, count_, max_, min_, mutex_, and sum_.

◆ snapshot()

auto kcenon::network::metrics::histogram::snapshot ( const std::map< std::string, std::string > & labels = {}) const -> histogram_snapshot
nodiscard

Create immutable snapshot of current state.

Parameters
labelsAdditional labels to include in snapshot
Returns
Snapshot with all statistics and bucket counts

Definition at line 322 of file histogram.cpp.

324{
325 histogram_snapshot snap;
326 snap.count = count();
327 snap.sum = sum();
328 snap.min_value = min();
329 snap.max_value = max();
330 snap.labels = labels;
331
332 // Calculate percentiles
333 snap.percentiles[0.5] = p50();
334 snap.percentiles[0.9] = percentile(0.9);
335 snap.percentiles[0.95] = p95();
336 snap.percentiles[0.99] = p99();
337 snap.percentiles[0.999] = p999();
338
339 // Get buckets
340 snap.buckets = buckets();
341
342 return snap;
343}
auto max() const -> double
Get maximum observed value.
auto p999() const -> double
Get 99.9th percentile.
Definition histogram.h:197
auto min() const -> double
Get minimum observed value.
auto p95() const -> double
Get 95th percentile.
Definition histogram.h:185
auto p50() const -> double
Get 50th percentile (median)
Definition histogram.h:179
auto buckets() const -> std::vector< std::pair< double, uint64_t > >
Get all bucket counts.

References kcenon::network::metrics::histogram_snapshot::buckets, kcenon::network::metrics::histogram_snapshot::count, kcenon::network::metrics::histogram_snapshot::labels, kcenon::network::metrics::histogram_snapshot::max_value, kcenon::network::metrics::histogram_snapshot::min_value, kcenon::network::metrics::histogram_snapshot::percentiles, and kcenon::network::metrics::histogram_snapshot::sum.

◆ sum()

auto kcenon::network::metrics::histogram::sum ( ) const -> double
nodiscard

Get sum of all observations.

Returns
Sum of recorded values

Definition at line 219 of file histogram.cpp.

220{
221 return sum_.load(std::memory_order_relaxed);
222}

References sum_.

Referenced by mean().

Here is the caller graph for this function:

◆ update_max()

void kcenon::network::metrics::histogram::update_max ( double value)
private

Update max value atomically.

Parameters
valueNew potential maximum

Definition at line 380 of file histogram.cpp.

381{
382 double current = max_.load(std::memory_order_relaxed);
383 while (value > current && !max_.compare_exchange_weak(current, value, std::memory_order_relaxed,
384 std::memory_order_relaxed))
385 {
386 // current is updated by compare_exchange_weak
387 }
388}

References max_.

Referenced by record().

Here is the caller graph for this function:

◆ update_min()

void kcenon::network::metrics::histogram::update_min ( double value)
private

Update min value atomically.

Parameters
valueNew potential minimum

Definition at line 370 of file histogram.cpp.

371{
372 double current = min_.load(std::memory_order_relaxed);
373 while (value < current && !min_.compare_exchange_weak(current, value, std::memory_order_relaxed,
374 std::memory_order_relaxed))
375 {
376 // current is updated by compare_exchange_weak
377 }
378}

References min_.

Referenced by record().

Here is the caller graph for this function:

Member Data Documentation

◆ boundaries_

std::vector<double> kcenon::network::metrics::histogram::boundaries_
private

Definition at line 221 of file histogram.h.

Referenced by buckets(), and histogram().

◆ bucket_count_

size_t kcenon::network::metrics::histogram::bucket_count_ {0}
private

Definition at line 223 of file histogram.h.

223{0};

Referenced by buckets(), histogram(), and reset().

◆ bucket_counts_

std::unique_ptr<std::atomic<uint64_t>[]> kcenon::network::metrics::histogram::bucket_counts_
private

Definition at line 222 of file histogram.h.

Referenced by buckets(), histogram(), record(), and reset().

◆ count_

std::atomic<uint64_t> kcenon::network::metrics::histogram::count_ {0}
private

Definition at line 224 of file histogram.h.

224{0};

Referenced by count(), record(), and reset().

◆ max_

std::atomic<double> kcenon::network::metrics::histogram::max_ {-std::numeric_limits<double>::infinity()}
private

Definition at line 227 of file histogram.h.

227{-std::numeric_limits<double>::infinity()};

Referenced by max(), reset(), and update_max().

◆ min_

std::atomic<double> kcenon::network::metrics::histogram::min_ {std::numeric_limits<double>::infinity()}
private

Definition at line 226 of file histogram.h.

226{std::numeric_limits<double>::infinity()};

Referenced by min(), reset(), and update_min().

◆ mutex_

std::mutex kcenon::network::metrics::histogram::mutex_
mutableprivate

Definition at line 228 of file histogram.h.

Referenced by buckets(), and reset().

◆ sum_

std::atomic<double> kcenon::network::metrics::histogram::sum_ {0.0}
private

Definition at line 225 of file histogram.h.

225{0.0};

Referenced by add_to_sum(), reset(), and sum().


The documentation for this class was generated from the following files: