]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
common: ceph::perf_counters::make_cache_key()
authorCasey Bodley <cbodley@redhat.com>
Wed, 17 Aug 2022 13:27:42 +0000 (09:27 -0400)
committerAli Maredia <amaredia@redhat.com>
Fri, 28 Oct 2022 12:23:05 +0000 (08:23 -0400)
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 <cbodley@redhat.com>
src/common/perf_counters_cache_key.h [new file with mode: 0644]
src/test/common/CMakeLists.txt
src/test/common/test_perf_cache_key.cc [new file with mode: 0644]

diff --git a/src/common/perf_counters_cache_key.h b/src/common/perf_counters_cache_key.h
new file mode 100644 (file)
index 0000000..6725081
--- /dev/null
@@ -0,0 +1,66 @@
+#pragma once
+
+#include <algorithm>
+#include <iterator>
+#include <numeric>
+#include <string_view>
+#include <utility>
+
+namespace ceph::perf_counters {
+
+/// key/value pair representing a perf counter label
+using label_pair = std::pair<std::string_view, std::string_view>;
+
+
+/// 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 <std::size_t Count>
+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
index 28738c090853822cbaa121379179afd5169c087d..26d27151273615f9032160389c81e849af6ed72b 100644 (file)
@@ -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 (file)
index 0000000..6ba186a
--- /dev/null
@@ -0,0 +1,45 @@
+#include "common/perf_counters_cache_key.h"
+#include <gtest/gtest.h>
+
+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