[libcxx-commits] [libcxx] [libc++] Make the associative container query benchmarks more representative (PR #183036)

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Tue Feb 24 04:20:41 PST 2026


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/183036

Currently the qeury benchmarks are training the branch predictor incredibly well, which isn't representative of the real world. This change causes the branch misses to go from <1% to ~50% with the current implementation.
This patch also removes the `non-existant` benchmarks, since it'd be non-trivial to write a representative benchmark for that case, and the benchmark would be relatively low value. We're already searching to leaf nodes ~50% of the time (since half the nodes are leaves) with the current benchmark. So we'd only additionally cover a relatively trivial failure branch that is only once taken per function call. The loop is already coverd through benchmarking with keys existing in the container.


>From e96740fa1ab203e94c6fb92cb2ed4d3fcd05f712 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Tue, 24 Feb 2026 13:13:43 +0100
Subject: [PATCH] [libc++] Make the associative container query benchmarks more
 representative

---
 .../associative_container_benchmarks.h        | 72 ++++---------------
 1 file changed, 15 insertions(+), 57 deletions(-)

diff --git a/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h b/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
index d1f55f94196fa..4d0e19bd0bfc1 100644
--- a/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
+++ b/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
@@ -687,74 +687,32 @@ void associative_container_benchmarks(std::string container) {
   /////////////////////////
   // Query
   /////////////////////////
-  auto with_existent_key = [=](auto func) {
+  auto query_bench = [=](auto func) {
     return [=](auto& st) {
       const std::size_t size = st.range(0);
       std::vector<Value> in  = make_value_types(generate_unique_keys(size));
-      // Pick any `BatchSize` number of elements
-      std::vector<Key> keys;
-      for (std::size_t i = 0; i < in.size(); i += (in.size() / BatchSize)) {
-        keys.push_back(get_key(in.at(i)));
-      }
       Container c(in.begin(), in.end());
 
-      while (st.KeepRunningBatch(BatchSize)) {
-        for (std::size_t i = 0; i != keys.size(); ++i) { // possible empty keys when Arg(0)
-          auto result = func(c, keys[i]);
-          benchmark::DoNotOptimize(c);
-          benchmark::DoNotOptimize(result);
-          benchmark::ClobberMemory();
-        }
-      }
-    };
-  };
-
-  auto with_nonexistent_key = [=](auto func) {
-    return [=](auto& st) {
-      const std::size_t size = st.range(0);
-      std::vector<Value> in  = make_value_types(generate_unique_keys(size + BatchSize));
-      std::vector<Key> keys;
-      for (std::size_t i = 0; i != BatchSize; ++i) {
-        keys.push_back(get_key(in.back()));
-        in.pop_back();
-      }
-      Container c(in.begin(), in.end());
-
-      while (st.KeepRunningBatch(BatchSize)) {
-        for (std::size_t i = 0; i != BatchSize; ++i) {
-          auto result = func(c, keys[i]);
-          benchmark::DoNotOptimize(c);
-          benchmark::DoNotOptimize(result);
-          benchmark::ClobberMemory();
-        }
+      for (auto _ : st) {
+        auto result = func(c, get_key(in[getRandomEngine()() % in.size()]));
+        benchmark::DoNotOptimize(c);
+        benchmark::DoNotOptimize(result);
+        benchmark::ClobberMemory();
       }
     };
   };
 
-  auto find = [](Container const& c, Key const& key) { return c.find(key); };
-  bench_non_empty("find(key) (existent)", with_existent_key(find));
-  bench("find(key) (non-existent)", with_nonexistent_key(find));
-
-  auto count = [](Container const& c, Key const& key) { return c.count(key); };
-  bench_non_empty("count(key) (existent)", with_existent_key(count));
-  bench("count(key) (non-existent)", with_nonexistent_key(count));
-
-  auto contains = [](Container const& c, Key const& key) { return c.contains(key); };
-  bench_non_empty("contains(key) (existent)", with_existent_key(contains));
-  bench("contains(key) (non-existent)", with_nonexistent_key(contains));
+  bench_non_empty("find(key)", query_bench([](Container const& c, Key const& key) { return c.find(key); }));
+  bench_non_empty("count(key)", query_bench([](Container const& c, Key const& key) { return c.count(key); }));
+  bench_non_empty("contains(key)", query_bench([](Container const& c, Key const& key) { return c.contains(key); }));
 
   if constexpr (is_ordered_container) {
-    auto lower_bound = [](Container const& c, Key const& key) { return c.lower_bound(key); };
-    bench_non_empty("lower_bound(key) (existent)", with_existent_key(lower_bound));
-    bench("lower_bound(key) (non-existent)", with_nonexistent_key(lower_bound));
-
-    auto upper_bound = [](Container const& c, Key const& key) { return c.upper_bound(key); };
-    bench_non_empty("upper_bound(key) (existent)", with_existent_key(upper_bound));
-    bench("upper_bound(key) (non-existent)", with_nonexistent_key(upper_bound));
-
-    auto equal_range = [](Container const& c, Key const& key) { return c.equal_range(key); };
-    bench_non_empty("equal_range(key) (existent)", with_existent_key(equal_range));
-    bench("equal_range(key) (non-existent)", with_nonexistent_key(equal_range));
+    bench_non_empty(
+        "lower_bound(key)", query_bench([](Container const& c, Key const& key) { return c.lower_bound(key); }));
+    bench_non_empty(
+        "upper_bound(key)", query_bench([](Container const& c, Key const& key) { return c.upper_bound(key); }));
+    bench_non_empty(
+        "equal_range(key)", query_bench([](Container const& c, Key const& key) { return c.equal_range(key); }));
   }
 }
 



More information about the libcxx-commits mailing list