From 7533c62e5d850a50ce7ff15fdb356d72a8d34a09 Mon Sep 17 00:00:00 2001 From: Casey Bodley Date: Wed, 17 Aug 2022 09:27:42 -0400 Subject: [PATCH] common: ceph::perf_counters::make_cache_key() a flat representation of a set of prometheus labels, returned as a std::string. this string can either be used for sorting an ordered container of perf counters, or for hashing an unordered container use a variadic template to initialize a temporary array for sorting use rvalue-ref to c array instead of variadic template Signed-off-by: Casey Bodley --- src/common/perf_counters_cache_key.h | 66 ++++++++++++++++++++++++++ src/test/common/CMakeLists.txt | 5 ++ src/test/common/test_perf_cache_key.cc | 45 ++++++++++++++++++ 3 files changed, 116 insertions(+) create mode 100644 src/common/perf_counters_cache_key.h create mode 100644 src/test/common/test_perf_cache_key.cc diff --git a/src/common/perf_counters_cache_key.h b/src/common/perf_counters_cache_key.h new file mode 100644 index 0000000000000..67250811fbd5c --- /dev/null +++ b/src/common/perf_counters_cache_key.h @@ -0,0 +1,66 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace ceph::perf_counters { + +/// key/value pair representing a perf counter label +using label_pair = std::pair; + + +/// construct a cache key for a perf counter and set of labels. returns a string +/// of the form "counter_name\0key1\0value1\0key2\0value2\0", where label pairs +/// are insertion-sorted by key then value +template +constexpr std::string make_cache_key(std::string_view counter_name, + label_pair (&&sorted)[Count]) +{ + std::sort(std::begin(sorted), std::end(sorted)); + + // use a null character to delimit strings + constexpr char delimiter = '\0'; + + // calculate the total size and preallocate the buffer + auto size = std::accumulate(std::begin(sorted), std::end(sorted), + counter_name.size() + sizeof(delimiter), + [] (std::size_t sum, const label_pair& l) { + return sum + l.first.size() + sizeof(delimiter) + + l.second.size() + sizeof(delimiter); + }); + + std::string result; + result.resize(size); + + auto pos = result.begin(); + pos = std::copy(counter_name.begin(), counter_name.end(), pos); + *(pos++) = delimiter; + + for (const auto& label : sorted) { + pos = std::copy(label.first.begin(), label.first.end(), pos); + *(pos++) = delimiter; + pos = std::copy(label.second.begin(), label.second.end(), pos); + *(pos++) = delimiter; + } + + return result; +} + +constexpr std::string make_cache_key(std::string_view counter_name) +{ + constexpr char delimiter = '\0'; + const auto size = counter_name.size() + sizeof(delimiter); + std::string result; + result.resize(size); + auto pos = result.begin(); + pos = std::copy(counter_name.begin(), counter_name.end(), pos); + *(pos++) = delimiter; + return result; +} + +// TODO: cache_key_insert() to add more labels to an existing string + +} // namespace ceph::perf_counters diff --git a/src/test/common/CMakeLists.txt b/src/test/common/CMakeLists.txt index 28738c0908538..26d2715127361 100644 --- a/src/test/common/CMakeLists.txt +++ b/src/test/common/CMakeLists.txt @@ -294,6 +294,11 @@ add_executable(unittest_perf_histogram add_ceph_unittest(unittest_perf_histogram) target_link_libraries(unittest_perf_histogram ceph-common) +# unittest_perf_cache_key +add_executable(unittest_perf_cache_key test_perf_cache_key.cc) +add_ceph_unittest(unittest_perf_cache_key) +target_link_libraries(unittest_perf_cache_key ceph-common) + # unittest_global_doublefree if(WITH_CEPHFS) add_executable(unittest_global_doublefree diff --git a/src/test/common/test_perf_cache_key.cc b/src/test/common/test_perf_cache_key.cc new file mode 100644 index 0000000000000..6ba186ad7ac8c --- /dev/null +++ b/src/test/common/test_perf_cache_key.cc @@ -0,0 +1,45 @@ +#include "common/perf_counters_cache_key.h" +#include + +namespace ceph::perf_counters { + +TEST(PerfCounters, CacheKey) +{ + EXPECT_EQ(make_cache_key(""), + std::string_view("\0", 1)); + + EXPECT_EQ(make_cache_key("perf"), + std::string_view("perf\0", 5)); + + EXPECT_EQ(make_cache_key("perf", {{"",""}}), + std::string_view("perf\0\0\0", 7)); + + EXPECT_EQ(make_cache_key("perf", {{"",""}, {"",""}}), + std::string_view("perf\0\0\0\0\0", 9)); + + EXPECT_EQ(make_cache_key("perf", {{"a","b"}}), + std::string_view("perf\0a\0b\0", 9)); + + EXPECT_EQ(make_cache_key("perf", {{"y","z"}, {"a","b"}}), + std::string_view("perf\0a\0b\0y\0z\0", 13)); + + EXPECT_EQ(make_cache_key("perf", {{"a","b"}, {"a","c"}}), + std::string_view("perf\0a\0b\0a\0c\0", 13)); + + EXPECT_EQ(make_cache_key("perf", {{"a","z"}, {"a","b"}}), + std::string_view("perf\0a\0b\0a\0z\0", 13)); + + EXPECT_EQ(make_cache_key("perf", {{"d",""}, {"c",""}, {"b",""}, {"a",""}}), + std::string_view("perf\0a\0\0b\0\0c\0\0d\0\0", 17)); + + // error: ‘ceph::perf_counters::make_cache_key(...)’ is not a constant + // expression because it refers to a result of ‘operator new’ +#if 0 + constexpr auto constexpr_key = make_cache_key("perf", { + {"d",""},{"c",""},{"b",""},{"a",""} + }); + EXPECT_EQ(constexpr_key, std::string_view("perf\0a\0\0b\0\0c\0\0d\0\0", 17)); +#endif +} + +} // namespace ceph::perf_counters -- 2.39.5