diff --git a/src/metric.cpp b/src/metric.cpp index e8a4ca9..b420c80 100644 --- a/src/metric.cpp +++ b/src/metric.cpp @@ -342,13 +342,20 @@ struct Gauge::State { struct Histogram::State { std::span thresholds; // Bucket boundaries (sorted, // deduplicated, sizes never change) - std::span counts; // Count per bucket - double sum; // Sum of observations - uint64_t observations; // Total observation count + + // Histogram counter data + struct Counters { + std::span bucket_counts; // Count per bucket + double sum = 0.0; // Sum of observations + uint64_t observations = 0; // Total observation count + }; + + Counters counters; // Main counter data + std::mutex mutex; // Per-thread, per-histogram mutex for consistent reads/writes - State() : sum(0.0), observations(0) {} + State() {} friend struct Metric; }; @@ -499,13 +506,16 @@ struct Metric { assert(global_state); // Accumulate bucket counts (mutex already held) - for (size_t i = 0; i < instance->counts.size(); ++i) { - global_state->counts[i] += instance->counts[i]; + for (size_t i = 0; i < instance->counters.bucket_counts.size(); + ++i) { + global_state->counters.bucket_counts[i] += + instance->counters.bucket_counts[i]; } // Accumulate sum and observations - global_state->sum += instance->sum; - global_state->observations += instance->observations; + global_state->counters.sum += instance->counters.sum; + global_state->counters.observations += + instance->counters.observations; } family->per_thread_state.erase(thread_it); } @@ -682,7 +692,8 @@ struct Metric { std::memset(counts_data, 0, bucket_count * sizeof(uint64_t)); ptr->thresholds = std::span(thresholds_data, bucket_count); - ptr->counts = std::span(counts_data, bucket_count); + ptr->counters.bucket_counts = + std::span(counts_data, bucket_count); // Ensure global accumulator exists for this label set auto &global_state = family->p->global_accumulated_values[key]; @@ -701,7 +712,7 @@ struct Metric { global_state->thresholds = std::span(global_thresholds_data, bucket_count); - global_state->counts = + global_state->counters.bucket_counts = std::span(global_counts_data, bucket_count); } } @@ -1128,11 +1139,12 @@ struct Metric { { std::lock_guard lock(instance->mutex); - for (size_t i = 0; i < instance->counts.size(); ++i) { - counts_snapshot[i] = instance->counts[i]; + for (size_t i = 0; i < instance->counters.bucket_counts.size(); + ++i) { + counts_snapshot[i] = instance->counters.bucket_counts[i]; } - sum_snapshot = instance->sum; - observations_snapshot = instance->observations; + sum_snapshot = instance->counters.sum; + observations_snapshot = instance->counters.observations; } for (size_t i = 0; i < bucket_count; ++i) { @@ -1145,11 +1157,12 @@ struct Metric { // Add global accumulated values if (instruction.aggregate_histogram.global_state) { auto *global_state = instruction.aggregate_histogram.global_state; - for (size_t i = 0; i < global_state->counts.size(); ++i) { - total_counts[i] += global_state->counts[i]; + for (size_t i = 0; i < global_state->counters.bucket_counts.size(); + ++i) { + total_counts[i] += global_state->counters.bucket_counts[i]; } - total_sum += global_state->sum; - total_observations += global_state->observations; + total_sum += global_state->counters.sum; + total_observations += global_state->counters.observations; } // Format explicit bucket counts @@ -1411,16 +1424,16 @@ update_histogram_buckets_simd(std::span thresholds, } void Histogram::observe(double x) { - assert(p->thresholds.size() == p->counts.size()); + assert(p->thresholds.size() == p->counters.bucket_counts.size()); std::lock_guard lock(p->mutex); // Update bucket counts using SIMD - update_histogram_buckets_simd(p->thresholds, p->counts, x, 0); + update_histogram_buckets_simd(p->thresholds, p->counters.bucket_counts, x, 0); // Update sum and observation count - p->sum += x; - p->observations++; + p->counters.sum += x; + p->counters.observations++; } template <> Family::Family() = default;