diff --git a/src/metric.cpp b/src/metric.cpp index 87ef826..4920e1a 100644 --- a/src/metric.cpp +++ b/src/metric.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -81,6 +82,7 @@ static std::string_view arena_copy_string(std::string_view str, struct LabelsKey { ArenaVector> labels; + // Arena-owning constructor (copies strings into arena) LabelsKey(std::span> l, ArenaAllocator &arena) : labels(&arena) { @@ -302,6 +304,16 @@ struct Metric { return *histogramFamilies; } + // Global label interning set to avoid duplicate LabelsKey allocations + static auto &get_interned_labels() { + using InternSet = std::unordered_set, + std::equal_to, + ArenaStlAllocator>; + static InternSet *internedLabels = + new InternSet(ArenaStlAllocator(&get_global_arena())); + return *internedLabels; + } + // Thread cleanup for per-family thread-local storage struct ThreadInit { ArenaAllocator arena; @@ -379,6 +391,27 @@ struct Metric { // Thread cleanup now handled by ThreadInit RAII + // Intern labels to avoid duplicate arena allocations + static const LabelsKey &intern_labels( + std::span> labels) { + auto &interned_set = get_interned_labels(); + + // Create temporary lookup key using stack-allocated arena + ArenaAllocator lookup_arena(1024); // Small arena for lookups + LabelsKey lookup_key{labels, lookup_arena}; + + // Use standard hash set lookup + auto it = interned_set.find(lookup_key); + if (it != interned_set.end()) { + return *it; + } + + // Not found - create and intern new key + LabelsKey new_key{labels, get_global_arena()}; + auto result = interned_set.emplace(std::move(new_key)); + return *result.first; + } + static Counter create_counter_instance( Family *family, std::span> labels) { @@ -386,7 +419,7 @@ struct Metric { (void)thread_init; std::unique_lock _{mutex}; - LabelsKey key{labels, get_global_arena()}; + const LabelsKey &key = intern_labels(labels); // Validate that labels aren't already registered as callback validate_or_abort(family->p->callbacks.find(key) == @@ -419,7 +452,7 @@ struct Metric { Family *family, std::span> labels) { std::unique_lock _{mutex}; - LabelsKey key{labels, get_global_arena()}; + const LabelsKey &key = intern_labels(labels); // Validate that labels aren't already registered as callback validate_or_abort(family->p->callbacks.find(key) == @@ -444,7 +477,7 @@ struct Metric { (void)thread_init; std::unique_lock _{mutex}; - LabelsKey key{labels, get_global_arena()}; + const LabelsKey &key = intern_labels(labels); // Ensure thread state exists auto thread_id = std::this_thread::get_id(); @@ -1163,7 +1196,7 @@ void Family::register_callback( std::span> labels, MetricCallback callback) { std::unique_lock _{Metric::mutex}; - LabelsKey key{labels, Metric::get_global_arena()}; + const LabelsKey &key = Metric::intern_labels(labels); // Validate that labels aren't already in use by create() calls for (const auto &[thread_id, per_thread] : p->per_thread_state) { @@ -1186,7 +1219,7 @@ void Family::register_callback( std::span> labels, MetricCallback callback) { std::unique_lock _{Metric::mutex}; - LabelsKey key{labels, Metric::get_global_arena()}; + const LabelsKey &key = Metric::intern_labels(labels); // Validate that labels aren't already in use by create() calls validate_or_abort(p->instances.find(key) == p->instances.end(),