[libcxx-commits] [libcxx] [libc++] Make the associative container query benchmarks more representative (PR #183036)
via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Feb 24 04:21:15 PST 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libcxx
Author: Nikolas Klauser (philnik777)
<details>
<summary>Changes</summary>
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.
---
Full diff: https://github.com/llvm/llvm-project/pull/183036.diff
1 Files Affected:
- (modified) libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h (+15-57)
``````````diff
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); }));
}
}
``````````
</details>
https://github.com/llvm/llvm-project/pull/183036
More information about the libcxx-commits
mailing list