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

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Tue Aug 11 06:35:27 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());
----------------
ldionne wrote:

I tried inserting into the containers with sorted data and with shuffled data, and it does seem to matter greatly:

```
  ┌──────────────────────────────────┬──────────┬────────┬────────┐
  │               Row                │ Shuffled │ Sorted │   Δ    │
  ├──────────────────────────────────┼──────────┼────────┼────────┤
  │ set<int> (identical)/8192        │ 98566    │ 38353  │ −61.1% │
  ├──────────────────────────────────┼──────────┼────────┼────────┤
  │ set<int> (interleaved)/8192      │ 127301   │ 62505  │ −50.9% │
  ├──────────────────────────────────┼──────────┼────────┼────────┤
  │ set<string> (identical)/8192     │ 1231289  │ 623452 │ −49.4% │
  ├──────────────────────────────────┼──────────┼────────┼────────┤
  │ set<int> (spread 1:8)/8192       │ 66099    │ 33480  │ −49.3% │
  ├──────────────────────────────────┼──────────┼────────┼────────┤
  │ set<string> (disjoint)/8192      │ 87834    │ 44773  │ −49.0% │
  ├──────────────────────────────────┼──────────┼────────┼────────┤
  │ set<int> (disjoint)/8192         │ 46108    │ 23928  │ −48.1% │
  ├──────────────────────────────────┼──────────┼────────┼────────┤
  │ set<int> (spread 1:1024)/8192    │ 58660    │ 31449  │ −46.4% │
  ├──────────────────────────────────┼──────────┼────────┼────────┤
  │ set<string> (interleaved)/8192   │ 282679   │ 153840 │ −45.6% │
  ├──────────────────────────────────┼──────────┼────────┼────────┤
  │ set<string> (spread 1:1024)/8192 │ 59257    │ 33509  │ −43.5% │
  ├──────────────────────────────────┼──────────┼────────┼────────┤
  │ vector<string> (identical)/8192  │ 789643   │ 504544 │ −36.1% │
  ├──────────────────────────────────┼──────────┼────────┼────────┤
  │ set<string> (spread 1:8)/8192    │ 209453   │ 140100 │ −33.1% │
  ├──────────────────────────────────┼──────────┼────────┼────────┤
  │ set<string> (prefix 1:1024)/8192 │ 361.9    │ 306.6  │ −15.3% │
  └──────────────────────────────────┴──────────┴────────┴────────┘
```

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


More information about the libcxx-commits mailing list