Intern label sets

This commit is contained in:
2025-08-31 12:46:29 -04:00
parent 4b2c5b8ce8
commit 889109f4ae

View File

@@ -16,6 +16,7 @@
#include <thread> #include <thread>
#include <type_traits> #include <type_traits>
#include <unordered_map> #include <unordered_map>
#include <unordered_set>
#include <vector> #include <vector>
#include <immintrin.h> #include <immintrin.h>
@@ -81,6 +82,7 @@ static std::string_view arena_copy_string(std::string_view str,
struct LabelsKey { struct LabelsKey {
ArenaVector<std::pair<std::string_view, std::string_view>> labels; ArenaVector<std::pair<std::string_view, std::string_view>> labels;
// Arena-owning constructor (copies strings into arena)
LabelsKey(std::span<const std::pair<std::string_view, std::string_view>> l, LabelsKey(std::span<const std::pair<std::string_view, std::string_view>> l,
ArenaAllocator &arena) ArenaAllocator &arena)
: labels(&arena) { : labels(&arena) {
@@ -302,6 +304,16 @@ struct Metric {
return *histogramFamilies; return *histogramFamilies;
} }
// Global label interning set to avoid duplicate LabelsKey allocations
static auto &get_interned_labels() {
using InternSet = std::unordered_set<LabelsKey, std::hash<LabelsKey>,
std::equal_to<LabelsKey>,
ArenaStlAllocator<LabelsKey>>;
static InternSet *internedLabels =
new InternSet(ArenaStlAllocator<LabelsKey>(&get_global_arena()));
return *internedLabels;
}
// Thread cleanup for per-family thread-local storage // Thread cleanup for per-family thread-local storage
struct ThreadInit { struct ThreadInit {
ArenaAllocator arena; ArenaAllocator arena;
@@ -379,6 +391,27 @@ struct Metric {
// Thread cleanup now handled by ThreadInit RAII // Thread cleanup now handled by ThreadInit RAII
// Intern labels to avoid duplicate arena allocations
static const LabelsKey &intern_labels(
std::span<const std::pair<std::string_view, std::string_view>> 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( static Counter create_counter_instance(
Family<Counter> *family, Family<Counter> *family,
std::span<const std::pair<std::string_view, std::string_view>> labels) { std::span<const std::pair<std::string_view, std::string_view>> labels) {
@@ -386,7 +419,7 @@ struct Metric {
(void)thread_init; (void)thread_init;
std::unique_lock<std::mutex> _{mutex}; std::unique_lock<std::mutex> _{mutex};
LabelsKey key{labels, get_global_arena()}; const LabelsKey &key = intern_labels(labels);
// Validate that labels aren't already registered as callback // Validate that labels aren't already registered as callback
validate_or_abort(family->p->callbacks.find(key) == validate_or_abort(family->p->callbacks.find(key) ==
@@ -419,7 +452,7 @@ struct Metric {
Family<Gauge> *family, Family<Gauge> *family,
std::span<const std::pair<std::string_view, std::string_view>> labels) { std::span<const std::pair<std::string_view, std::string_view>> labels) {
std::unique_lock<std::mutex> _{mutex}; std::unique_lock<std::mutex> _{mutex};
LabelsKey key{labels, get_global_arena()}; const LabelsKey &key = intern_labels(labels);
// Validate that labels aren't already registered as callback // Validate that labels aren't already registered as callback
validate_or_abort(family->p->callbacks.find(key) == validate_or_abort(family->p->callbacks.find(key) ==
@@ -444,7 +477,7 @@ struct Metric {
(void)thread_init; (void)thread_init;
std::unique_lock<std::mutex> _{mutex}; std::unique_lock<std::mutex> _{mutex};
LabelsKey key{labels, get_global_arena()}; const LabelsKey &key = intern_labels(labels);
// Ensure thread state exists // Ensure thread state exists
auto thread_id = std::this_thread::get_id(); auto thread_id = std::this_thread::get_id();
@@ -1163,7 +1196,7 @@ void Family<Counter>::register_callback(
std::span<const std::pair<std::string_view, std::string_view>> labels, std::span<const std::pair<std::string_view, std::string_view>> labels,
MetricCallback<Counter> callback) { MetricCallback<Counter> callback) {
std::unique_lock<std::mutex> _{Metric::mutex}; std::unique_lock<std::mutex> _{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 that labels aren't already in use by create() calls
for (const auto &[thread_id, per_thread] : p->per_thread_state) { for (const auto &[thread_id, per_thread] : p->per_thread_state) {
@@ -1186,7 +1219,7 @@ void Family<Gauge>::register_callback(
std::span<const std::pair<std::string_view, std::string_view>> labels, std::span<const std::pair<std::string_view, std::string_view>> labels,
MetricCallback<Gauge> callback) { MetricCallback<Gauge> callback) {
std::unique_lock<std::mutex> _{Metric::mutex}; std::unique_lock<std::mutex> _{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 that labels aren't already in use by create() calls
validate_or_abort(p->instances.find(key) == p->instances.end(), validate_or_abort(p->instances.find(key) == p->instances.end(),