[libcxx-commits] [libcxx] [libc++] Rewrite the set_intersection benchmark without CartesianBenchmarks (PR #214302)

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Wed Aug 12 04:35:13 PDT 2026


================
@@ -0,0 +1,209 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <set>
+#include <string>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+// Each benchmarking scenario produces the two sorted ranges to intersect, with `n` the size of the
+// larger of the two. The size of the smaller range is derived from `n`, and the ratio between the
+// two appears in the benchmark's name. That ratio is used to exercise implementation choices that
+// make a difference when the two ranges have very different sizes.
+
+// Generate `n` random values and return two independently allocated sorted copies of them.
+//
+// The copy is made before sorting, so that the two copies don't end up with their out-of-line
+// storage laid out in the same order.
+template <class T>
+std::pair<std::vector<T>, std::vector<T>> two_sorted_copies(std::size_t n) {
+  std::vector<T> a(n);
+  std::generate_n(a.begin(), n, [] { return Generate<T>::random(); });
+  std::vector<T> b = a;
+  std::sort(a.begin(), a.end());
+  std::sort(b.begin(), b.end());
+  return std::make_pair(std::move(a), std::move(b));
+}
+
+// Generate two ranges where the larger range holds `n` elements and the smaller one holds every `ratio`th
+// element of it, so the whole smaller range matches and the matches are spread evenly through the larger
+// one. Whether searching within the larger range beats scanning it depends on how far apart the matches
+// are, which is why this scenario is instantiated with several ratios. With a ratio of 1 the two ranges
+// are identical, which is the worst case for the number of elements written to the output.
+template <class T>
+std::pair<std::vector<T>, std::vector<T>> spread(std::size_t n, std::size_t ratio) {
+  auto [large, sampled] = two_sorted_copies<T>(n);
+  std::vector<T> small;
+  small.reserve(n / ratio + 1);
+  for (std::size_t i = 0; i < n; i += ratio)
+    small.push_back(std::move(sampled[i]));
+  return std::make_pair(std::move(large), std::move(small));
+}
+
+int main(int argc, char** argv) {
+  auto bm = []<class Container>(std::string name, auto generate) {
+    return benchmark::RegisterBenchmark(name, [generate](auto& st) {
+      using ValueType        = typename Container::value_type;
+      std::size_t const size = st.range(0);
+
+      // Generate the values, then shuffle them so we don't insert into the containers in sorted order.
+      // Otherwise, node-based containers end up with an artificially good memory layout.
+      auto [values1, values2] = generate(std::type_identity<ValueType>{}, size);
+      std::shuffle(values1.begin(), values1.end(), getRandomEngine());
+      std::shuffle(values2.begin(), values2.end(), getRandomEngine());
----------------
philnik777 wrote:

In that case we should probably consider doing that for the associative container benchmarks as well.

https://github.com/llvm/llvm-project/pull/214302


More information about the libcxx-commits mailing list