[libcxx-commits] [libcxx] [libc++] Refactor and add benchmarks from [alg.nonmodifying] (PR #128206)

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Mon Mar 17 22:01:44 PDT 2025


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/128206

>From f350b39ed472db8acfc3f19f25abb9730555601e Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 6 Feb 2025 19:13:16 -0500
Subject: [PATCH 01/20] [libc++] Refactor and add benchmarks from
 [alg.nonmodifying]

---
 libcxx/test/benchmarks/GenerateInput.h        |   1 +
 .../benchmarks/algorithms/count.bench.cpp     |  37 ----
 .../benchmarks/algorithms/equal.bench.cpp     |  99 ----------
 .../test/benchmarks/algorithms/find.bench.cpp |  90 ---------
 .../benchmarks/algorithms/for_each.bench.cpp  |  25 ---
 .../benchmarks/algorithms/mismatch.bench.cpp  |  58 ------
 .../nonmodifying/adjacent_find.bench.cpp      |  87 +++++++++
 .../nonmodifying/any_all_none_of.bench.cpp    | 152 +++++++++++++++
 .../nonmodifying/contains.bench.cpp           |  80 ++++++++
 .../nonmodifying/contains_subrange.bench.cpp  |  82 ++++++++
 .../algorithms/nonmodifying/count.bench.cpp   | 156 +++++++++++++++
 .../nonmodifying/ends_with.bench.cpp          | 104 ++++++++++
 .../algorithms/nonmodifying/equal.bench.cpp   | 106 +++++++++++
 .../algorithms/nonmodifying/find.bench.cpp    | 179 ++++++++++++++++++
 .../nonmodifying/find_end.bench.cpp           | 102 ++++++++++
 .../nonmodifying/find_first_of.bench.cpp      | 115 +++++++++++
 .../nonmodifying/find_last.bench.cpp          | 134 +++++++++++++
 .../algorithms/nonmodifying/fold.bench.cpp    |  76 ++++++++
 .../nonmodifying/for_each.bench.cpp           |  57 ++++++
 .../nonmodifying/is_permutation.bench.cpp     | 157 +++++++++++++++
 .../nonmodifying/mismatch.bench.cpp           | 111 +++++++++++
 .../algorithms/nonmodifying/search.bench.cpp  | 137 ++++++++++++++
 .../nonmodifying/search_n.bench.cpp           | 138 ++++++++++++++
 .../nonmodifying/starts_with.bench.cpp        |  69 +++++++
 .../algorithms/ranges_contains.bench.cpp      |  51 -----
 .../algorithms/ranges_ends_with.bench.cpp     | 109 -----------
 26 files changed, 2043 insertions(+), 469 deletions(-)
 delete mode 100644 libcxx/test/benchmarks/algorithms/count.bench.cpp
 delete mode 100644 libcxx/test/benchmarks/algorithms/equal.bench.cpp
 delete mode 100644 libcxx/test/benchmarks/algorithms/find.bench.cpp
 delete mode 100644 libcxx/test/benchmarks/algorithms/for_each.bench.cpp
 delete mode 100644 libcxx/test/benchmarks/algorithms/mismatch.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/adjacent_find.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/any_all_none_of.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/count.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/equal.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/find.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/for_each.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/is_permutation.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/mismatch.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/starts_with.bench.cpp
 delete mode 100644 libcxx/test/benchmarks/algorithms/ranges_contains.bench.cpp
 delete mode 100644 libcxx/test/benchmarks/algorithms/ranges_ends_with.bench.cpp

diff --git a/libcxx/test/benchmarks/GenerateInput.h b/libcxx/test/benchmarks/GenerateInput.h
index 9be76f55c2774..06387852f76a6 100644
--- a/libcxx/test/benchmarks/GenerateInput.h
+++ b/libcxx/test/benchmarks/GenerateInput.h
@@ -13,6 +13,7 @@
 #include <climits>
 #include <concepts>
 #include <cstddef>
+#include <initializer_list>
 #include <random>
 #include <string>
 #include <vector>
diff --git a/libcxx/test/benchmarks/algorithms/count.bench.cpp b/libcxx/test/benchmarks/algorithms/count.bench.cpp
deleted file mode 100644
index 46b85e909efa5..0000000000000
--- a/libcxx/test/benchmarks/algorithms/count.bench.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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 <benchmark/benchmark.h>
-#include <cstring>
-#include <random>
-#include <vector>
-
-static void bm_vector_bool_count(benchmark::State& state) {
-  std::vector<bool> vec1(state.range(), false);
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(vec1);
-    benchmark::DoNotOptimize(std::count(vec1.begin(), vec1.end(), true));
-  }
-}
-BENCHMARK(bm_vector_bool_count)->DenseRange(1, 8)->Range(16, 1 << 20);
-
-static void bm_vector_bool_ranges_count(benchmark::State& state) {
-  std::vector<bool> vec1(state.range(), false);
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(vec1);
-    benchmark::DoNotOptimize(std::ranges::count(vec1.begin(), vec1.end(), true));
-  }
-}
-BENCHMARK(bm_vector_bool_ranges_count)->DenseRange(1, 8)->Range(16, 1 << 20);
-
-BENCHMARK_MAIN();
diff --git a/libcxx/test/benchmarks/algorithms/equal.bench.cpp b/libcxx/test/benchmarks/algorithms/equal.bench.cpp
deleted file mode 100644
index 328b39608607e..0000000000000
--- a/libcxx/test/benchmarks/algorithms/equal.bench.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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 <benchmark/benchmark.h>
-#include <vector>
-
-static void bm_equal_iter(benchmark::State& state) {
-  std::vector<char> vec1(state.range(), '1');
-  std::vector<char> vec2(state.range(), '1');
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(vec1);
-    benchmark::DoNotOptimize(vec2);
-    benchmark::DoNotOptimize(std::equal(vec1.begin(), vec1.end(), vec2.begin()));
-  }
-}
-BENCHMARK(bm_equal_iter)->DenseRange(1, 8)->Range(16, 1 << 20);
-
-static void bm_equal(benchmark::State& state) {
-  std::vector<char> vec1(state.range(), '1');
-  std::vector<char> vec2(state.range(), '1');
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(vec1);
-    benchmark::DoNotOptimize(vec2);
-    benchmark::DoNotOptimize(std::equal(vec1.begin(), vec1.end(), vec2.begin(), vec2.end()));
-  }
-}
-BENCHMARK(bm_equal)->DenseRange(1, 8)->Range(16, 1 << 20);
-
-static void bm_ranges_equal(benchmark::State& state) {
-  std::vector<char> vec1(state.range(), '1');
-  std::vector<char> vec2(state.range(), '1');
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(vec1);
-    benchmark::DoNotOptimize(vec2);
-    benchmark::DoNotOptimize(std::ranges::equal(vec1, vec2));
-  }
-}
-BENCHMARK(bm_ranges_equal)->DenseRange(1, 8)->Range(16, 1 << 20);
-
-static void bm_ranges_equal_vb_aligned(benchmark::State& state) {
-  auto n = state.range();
-  std::vector<bool> vec1(n, true);
-  std::vector<bool> vec2(n, true);
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(std::ranges::equal(vec1, vec2));
-    benchmark::DoNotOptimize(&vec1);
-    benchmark::DoNotOptimize(&vec2);
-  }
-}
-
-static void bm_ranges_equal_vb_unaligned(benchmark::State& state) {
-  auto n = state.range();
-  std::vector<bool> vec1(n, true);
-  std::vector<bool> vec2(n + 8, true);
-  auto beg1 = std::ranges::begin(vec1);
-  auto end1 = std::ranges::end(vec1);
-  auto beg2 = std::ranges::begin(vec2) + 4;
-  auto end2 = std::ranges::end(vec2) - 4;
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(std::ranges::equal(beg1, end1, beg2, end2));
-    benchmark::DoNotOptimize(&vec1);
-    benchmark::DoNotOptimize(&vec2);
-  }
-}
-
-// Test std::ranges::equal for vector<bool>::iterator
-BENCHMARK(bm_ranges_equal_vb_aligned)->RangeMultiplier(4)->Range(8, 1 << 20);
-BENCHMARK(bm_ranges_equal_vb_unaligned)->Range(8, 1 << 20);
-
-static void bm_equal_vb(benchmark::State& state, bool aligned) {
-  auto n = state.range();
-  std::vector<bool> vec1(n, true);
-  std::vector<bool> vec2(aligned ? n : n + 8, true);
-  auto beg1 = vec1.begin();
-  auto end1 = vec1.end();
-  auto beg2 = aligned ? vec2.begin() : vec2.begin() + 4;
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(std::equal(beg1, end1, beg2));
-    benchmark::DoNotOptimize(&vec1);
-    benchmark::DoNotOptimize(&vec2);
-  }
-}
-
-static void bm_equal_vb_aligned(benchmark::State& state) { bm_equal_vb(state, true); }
-static void bm_equal_vb_unaligned(benchmark::State& state) { bm_equal_vb(state, false); }
-
-// Test std::equal for vector<bool>::iterator
-BENCHMARK(bm_equal_vb_aligned)->Range(8, 1 << 20);
-BENCHMARK(bm_equal_vb_unaligned)->Range(8, 1 << 20);
-
-BENCHMARK_MAIN();
diff --git a/libcxx/test/benchmarks/algorithms/find.bench.cpp b/libcxx/test/benchmarks/algorithms/find.bench.cpp
deleted file mode 100644
index 43d103474ebdf..0000000000000
--- a/libcxx/test/benchmarks/algorithms/find.bench.cpp
+++ /dev/null
@@ -1,90 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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 <benchmark/benchmark.h>
-#include <cstring>
-#include <deque>
-#include <random>
-#include <vector>
-
-template <class Container>
-static void bm_find(benchmark::State& state) {
-  using T = Container::value_type;
-
-  Container vec1(state.range(), '1');
-  std::mt19937_64 rng(std::random_device{}());
-
-  for (auto _ : state) {
-    auto idx  = rng() % vec1.size();
-    vec1[idx] = '2';
-    benchmark::DoNotOptimize(vec1);
-    benchmark::DoNotOptimize(std::find(vec1.begin(), vec1.end(), T('2')));
-    vec1[idx] = '1';
-  }
-}
-BENCHMARK(bm_find<std::vector<char>>)->DenseRange(1, 8)->Range(16, 1 << 20);
-BENCHMARK(bm_find<std::vector<short>>)->DenseRange(1, 8)->Range(16, 1 << 20);
-BENCHMARK(bm_find<std::vector<int>>)->DenseRange(1, 8)->Range(16, 1 << 20);
-BENCHMARK(bm_find<std::deque<char>>)->DenseRange(1, 8)->Range(16, 1 << 20);
-BENCHMARK(bm_find<std::deque<short>>)->DenseRange(1, 8)->Range(16, 1 << 20);
-BENCHMARK(bm_find<std::deque<int>>)->DenseRange(1, 8)->Range(16, 1 << 20);
-
-template <class Container>
-static void bm_ranges_find(benchmark::State& state) {
-  using T = Container::value_type;
-
-  Container vec1(state.range(), '1');
-  std::mt19937_64 rng(std::random_device{}());
-
-  for (auto _ : state) {
-    auto idx  = rng() % vec1.size();
-    vec1[idx] = '2';
-    benchmark::DoNotOptimize(vec1);
-    benchmark::DoNotOptimize(std::ranges::find(vec1, T('2')));
-    vec1[idx] = '1';
-  }
-}
-BENCHMARK(bm_ranges_find<std::vector<char>>)->DenseRange(1, 8)->Range(16, 1 << 20);
-BENCHMARK(bm_ranges_find<std::vector<short>>)->DenseRange(1, 8)->Range(16, 1 << 20);
-BENCHMARK(bm_ranges_find<std::vector<int>>)->DenseRange(1, 8)->Range(16, 1 << 20);
-BENCHMARK(bm_ranges_find<std::deque<char>>)->DenseRange(1, 8)->Range(16, 1 << 20);
-BENCHMARK(bm_ranges_find<std::deque<short>>)->DenseRange(1, 8)->Range(16, 1 << 20);
-BENCHMARK(bm_ranges_find<std::deque<int>>)->DenseRange(1, 8)->Range(16, 1 << 20);
-
-static void bm_vector_bool_find(benchmark::State& state) {
-  std::vector<bool> vec1(state.range(), false);
-  std::mt19937_64 rng(std::random_device{}());
-
-  for (auto _ : state) {
-    auto idx  = rng() % vec1.size();
-    vec1[idx] = true;
-    benchmark::DoNotOptimize(vec1);
-    benchmark::DoNotOptimize(std::find(vec1.begin(), vec1.end(), true));
-    vec1[idx] = false;
-  }
-}
-BENCHMARK(bm_vector_bool_find)->DenseRange(1, 8)->Range(16, 1 << 20);
-
-static void bm_vector_bool_ranges_find(benchmark::State& state) {
-  std::vector<bool> vec1(state.range(), false);
-  std::mt19937_64 rng(std::random_device{}());
-
-  for (auto _ : state) {
-    auto idx  = rng() % vec1.size();
-    vec1[idx] = true;
-    benchmark::DoNotOptimize(vec1);
-    benchmark::DoNotOptimize(std::ranges::find(vec1, true));
-    vec1[idx] = false;
-  }
-}
-BENCHMARK(bm_vector_bool_ranges_find)->DenseRange(1, 8)->Range(16, 1 << 20);
-
-BENCHMARK_MAIN();
diff --git a/libcxx/test/benchmarks/algorithms/for_each.bench.cpp b/libcxx/test/benchmarks/algorithms/for_each.bench.cpp
deleted file mode 100644
index 554c9ec993043..0000000000000
--- a/libcxx/test/benchmarks/algorithms/for_each.bench.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-#include <algorithm>
-#include <benchmark/benchmark.h>
-#include <deque>
-
-static void bm_deque_for_each(benchmark::State& state) {
-  std::deque<char> vec1(state.range(), '1');
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(vec1);
-    benchmark::DoNotOptimize(
-        std::for_each(vec1.begin(), vec1.end(), [](char& v) { v = std::clamp(v, (char)10, (char)100); }));
-  }
-}
-BENCHMARK(bm_deque_for_each)->DenseRange(1, 8)->Range(16, 1 << 20);
-
-BENCHMARK_MAIN();
diff --git a/libcxx/test/benchmarks/algorithms/mismatch.bench.cpp b/libcxx/test/benchmarks/algorithms/mismatch.bench.cpp
deleted file mode 100644
index 348009a230d6e..0000000000000
--- a/libcxx/test/benchmarks/algorithms/mismatch.bench.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-
-#include <algorithm>
-#include <benchmark/benchmark.h>
-#include <random>
-
-void BenchmarkSizes(benchmark::internal::Benchmark* Benchmark) {
-  Benchmark->DenseRange(1, 8);
-  for (size_t i = 16; i != 1 << 20; i *= 2) {
-    Benchmark->Arg(i - 1);
-    Benchmark->Arg(i);
-    Benchmark->Arg(i + 1);
-  }
-}
-
-// TODO: Look into benchmarking aligned and unaligned memory explicitly
-// (currently things happen to be aligned because they are malloced that way)
-template <class T>
-static void bm_mismatch(benchmark::State& state) {
-  std::vector<T> vec1(state.range(), '1');
-  std::vector<T> vec2(state.range(), '1');
-  std::mt19937_64 rng(std::random_device{}());
-
-  vec1.back() = '2';
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(vec1);
-    benchmark::DoNotOptimize(std::mismatch(vec1.begin(), vec1.end(), vec2.begin()));
-  }
-}
-BENCHMARK(bm_mismatch<char>)->Apply(BenchmarkSizes);
-BENCHMARK(bm_mismatch<short>)->Apply(BenchmarkSizes);
-BENCHMARK(bm_mismatch<int>)->Apply(BenchmarkSizes);
-
-template <class T>
-static void bm_mismatch_two_range_overload(benchmark::State& state) {
-  std::vector<T> vec1(state.range(), '1');
-  std::vector<T> vec2(state.range(), '1');
-  std::mt19937_64 rng(std::random_device{}());
-
-  vec1.back() = '2';
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(vec1);
-    benchmark::DoNotOptimize(std::mismatch(vec1.begin(), vec1.end(), vec2.begin(), vec2.end()));
-  }
-}
-BENCHMARK(bm_mismatch_two_range_overload<char>)->DenseRange(1, 8)->Range(16, 1 << 20);
-BENCHMARK(bm_mismatch_two_range_overload<short>)->DenseRange(1, 8)->Range(16, 1 << 20);
-BENCHMARK(bm_mismatch_two_range_overload<int>)->DenseRange(1, 8)->Range(16, 1 << 20);
-
-BENCHMARK_MAIN();
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/adjacent_find.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/adjacent_find.bench.cpp
new file mode 100644
index 0000000000000..1f6e20dfa4527
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/adjacent_find.bench.cpp
@@ -0,0 +1,87 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <list>
+#include <string>
+#include <vector>
+
+#include <benchmark/benchmark.h>
+#include "../../GenerateInput.h"
+
+int main(int argc, char** argv) {
+  auto std_adjacent_find      = [](auto first, auto last) { return std::adjacent_find(first, last); };
+  auto std_adjacent_find_pred = [](auto first, auto last) {
+    return std::adjacent_find(first, last, [](auto x, auto y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x == y;
+    });
+  };
+  auto ranges_adjacent_find_pred = [](auto first, auto last) {
+    return std::ranges::adjacent_find(first, last, [](auto x, auto y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x == y;
+    });
+  };
+
+  // Benchmark {std,ranges}::adjacent_find on a sequence of the form xyxyxyxyxyxyxyxyxyxy,
+  // which means we never find adjacent equal elements (the worst case of the algorithm).
+  {
+    auto bm = []<class Container>(std::string name, auto adjacent_find) {
+      benchmark::RegisterBenchmark(
+          name,
+          [adjacent_find](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            ValueType x            = Generate<ValueType>::random();
+            ValueType y            = random_different_from({x});
+            Container c;
+            for (std::size_t i = 0; i != size; ++i) {
+              c.push_back(i % 2 == 0 ? x : y);
+            }
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              auto result = adjacent_find(c.begin(), c.end());
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(1024)
+          ->Arg(8192)
+          ->Arg(1 << 20);
+    };
+
+    // {std,ranges}::adjacent_find
+    bm.operator()<std::vector<int>>("std::adjacent_find(vector<int>)", std_adjacent_find);
+    bm.operator()<std::deque<int>>("std::adjacent_find(deque<int>)", std_adjacent_find);
+    bm.operator()<std::list<int>>("std::adjacent_find(list<int>)", std_adjacent_find);
+    bm.operator()<std::vector<int>>("rng::adjacent_find(vector<int>)", std::ranges::adjacent_find);
+    bm.operator()<std::deque<int>>("rng::adjacent_find(deque<int>)", std::ranges::adjacent_find);
+    bm.operator()<std::list<int>>("rng::adjacent_find(list<int>)", std::ranges::adjacent_find);
+
+    // {std,ranges}::adjacent_find(pred)
+    bm.operator()<std::vector<int>>("std::adjacent_find(vector<int>, pred)", std_adjacent_find_pred);
+    bm.operator()<std::deque<int>>("std::adjacent_find(deque<int>, pred)", std_adjacent_find_pred);
+    bm.operator()<std::list<int>>("std::adjacent_find(list<int>, pred)", std_adjacent_find_pred);
+    bm.operator()<std::vector<int>>("rng::adjacent_find(vector<int>, pred)", ranges_adjacent_find_pred);
+    bm.operator()<std::deque<int>>("rng::adjacent_find(deque<int>, pred)", ranges_adjacent_find_pred);
+    bm.operator()<std::list<int>>("rng::adjacent_find(list<int>, pred)", ranges_adjacent_find_pred);
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/any_all_none_of.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/any_all_none_of.bench.cpp
new file mode 100644
index 0000000000000..363ed96f6d201
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/any_all_none_of.bench.cpp
@@ -0,0 +1,152 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <list>
+#include <numeric>
+#include <string>
+#include <vector>
+
+#include <benchmark/benchmark.h>
+#include "../../GenerateInput.h"
+
+int main(int argc, char** argv) {
+  auto std_any_of = [](auto first, auto last, auto pred) { return std::any_of(first, last, pred); };
+  auto std_all_of = [](auto first, auto last, auto pred) {
+    // match semantics of any_of
+    return !std::all_of(first, last, [pred](auto x) { return !pred(x); });
+  };
+  auto std_none_of = [](auto first, auto last, auto pred) {
+    // match semantics of any_of
+    return !std::none_of(first, last, pred);
+  };
+
+  auto ranges_all_of = [](auto first, auto last, auto pred) {
+    // match semantics of any_of
+    return !std::ranges::all_of(first, last, [pred](auto x) { return !pred(x); });
+  };
+  auto ranges_none_of = [](auto first, auto last, auto pred) {
+    // match semantics of any_of
+    return !std::ranges::none_of(first, last, pred);
+  };
+
+  // Benchmark {std,ranges}::{any_of,all_of,none_of} where we bail out early
+  // (after visiting 25% of the elements).
+  {
+    auto bm = []<class Container>(std::string name, auto any_of) {
+      benchmark::RegisterBenchmark(
+          name,
+          [any_of](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            ValueType x            = Generate<ValueType>::random();
+            ValueType y            = random_different_from({x});
+            Container c(size, x);
+            *std::next(c.begin(), size / 4) = y; // bail out after the first 25% elements
+
+            for (auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              auto result = any_of(c.begin(), c.end(), [&](auto element) {
+                benchmark::DoNotOptimize(element);
+                return element == y;
+              });
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(32)
+          ->Arg(8192)
+          ->Arg(32768);
+    };
+
+    // any_of
+    bm.operator()<std::vector<int>>("std::any_of(vector<int>) (bail 25%)", std_any_of);
+    bm.operator()<std::deque<int>>("std::any_of(deque<int>) (bail 25%)", std_any_of);
+    bm.operator()<std::list<int>>("std::any_of(list<int>) (bail 25%)", std_any_of);
+    bm.operator()<std::vector<int>>("rng::any_of(vector<int>) (bail 25%)", std::ranges::any_of);
+    bm.operator()<std::deque<int>>("rng::any_of(deque<int>) (bail 25%)", std::ranges::any_of);
+    bm.operator()<std::list<int>>("rng::any_of(list<int>) (bail 25%)", std::ranges::any_of);
+
+    // all_of
+    bm.operator()<std::vector<int>>("std::all_of(vector<int>) (bail 25%)", std_all_of);
+    bm.operator()<std::deque<int>>("std::all_of(deque<int>) (bail 25%)", std_all_of);
+    bm.operator()<std::list<int>>("std::all_of(list<int>) (bail 25%)", std_all_of);
+    bm.operator()<std::vector<int>>("rng::all_of(vector<int>) (bail 25%)", ranges_all_of);
+    bm.operator()<std::deque<int>>("rng::all_of(deque<int>) (bail 25%)", ranges_all_of);
+    bm.operator()<std::list<int>>("rng::all_of(list<int>) (bail 25%)", ranges_all_of);
+
+    // none_of
+    bm.operator()<std::vector<int>>("std::none_of(vector<int>) (bail 25%)", std_none_of);
+    bm.operator()<std::deque<int>>("std::none_of(deque<int>) (bail 25%)", std_none_of);
+    bm.operator()<std::list<int>>("std::none_of(list<int>) (bail 25%)", std_none_of);
+    bm.operator()<std::vector<int>>("rng::none_of(vector<int>) (bail 25%)", ranges_none_of);
+    bm.operator()<std::deque<int>>("rng::none_of(deque<int>) (bail 25%)", ranges_none_of);
+    bm.operator()<std::list<int>>("rng::none_of(list<int>) (bail 25%)", ranges_none_of);
+  }
+
+  // Benchmark {std,ranges}::{any_of,all_of,none_of} where we process the whole sequence.
+  {
+    auto bm = []<class Container>(std::string name, auto any_of) {
+      benchmark::RegisterBenchmark(
+          name,
+          [any_of](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            ValueType x            = Generate<ValueType>::random();
+            ValueType y            = random_different_from({x});
+            Container c(size, x);
+
+            for (auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              auto result = any_of(c.begin(), c.end(), [&](auto element) {
+                benchmark::DoNotOptimize(element);
+                return element == y;
+              });
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(32)
+          ->Arg(8192)
+          ->Arg(32768);
+    };
+
+    // any_of
+    bm.operator()<std::vector<int>>("std::any_of(vector<int>) (process all)", std_any_of);
+    bm.operator()<std::deque<int>>("std::any_of(deque<int>) (process all)", std_any_of);
+    bm.operator()<std::list<int>>("std::any_of(list<int>) (process all)", std_any_of);
+    bm.operator()<std::vector<int>>("rng::any_of(vector<int>) (process all)", std::ranges::any_of);
+    bm.operator()<std::deque<int>>("rng::any_of(deque<int>) (process all)", std::ranges::any_of);
+    bm.operator()<std::list<int>>("rng::any_of(list<int>) (process all)", std::ranges::any_of);
+
+    // all_of
+    bm.operator()<std::vector<int>>("std::all_of(vector<int>) (process all)", std_all_of);
+    bm.operator()<std::deque<int>>("std::all_of(deque<int>) (process all)", std_all_of);
+    bm.operator()<std::list<int>>("std::all_of(list<int>) (process all)", std_all_of);
+    bm.operator()<std::vector<int>>("rng::all_of(vector<int>) (process all)", ranges_all_of);
+    bm.operator()<std::deque<int>>("rng::all_of(deque<int>) (process all)", ranges_all_of);
+    bm.operator()<std::list<int>>("rng::all_of(list<int>) (process all)", ranges_all_of);
+
+    // none_of
+    bm.operator()<std::vector<int>>("std::none_of(vector<int>) (process all)", std_none_of);
+    bm.operator()<std::deque<int>>("std::none_of(deque<int>) (process all)", std_none_of);
+    bm.operator()<std::list<int>>("std::none_of(list<int>) (process all)", std_none_of);
+    bm.operator()<std::vector<int>>("rng::none_of(vector<int>) (process all)", ranges_none_of);
+    bm.operator()<std::deque<int>>("rng::none_of(deque<int>) (process all)", ranges_none_of);
+    bm.operator()<std::list<int>>("rng::none_of(list<int>) (process all)", ranges_none_of);
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp
new file mode 100644
index 0000000000000..c0fbae11a218a
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp
@@ -0,0 +1,80 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <list>
+#include <vector>
+
+#include <benchmark/benchmark.h>
+
+int main(int argc, char** argv) {
+  // Benchmark ranges::contains where we bail out early (after visiting 25% of the elements).
+  {
+    auto bm = []<class Container>(std::string name) {
+      benchmark::RegisterBenchmark(
+          name,
+          [](auto& st) {
+            std::size_t const size = st.range(0);
+            Container c(size, 1);
+            *std::next(c.begin(), size / 4) = 42; // bail out after checking 25% of values
+            auto first                      = c.begin();
+            auto last                       = c.end();
+
+            for (auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              auto result = std::ranges::contains(first, last, 42);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(32)
+          ->Arg(8192)
+          ->Arg(1 << 20);
+    };
+    bm.operator()<std::vector<int>>("rng::contains(vector<int>) (bail 25%)");
+    bm.operator()<std::deque<int>>("rng::contains(deque<int>) (bail 25%)");
+    bm.operator()<std::list<int>>("rng::contains(list<int>) (bail 25%)");
+  }
+
+  // Benchmark ranges::contains where we process the whole sequence.
+  {
+    auto bm = []<class Container>(std::string name) {
+      benchmark::RegisterBenchmark(
+          name,
+          [](auto& st) {
+            std::size_t const size = st.range(0);
+            Container c(size, 1);
+            auto first = c.begin();
+            auto last  = c.end();
+
+            for (auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              auto result = std::ranges::contains(first, last, 42);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(32)
+          ->Arg(8192)
+          ->Arg(1 << 20);
+    };
+    bm.operator()<std::vector<int>>("rng::contains(vector<int>) (process all)");
+    bm.operator()<std::deque<int>>("rng::contains(deque<int>) (process all)");
+    bm.operator()<std::list<int>>("rng::contains(list<int>) (process all)");
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp
new file mode 100644
index 0000000000000..6b585acc7c752
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp
@@ -0,0 +1,82 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <list>
+#include <vector>
+
+#include <benchmark/benchmark.h>
+
+int main(int argc, char** argv) {
+  // Benchmark ranges::contains_subrange where we find our target starting at 25% of the elements
+  {
+    auto bm = []<class Container>(std::string name) {
+      benchmark::RegisterBenchmark(
+          name,
+          [](auto& st) {
+            std::size_t const size = st.range(0);
+            Container c(size, 1);
+            Container subrange(size / 10, 42); // subrange of length 10% of the full range
+
+            // At 25% of the range, put the subrange we're going to find
+            std::ranges::copy(subrange, std::next(c.begin(), c.size() / 4));
+
+            for (auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              benchmark::DoNotOptimize(subrange);
+              auto result = std::ranges::contains_subrange(c, subrange);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(16)
+          ->Arg(32)
+          ->Arg(8192)
+          ->Arg(1 << 20);
+    };
+    bm.operator()<std::vector<int>>("rng::contains_subrange(vector<int>) (bail 25%)");
+    bm.operator()<std::deque<int>>("rng::contains_subrange(deque<int>) (bail 25%)");
+    bm.operator()<std::list<int>>("rng::contains_subrange(list<int>) (bail 25%)");
+  }
+
+  // Benchmark ranges::contains_subrange where we never find our target
+  {
+    auto bm = []<class Container>(std::string name) {
+      benchmark::RegisterBenchmark(
+          name,
+          [](auto& st) {
+            std::size_t const size = st.range(0);
+            Container c(size, 1);
+            Container subrange(size / 10, 42); // subrange of length 10% of the full range, but we'll never find it
+
+            for (auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              benchmark::DoNotOptimize(subrange);
+              auto result = std::ranges::contains_subrange(c, subrange);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(16)
+          ->Arg(32)
+          ->Arg(8192)
+          ->Arg(1 << 20);
+    };
+    bm.operator()<std::vector<int>>("rng::contains_subrange(vector<int>) (process all)");
+    bm.operator()<std::deque<int>>("rng::contains_subrange(deque<int>) (process all)");
+    bm.operator()<std::list<int>>("rng::contains_subrange(list<int>) (process all)");
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/count.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/count.bench.cpp
new file mode 100644
index 0000000000000..13d8f9f2690e9
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/count.bench.cpp
@@ -0,0 +1,156 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <list>
+#include <string>
+#include <vector>
+
+#include <benchmark/benchmark.h>
+#include "../../GenerateInput.h"
+
+int main(int argc, char** argv) {
+  auto std_count    = [](auto first, auto last, auto const& value) { return std::count(first, last, value); };
+  auto std_count_if = [](auto first, auto last, auto const& value) {
+    return std::count_if(first, last, [&](auto element) {
+      benchmark::DoNotOptimize(element);
+      return element == value;
+    });
+  };
+
+  auto ranges_count = [](auto first, auto last, auto const& value) { return std::ranges::count(first, last, value); };
+  auto ranges_count_if = [](auto first, auto last, auto const& value) {
+    return std::ranges::count_if(first, last, [&](auto element) {
+      benchmark::DoNotOptimize(element);
+      return element == value;
+    });
+  };
+
+  // Benchmark {std,ranges}::{count,count_if} on a sequence where every other element is counted.
+  {
+    auto bm = []<class Container>(std::string name, auto count) {
+      benchmark::RegisterBenchmark(
+          name,
+          [count](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            ValueType x            = Generate<ValueType>::random();
+            ValueType y            = random_different_from({x});
+            Container c;
+            for (std::size_t i = 0; i != size; ++i) {
+              c.push_back(i % 2 == 0 ? x : y);
+            }
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              benchmark::DoNotOptimize(x);
+              auto result = count(c.begin(), c.end(), x);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(1024)
+          ->Arg(8192)
+          ->Arg(1 << 20);
+    };
+
+    // count
+    bm.operator()<std::vector<int>>("std::count(vector<int>) (every other)", std_count);
+    bm.operator()<std::deque<int>>("std::count(deque<int>) (every other)", std_count);
+    bm.operator()<std::list<int>>("std::count(list<int>) (every other)", std_count);
+    bm.operator()<std::vector<int>>("rng::count(vector<int>) (every other)", ranges_count);
+    bm.operator()<std::deque<int>>("rng::count(deque<int>) (every other)", ranges_count);
+    bm.operator()<std::list<int>>("rng::count(list<int>) (every other)", ranges_count);
+
+    // count_if
+    bm.operator()<std::vector<int>>("std::count_if(vector<int>) (every other)", std_count_if);
+    bm.operator()<std::deque<int>>("std::count_if(deque<int>) (every other)", std_count_if);
+    bm.operator()<std::list<int>>("std::count_if(list<int>) (every other)", std_count_if);
+    bm.operator()<std::vector<int>>("rng::count_if(vector<int>) (every other)", ranges_count_if);
+    bm.operator()<std::deque<int>>("rng::count_if(deque<int>) (every other)", ranges_count_if);
+    bm.operator()<std::list<int>>("rng::count_if(list<int>) (every other)", ranges_count_if);
+  }
+
+  // Benchmark {std,ranges}::{count,count_if} on a sequence where only a few elements are counted.
+  // In theory, we could blaze through contiguous sequences where there are no elements to count.
+  {
+    auto bm = []<class Container>(std::string name, auto count) {
+      benchmark::RegisterBenchmark(
+          name,
+          [count](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            ValueType x            = Generate<ValueType>::random();
+            ValueType y            = random_different_from({x});
+            Container c;
+            for (std::size_t i = 0; i != size; ++i) {
+              c.push_back(i % (size / 5) == 0 ? x : y); // intersperse 5 elements to count at regular intervals
+            }
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              benchmark::DoNotOptimize(x);
+              auto result = count(c.begin(), c.end(), x);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(1024)
+          ->Arg(8192)
+          ->Arg(1 << 20);
+    };
+
+    // count
+    bm.operator()<std::vector<int>>("std::count(vector<int>) (sparse)", std_count);
+    bm.operator()<std::deque<int>>("std::count(deque<int>) (sparse)", std_count);
+    bm.operator()<std::list<int>>("std::count(list<int>) (sparse)", std_count);
+    bm.operator()<std::vector<int>>("rng::count(vector<int>) (sparse)", ranges_count);
+    bm.operator()<std::deque<int>>("rng::count(deque<int>) (sparse)", ranges_count);
+    bm.operator()<std::list<int>>("rng::count(list<int>) (sparse)", ranges_count);
+
+    // count_if
+    bm.operator()<std::vector<int>>("std::count_if(vector<int>) (sparse)", std_count_if);
+    bm.operator()<std::deque<int>>("std::count_if(deque<int>) (sparse)", std_count_if);
+    bm.operator()<std::list<int>>("std::count_if(list<int>) (sparse)", std_count_if);
+    bm.operator()<std::vector<int>>("rng::count_if(vector<int>) (sparse)", ranges_count_if);
+    bm.operator()<std::deque<int>>("rng::count_if(deque<int>) (sparse)", ranges_count_if);
+    bm.operator()<std::list<int>>("rng::count_if(list<int>) (sparse)", ranges_count_if);
+  }
+
+  // Benchmark {std,ranges}::count(vector<bool>)
+  {
+    auto bm = [](std::string name, auto count) {
+      benchmark::RegisterBenchmark(
+          name,
+          [count](auto& st) {
+            std::size_t const size = st.range(0);
+            std::vector<bool> c(size, false);
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              auto result = count(c.begin(), c.end(), true);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(1024)
+          ->Arg(8192)
+          ->Arg(1 << 20);
+    };
+    bm.operator()("std::count(vector<bool>)", std_count);
+    bm.operator()("rng::count(vector<bool>)", ranges_count);
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp
new file mode 100644
index 0000000000000..08ff693916f67
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp
@@ -0,0 +1,104 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <list>
+#include <string>
+#include <vector>
+
+#include <benchmark/benchmark.h>
+#include "../../GenerateInput.h"
+
+int main(int argc, char** argv) {
+  auto ranges_ends_with_pred = [](auto first1, auto last1, auto first2, auto last2) {
+    return std::ranges::ends_with(first1, last1, first2, last2, [](auto x, auto y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x == y;
+    });
+  };
+
+  // Benchmark ranges::ends_with where we find the mismatching element at the very end.
+  {
+    auto bm = []<class Container>(std::string name, auto ends_with) {
+      benchmark::RegisterBenchmark(
+          name,
+          [ends_with](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            ValueType x            = Generate<ValueType>::random();
+            ValueType y            = random_different_from({x});
+            Container c1(size, x);
+            Container c2(size, x);
+            c2.back() = y;
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c1);
+              benchmark::DoNotOptimize(c2);
+              auto result = ends_with(c1.begin(), c1.end(), c2.begin(), c2.end());
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(1024)
+          ->Arg(8192)
+          ->Arg(1 << 20);
+    };
+    bm.operator()<std::vector<int>>("rng::ends_with(vector<int>) (mismatch at end)", std::ranges::ends_with);
+    bm.operator()<std::deque<int>>("rng::ends_with(deque<int>) (mismatch at end)", std::ranges::ends_with);
+    bm.operator()<std::list<int>>("rng::ends_with(list<int>) (mismatch at end)", std::ranges::ends_with);
+
+    bm.operator()<std::vector<int>>("rng::ends_with(vector<int>, pred) (mismatch at end)", ranges_ends_with_pred);
+    bm.operator()<std::deque<int>>("rng::ends_with(deque<int>, pred) (mismatch at end)", ranges_ends_with_pred);
+    bm.operator()<std::list<int>>("rng::ends_with(list<int>, pred) (mismatch at end)", ranges_ends_with_pred);
+  }
+
+  // Benchmark ranges::ends_with where we find the mismatching element at the very beginning.
+  {
+    auto bm = []<class Container>(std::string name, auto ends_with) {
+      benchmark::RegisterBenchmark(
+          name,
+          [ends_with](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            ValueType x            = Generate<ValueType>::random();
+            ValueType y            = random_different_from({x});
+            Container c1(size, x);
+            Container c2(size, x);
+            c2.front() = y;
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c1);
+              benchmark::DoNotOptimize(c2);
+              auto result = ends_with(c1.begin(), c1.end(), c2.begin(), c2.end());
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(1024)
+          ->Arg(8192)
+          ->Arg(1 << 20);
+    };
+    bm.operator()<std::vector<int>>("rng::ends_with(vector<int>) (mismatch at start)", std::ranges::ends_with);
+    bm.operator()<std::deque<int>>("rng::ends_with(deque<int>) (mismatch at start)", std::ranges::ends_with);
+    bm.operator()<std::list<int>>("rng::ends_with(list<int>) (mismatch at start)", std::ranges::ends_with);
+
+    bm.operator()<std::vector<int>>("rng::ends_with(vector<int>, pred) (mismatch at start)", ranges_ends_with_pred);
+    bm.operator()<std::deque<int>>("rng::ends_with(deque<int>, pred) (mismatch at start)", ranges_ends_with_pred);
+    bm.operator()<std::list<int>>("rng::ends_with(list<int>, pred) (mismatch at start)", ranges_ends_with_pred);
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/equal.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/equal.bench.cpp
new file mode 100644
index 0000000000000..ad73ba3610abf
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/equal.bench.cpp
@@ -0,0 +1,106 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <list>
+#include <string>
+#include <vector>
+
+#include <benchmark/benchmark.h>
+#include "../../GenerateInput.h"
+
+int main(int argc, char** argv) {
+  auto std_equal_3leg = [](auto first1, auto last1, auto first2, auto) { return std::equal(first1, last1, first2); };
+  auto std_equal_4leg = [](auto first1, auto last1, auto first2, auto last2) {
+    return std::equal(first1, last1, first2, last2);
+  };
+  auto std_equal_3leg_pred = [](auto first1, auto last1, auto first2, auto) {
+    return std::equal(first1, last1, first2, [](auto x, auto y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x == y;
+    });
+  };
+  auto std_equal_4leg_pred = [](auto first1, auto last1, auto first2, auto last2) {
+    return std::equal(first1, last1, first2, last2, [](auto x, auto y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x == y;
+    });
+  };
+  auto ranges_equal_4leg_pred = [](auto first1, auto last1, auto first2, auto last2) {
+    return std::ranges::equal(first1, last1, first2, last2, [](auto x, auto y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x == y;
+    });
+  };
+
+  // Benchmark {std,ranges}::equal where we determine inequality at the very end (worst case).
+  {
+    auto bm = []<class Container>(std::string name, auto equal) {
+      benchmark::RegisterBenchmark(
+          name,
+          [equal](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            ValueType x            = Generate<ValueType>::random();
+            ValueType y            = random_different_from({x});
+            Container c1(size, x);
+            Container c2(size, x);
+            c2.back() = y;
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c1);
+              benchmark::DoNotOptimize(c2);
+              auto result = equal(c1.begin(), c1.end(), c2.begin(), c2.end());
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(1024)
+          ->Arg(8192)
+          ->Arg(1 << 20);
+    };
+
+    // std::equal(it, it, it)
+    bm.operator()<std::vector<int>>("std::equal(vector<int>) (it, it, it)", std_equal_3leg);
+    bm.operator()<std::deque<int>>("std::equal(deque<int>) (it, it, it)", std_equal_3leg);
+    bm.operator()<std::list<int>>("std::equal(list<int>) (it, it, it)", std_equal_3leg);
+
+    // std::equal(it, it, it, pred)
+    bm.operator()<std::vector<int>>("std::equal(vector<int>) (it, it, it, pred)", std_equal_3leg_pred);
+    bm.operator()<std::deque<int>>("std::equal(deque<int>) (it, it, it, pred)", std_equal_3leg_pred);
+    bm.operator()<std::list<int>>("std::equal(list<int>) (it, it, it, pred)", std_equal_3leg_pred);
+
+    // {std,ranges}::equal(it, it, it, it)
+    bm.operator()<std::vector<int>>("std::equal(vector<int>) (it, it, it, it)", std_equal_4leg);
+    bm.operator()<std::deque<int>>("std::equal(deque<int>) (it, it, it, it)", std_equal_4leg);
+    bm.operator()<std::list<int>>("std::equal(list<int>) (it, it, it, it)", std_equal_4leg);
+    bm.operator()<std::vector<int>>("rng::equal(vector<int>) (it, it, it, it)", std::ranges::equal);
+    bm.operator()<std::deque<int>>("rng::equal(deque<int>) (it, it, it, it)", std::ranges::equal);
+    bm.operator()<std::list<int>>("rng::equal(list<int>) (it, it, it, it)", std::ranges::equal);
+
+    // {std,ranges}::equal(it, it, it, it, pred)
+    bm.operator()<std::vector<int>>("std::equal(vector<int>) (it, it, it, it, pred)", std_equal_4leg_pred);
+    bm.operator()<std::deque<int>>("std::equal(deque<int>) (it, it, it, it, pred)", std_equal_4leg_pred);
+    bm.operator()<std::list<int>>("std::equal(list<int>) (it, it, it, it, pred)", std_equal_4leg_pred);
+    bm.operator()<std::vector<int>>("rng::equal(vector<int>) (it, it, it, it, pred)", ranges_equal_4leg_pred);
+    bm.operator()<std::deque<int>>("rng::equal(deque<int>) (it, it, it, it, pred)", ranges_equal_4leg_pred);
+    bm.operator()<std::list<int>>("rng::equal(list<int>) (it, it, it, it, pred)", ranges_equal_4leg_pred);
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find.bench.cpp
new file mode 100644
index 0000000000000..3be7df0ac1131
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find.bench.cpp
@@ -0,0 +1,179 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <list>
+#include <string>
+#include <vector>
+
+#include <benchmark/benchmark.h>
+#include "../../GenerateInput.h"
+
+int main(int argc, char** argv) {
+  auto std_find    = [](auto first, auto last, auto const& value) { return std::find(first, last, value); };
+  auto std_find_if = [](auto first, auto last, auto const& value) {
+    return std::find_if(first, last, [&](auto element) {
+      benchmark::DoNotOptimize(element);
+      return element == value;
+    });
+  };
+  auto std_find_if_not = [](auto first, auto last, auto const& value) {
+    return std::find_if_not(first, last, [&](auto element) {
+      benchmark::DoNotOptimize(element);
+      return element != value;
+    });
+  };
+
+  auto ranges_find    = [](auto first, auto last, auto const& value) { return std::ranges::find(first, last, value); };
+  auto ranges_find_if = [](auto first, auto last, auto const& value) {
+    return std::ranges::find_if(first, last, [&](auto element) {
+      benchmark::DoNotOptimize(element);
+      return element == value;
+    });
+  };
+  auto ranges_find_if_not = [](auto first, auto last, auto const& value) {
+    return std::ranges::find_if_not(first, last, [&](auto element) {
+      benchmark::DoNotOptimize(element);
+      return element != value;
+    });
+  };
+
+  auto register_benchmarks = [&](auto bm, std::string comment) {
+    // find
+    bm.template operator()<std::vector<char>>("std::find(vector<char>) (" + comment + ")", std_find);
+    bm.template operator()<std::vector<int>>("std::find(vector<int>) (" + comment + ")", std_find);
+    bm.template operator()<std::deque<int>>("std::find(deque<int>) (" + comment + ")", std_find);
+    bm.template operator()<std::list<int>>("std::find(list<int>) (" + comment + ")", std_find);
+
+    bm.template operator()<std::vector<char>>("rng::find(vector<char>) (" + comment + ")", ranges_find);
+    bm.template operator()<std::vector<int>>("rng::find(vector<int>) (" + comment + ")", ranges_find);
+    bm.template operator()<std::deque<int>>("rng::find(deque<int>) (" + comment + ")", ranges_find);
+    bm.template operator()<std::list<int>>("rng::find(list<int>) (" + comment + ")", ranges_find);
+
+    // find_if
+    bm.template operator()<std::vector<char>>("std::find_if(vector<char>) (" + comment + ")", std_find_if);
+    bm.template operator()<std::vector<int>>("std::find_if(vector<int>) (" + comment + ")", std_find_if);
+    bm.template operator()<std::deque<int>>("std::find_if(deque<int>) (" + comment + ")", std_find_if);
+    bm.template operator()<std::list<int>>("std::find_if(list<int>) (" + comment + ")", std_find_if);
+
+    bm.template operator()<std::vector<char>>("rng::find_if(vector<char>) (" + comment + ")", ranges_find_if);
+    bm.template operator()<std::vector<int>>("rng::find_if(vector<int>) (" + comment + ")", ranges_find_if);
+    bm.template operator()<std::deque<int>>("rng::find_if(deque<int>) (" + comment + ")", ranges_find_if);
+    bm.template operator()<std::list<int>>("rng::find_if(list<int>) (" + comment + ")", ranges_find_if);
+
+    // find_if_not
+    bm.template operator()<std::vector<char>>("std::find_if_not(vector<char>) (" + comment + ")", std_find_if_not);
+    bm.template operator()<std::vector<int>>("std::find_if_not(vector<int>) (" + comment + ")", std_find_if_not);
+    bm.template operator()<std::deque<int>>("std::find_if_not(deque<int>) (" + comment + ")", std_find_if_not);
+    bm.template operator()<std::list<int>>("std::find_if_not(list<int>) (" + comment + ")", std_find_if_not);
+
+    bm.template operator()<std::vector<char>>("rng::find_if_not(vector<char>) (" + comment + ")", ranges_find_if_not);
+    bm.template operator()<std::vector<int>>("rng::find_if_not(vector<int>) (" + comment + ")", ranges_find_if_not);
+    bm.template operator()<std::deque<int>>("rng::find_if_not(deque<int>) (" + comment + ")", ranges_find_if_not);
+    bm.template operator()<std::list<int>>("rng::find_if_not(list<int>) (" + comment + ")", ranges_find_if_not);
+  };
+
+  // Benchmark {std,ranges}::{find,find_if,find_if_not}(normal container) where we
+  // bail out after 25% of elements
+  {
+    auto bm = []<class Container>(std::string name, auto find) {
+      benchmark::RegisterBenchmark(
+          name,
+          [find](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            ValueType x            = Generate<ValueType>::random();
+            ValueType y            = random_different_from({x});
+            Container c(size, x);
+
+            // put the element we're searching for at 25% of the sequence
+            *std::next(c.begin(), size / 4) = y;
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              benchmark::DoNotOptimize(y);
+              auto result = find(c.begin(), c.end(), y);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(1024)
+          ->Arg(8192)
+          ->Arg(1 << 15);
+    };
+    register_benchmarks(bm, "bail 25%");
+  }
+
+  // Benchmark {std,ranges}::{find,find_if,find_if_not}(normal container) where we process the whole sequence
+  {
+    auto bm = []<class Container>(std::string name, auto find) {
+      benchmark::RegisterBenchmark(
+          name,
+          [find](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            ValueType x            = Generate<ValueType>::random();
+            ValueType y            = random_different_from({x});
+            Container c(size, x);
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              benchmark::DoNotOptimize(y);
+              auto result = find(c.begin(), c.end(), y);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(1024)
+          ->Arg(8192)
+          ->Arg(1 << 15);
+    };
+    register_benchmarks(bm, "process all");
+  }
+
+  // Benchmark {std,ranges}::{find,find_if,find_if_not}(vector<bool>) where we process the whole sequence
+  {
+    auto bm = [](std::string name, auto find) {
+      benchmark::RegisterBenchmark(
+          name,
+          [find](auto& st) {
+            std::size_t const size = st.range(0);
+            std::vector<bool> c(size, true);
+            bool y = false;
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              benchmark::DoNotOptimize(y);
+              auto result = find(c.begin(), c.end(), y);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(1024)
+          ->Arg(8192)
+          ->Arg(1 << 20);
+    };
+    bm("std::find(vector<bool>) (process all)", std_find);
+    bm("rng::find(vector<bool>) (process all)", ranges_find);
+
+    bm("std::find_if(vector<bool>) (process all)", std_find_if);
+    bm("rng::find_if(vector<bool>) (process all)", ranges_find_if);
+
+    bm("std::find_if_not(vector<bool>) (process all)", std_find_if_not);
+    bm("rng::find_if_not(vector<bool>) (process all)", ranges_find_if_not);
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp
new file mode 100644
index 0000000000000..70576acd79792
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp
@@ -0,0 +1,102 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <list>
+#include <string>
+#include <vector>
+
+#include <benchmark/benchmark.h>
+#include "../../GenerateInput.h"
+
+int main(int argc, char** argv) {
+  auto std_find_end = [](auto first1, auto last1, auto first2, auto last2) {
+    return std::find_end(first1, last1, first2, last2);
+  };
+
+  // Benchmark {std,ranges}::find_end where the subsequence is found
+  // 25% into the sequence
+  {
+    auto bm = []<class Container>(std::string name, auto find_end) {
+      benchmark::RegisterBenchmark(
+          name,
+          [find_end](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            ValueType x            = Generate<ValueType>::random();
+            ValueType y            = random_different_from({x});
+            Container c(size, x);
+            Container subrange(size / 10, y); // subrange of length 10% of the full range
+
+            // put the element we're searching for at 25% of the sequence
+            std::ranges::copy(subrange, std::next(c.begin(), c.size() / 4));
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              benchmark::DoNotOptimize(subrange);
+              auto result = find_end(c.begin(), c.end(), subrange.begin(), subrange.end());
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(1024)
+          ->Arg(8192)
+          ->Arg(1 << 20);
+    };
+    bm.operator()<std::vector<int>>("std::find_end(vector<int>) (bail 25%)", std_find_end);
+    bm.operator()<std::deque<int>>("std::find_end(deque<int>) (bail 25%)", std_find_end);
+    bm.operator()<std::list<int>>("std::find_end(list<int>) (bail 25%)", std_find_end);
+    bm.operator()<std::vector<int>>("rng::find_end(vector<int>) (bail 25%)", std::ranges::find_end);
+    bm.operator()<std::deque<int>>("rng::find_end(deque<int>) (bail 25%)", std::ranges::find_end);
+    bm.operator()<std::list<int>>("rng::find_end(list<int>) (bail 25%)", std::ranges::find_end);
+  }
+
+  // Benchmark {std,ranges}::find_end where the subsequence is found
+  // 90% into the sequence (i.e. near the end)
+  {
+    auto bm = []<class Container>(std::string name, auto find_end) {
+      benchmark::RegisterBenchmark(
+          name,
+          [find_end](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            ValueType x            = Generate<ValueType>::random();
+            ValueType y            = random_different_from({x});
+            Container c(size, x);
+            Container subrange(size / 10, y); // subrange of length 10% of the full range
+
+            // put the element we're searching for at 90% of the sequence
+            std::ranges::copy(subrange, std::next(c.begin(), 9 * (c.size() / 10)));
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              benchmark::DoNotOptimize(subrange);
+              auto result = find_end(c.begin(), c.end(), subrange.begin(), subrange.end());
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(1024)
+          ->Arg(8192)
+          ->Arg(1 << 20);
+    };
+    bm.operator()<std::vector<int>>("std::find_end(vector<int>) (bail 90%)", std_find_end);
+    bm.operator()<std::deque<int>>("std::find_end(deque<int>) (bail 90%)", std_find_end);
+    bm.operator()<std::list<int>>("std::find_end(list<int>) (bail 90%)", std_find_end);
+    bm.operator()<std::vector<int>>("rng::find_end(vector<int>) (bail 90%)", std::ranges::find_end);
+    bm.operator()<std::deque<int>>("rng::find_end(deque<int>) (bail 90%)", std::ranges::find_end);
+    bm.operator()<std::list<int>>("rng::find_end(list<int>) (bail 90%)", std::ranges::find_end);
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp
new file mode 100644
index 0000000000000..cf83c2761232c
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp
@@ -0,0 +1,115 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <initializer_list>
+#include <list>
+#include <string>
+#include <vector>
+
+#include <benchmark/benchmark.h>
+#include "../../GenerateInput.h"
+
+int main(int argc, char** argv) {
+  auto std_find_first_of = [](auto first1, auto last1, auto first2, auto last2) {
+    return std::find_first_of(first1, last1, first2, last2);
+  };
+
+  // Benchmark {std,ranges}::find_first_of where we have a hit at 25% of the haystack
+  // and at the end of the needle. This measures how quickly we're able to search inside
+  // the needle.
+  {
+    auto bm = []<class Container>(std::string name, auto find_first_of) {
+      benchmark::RegisterBenchmark(
+          name,
+          [find_first_of](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            ValueType x            = Generate<ValueType>::random();
+            ValueType y            = random_different_from({x});
+            ValueType z            = random_different_from({x, y});
+            Container haystack(size, x);
+            Container needle(size, y);
+            needle.back() = z; // hit at the very end of the needle
+
+            // put the needle at 25% of the haystack
+            *std::next(haystack.begin(), haystack.size() / 4) = z;
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(haystack);
+              benchmark::DoNotOptimize(needle);
+              auto result = find_first_of(haystack.begin(), haystack.end(), needle.begin(), needle.end());
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(32)
+          ->Arg(1024)
+          ->Arg(8192);
+    };
+    bm.operator()<std::vector<int>>("std::find_first_of(vector<int>) (25% haystack, late needle)", std_find_first_of);
+    bm.operator()<std::deque<int>>("std::find_first_of(deque<int>) (25% haystack, late needle)", std_find_first_of);
+    bm.operator()<std::list<int>>("std::find_first_of(list<int>) (25% haystack, late needle)", std_find_first_of);
+    bm.operator()<std::vector<int>>(
+        "rng::find_first_of(vector<int>) (25% haystack, late needle)", std::ranges::find_first_of);
+    bm.operator()<std::deque<int>>(
+        "rng::find_first_of(deque<int>) (25% haystack, late needle)", std::ranges::find_first_of);
+    bm.operator()<std::list<int>>(
+        "rng::find_first_of(list<int>) (25% haystack, late needle)", std::ranges::find_first_of);
+  }
+
+  // Benchmark {std,ranges}::find_first_of where we have a hit at 90% of the haystack
+  // but at the beginning of the needle. This measures how quickly we're able to search
+  // inside the haystack.
+  {
+    auto bm = []<class Container>(std::string name, auto find_first_of) {
+      benchmark::RegisterBenchmark(
+          name,
+          [find_first_of](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            ValueType x            = Generate<ValueType>::random();
+            ValueType y            = random_different_from({x});
+            ValueType z            = random_different_from({x, y});
+            Container haystack(size, x);
+            Container needle(size, y);
+            *std::next(needle.begin(), needle.size() / 10) = z; // hit at 10% of the needle
+
+            // put the needle at 90% of the haystack
+            *std::next(haystack.begin(), 9 * (haystack.size() / 10)) = z;
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(haystack);
+              benchmark::DoNotOptimize(needle);
+              auto result = find_first_of(haystack.begin(), haystack.end(), needle.begin(), needle.end());
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(32)
+          ->Arg(1024)
+          ->Arg(8192);
+    };
+    bm.operator()<std::vector<int>>("std::find_first_of(vector<int>) (90% haystack, early needle)", std_find_first_of);
+    bm.operator()<std::deque<int>>("std::find_first_of(deque<int>) (90% haystack, early needle)", std_find_first_of);
+    bm.operator()<std::list<int>>("std::find_first_of(list<int>) (90% haystack, early needle)", std_find_first_of);
+    bm.operator()<std::vector<int>>(
+        "rng::find_first_of(vector<int>) (90% haystack, early needle)", std::ranges::find_first_of);
+    bm.operator()<std::deque<int>>(
+        "rng::find_first_of(deque<int>) (90% haystack, early needle)", std::ranges::find_first_of);
+    bm.operator()<std::list<int>>(
+        "rng::find_first_of(list<int>) (90% haystack, early needle)", std::ranges::find_first_of);
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp
new file mode 100644
index 0000000000000..536d7d4f0350c
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp
@@ -0,0 +1,134 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <list>
+#include <string>
+#include <vector>
+
+#include <benchmark/benchmark.h>
+#include "../../GenerateInput.h"
+
+int main(int argc, char** argv) {
+  auto ranges_find_last_if = [](auto first, auto last, auto const& value) {
+    return std::ranges::find_last_if(first, last, [&](auto element) {
+      benchmark::DoNotOptimize(element);
+      return element == value;
+    });
+  };
+  auto ranges_find_last_if_not = [](auto first, auto last, auto const& value) {
+    return std::ranges::find_last_if_not(first, last, [&](auto element) {
+      benchmark::DoNotOptimize(element);
+      return element != value;
+    });
+  };
+
+  // Benchmark ranges::{find_last,find_last_if,find_last_if_not} where the last element
+  // is found 25% into the sequence
+  {
+    auto bm = []<class Container>(std::string name, auto find_last) {
+      benchmark::RegisterBenchmark(
+          name,
+          [find_last](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            ValueType x            = Generate<ValueType>::random();
+            ValueType y            = random_different_from({x});
+            Container c(size, x);
+
+            // put the element we're searching for at 25% of the sequence
+            *std::next(c.begin(), size / 4) = y;
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              benchmark::DoNotOptimize(y);
+              auto result = find_last(c.begin(), c.end(), y);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(1024)
+          ->Arg(8192)
+          ->Arg(1 << 20);
+    };
+
+    // find_last
+    bm.operator()<std::vector<char>>("rng::find_last(vector<char>) (bail 25%)", std::ranges::find_last);
+    bm.operator()<std::vector<int>>("rng::find_last(vector<int>) (bail 25%)", std::ranges::find_last);
+    bm.operator()<std::deque<int>>("rng::find_last(deque<int>) (bail 25%)", std::ranges::find_last);
+    bm.operator()<std::list<int>>("rng::find_last(list<int>) (bail 25%)", std::ranges::find_last);
+
+    // find_last_if
+    bm.operator()<std::vector<char>>("rng::find_last_if(vector<char>) (bail 25%)", ranges_find_last_if);
+    bm.operator()<std::vector<int>>("rng::find_last_if(vector<int>) (bail 25%)", ranges_find_last_if);
+    bm.operator()<std::deque<int>>("rng::find_last_if(deque<int>) (bail 25%)", ranges_find_last_if);
+    bm.operator()<std::list<int>>("rng::find_last_if(list<int>) (bail 25%)", ranges_find_last_if);
+
+    // find_last_if_not
+    bm.operator()<std::vector<char>>("rng::find_last_if_not(vector<char>) (bail 25%)", ranges_find_last_if_not);
+    bm.operator()<std::vector<int>>("rng::find_last_if_not(vector<int>) (bail 25%)", ranges_find_last_if_not);
+    bm.operator()<std::deque<int>>("rng::find_last_if_not(deque<int>) (bail 25%)", ranges_find_last_if_not);
+    bm.operator()<std::list<int>>("rng::find_last_if_not(list<int>) (bail 25%)", ranges_find_last_if_not);
+  }
+
+  // Benchmark ranges::{find_last,find_last_if,find_last_if_not} where the last element
+  // is found 90% into the sequence (i.e. near the end)
+  {
+    auto bm = []<class Container>(std::string name, auto find_last) {
+      benchmark::RegisterBenchmark(
+          name,
+          [find_last](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            ValueType x            = Generate<ValueType>::random();
+            ValueType y            = random_different_from({x});
+            Container c(size, x);
+
+            // put the element we're searching for at 90% of the sequence
+            *std::next(c.begin(), 9 * (size / 10)) = y;
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              benchmark::DoNotOptimize(y);
+              auto result = find_last(c.begin(), c.end(), y);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(1024)
+          ->Arg(8192)
+          ->Arg(1 << 20);
+    };
+    // find_last
+    bm.operator()<std::vector<char>>("rng::find_last(vector<char>) (bail 90%)", std::ranges::find_last);
+    bm.operator()<std::vector<int>>("rng::find_last(vector<int>) (bail 90%)", std::ranges::find_last);
+    bm.operator()<std::deque<int>>("rng::find_last(deque<int>) (bail 90%)", std::ranges::find_last);
+    bm.operator()<std::list<int>>("rng::find_last(list<int>) (bail 90%)", std::ranges::find_last);
+
+    // find_last_if
+    bm.operator()<std::vector<char>>("rng::find_last_if(vector<char>) (bail 90%)", ranges_find_last_if);
+    bm.operator()<std::vector<int>>("rng::find_last_if(vector<int>) (bail 90%)", ranges_find_last_if);
+    bm.operator()<std::deque<int>>("rng::find_last_if(deque<int>) (bail 90%)", ranges_find_last_if);
+    bm.operator()<std::list<int>>("rng::find_last_if(list<int>) (bail 90%)", ranges_find_last_if);
+
+    // find_last_if_not
+    bm.operator()<std::vector<char>>("rng::find_last_if_not(vector<char>) (bail 90%)", ranges_find_last_if_not);
+    bm.operator()<std::vector<int>>("rng::find_last_if_not(vector<int>) (bail 90%)", ranges_find_last_if_not);
+    bm.operator()<std::deque<int>>("rng::find_last_if_not(deque<int>) (bail 90%)", ranges_find_last_if_not);
+    bm.operator()<std::list<int>>("rng::find_last_if_not(list<int>) (bail 90%)", ranges_find_last_if_not);
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp
new file mode 100644
index 0000000000000..d9b6dbedf3186
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp
@@ -0,0 +1,76 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <cassert>
+#include <cstddef>
+#include <deque>
+#include <limits>
+#include <list>
+#include <string>
+#include <vector>
+
+#include <benchmark/benchmark.h>
+#include "../../GenerateInput.h"
+
+int main(int argc, char** argv) {
+  // ranges::{fold_left,fold_right}
+  {
+    auto bm = []<class Container>(std::string name, auto fold) {
+      benchmark::RegisterBenchmark(
+          name,
+          [fold](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            ValueType const limit  = 1000; // ensure we never overflow in the addition
+            assert(size <= std::numeric_limits<ValueType>::max());
+            assert(std::numeric_limits<ValueType>::max() > static_cast<ValueType>(size) * limit);
+            assert(std::numeric_limits<ValueType>::min() < static_cast<ValueType>(size) * limit * -1);
+
+            Container c;
+            std::generate_n(std::back_inserter(c), size, [&] {
+              return std::clamp(Generate<ValueType>::random(), -1 * limit, limit);
+            });
+            ValueType init = c.back();
+            c.pop_back();
+
+            auto f = [](auto x, auto y) {
+              benchmark::DoNotOptimize(x);
+              benchmark::DoNotOptimize(y);
+              return x + y;
+            };
+
+            for (auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              benchmark::DoNotOptimize(init);
+              auto result = fold(c.begin(), c.end(), init, f);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(32)
+          ->Arg(8192)
+          ->Arg(1 << 20);
+    };
+    bm.operator()<std::vector<int>>("rng::fold_left(vector<int>)", std::ranges::fold_left);
+    bm.operator()<std::deque<int>>("rng::fold_left(deque<int>)", std::ranges::fold_left);
+    bm.operator()<std::list<int>>("rng::fold_left(list<int>)", std::ranges::fold_left);
+
+    // fold_right not implemented yet
+    // bm.operator()<std::vector<int>>("rng::fold_right(vector<int>)", std::ranges::fold_right);
+    // bm.operator()<std::deque<int>>("rng::fold_right(deque<int>)", std::ranges::fold_right);
+    // bm.operator()<std::list<int>>("rng::fold_right(list<int>)", std::ranges::fold_right);
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/for_each.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/for_each.bench.cpp
new file mode 100644
index 0000000000000..693403bc8f671
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/for_each.bench.cpp
@@ -0,0 +1,57 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <list>
+#include <string>
+#include <vector>
+
+#include <benchmark/benchmark.h>
+
+int main(int argc, char** argv) {
+  auto std_for_each = [](auto first, auto last, auto f) { return std::for_each(first, last, f); };
+
+  // {std,ranges}::for_each
+  {
+    auto bm = []<class Container>(std::string name, auto for_each) {
+      benchmark::RegisterBenchmark(
+          name,
+          [for_each](auto& st) {
+            std::size_t const size = st.range(0);
+            Container c(size, 1);
+            auto first = c.begin();
+            auto last  = c.end();
+
+            for (auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              auto result = for_each(first, last, [](int& x) { x = std::clamp(x, 10, 100); });
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(32)
+          ->Arg(8192)
+          ->Arg(1 << 20);
+    };
+    bm.operator()<std::vector<int>>("std::for_each(vector<int>)", std_for_each);
+    bm.operator()<std::deque<int>>("std::for_each(deque<int>)", std_for_each);
+    bm.operator()<std::list<int>>("std::for_each(list<int>)", std_for_each);
+    bm.operator()<std::vector<int>>("rng::for_each(vector<int>)", std::ranges::for_each);
+    bm.operator()<std::deque<int>>("rng::for_each(deque<int>)", std::ranges::for_each);
+    bm.operator()<std::list<int>>("rng::for_each(list<int>)", std::ranges::for_each);
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/is_permutation.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/is_permutation.bench.cpp
new file mode 100644
index 0000000000000..41d18ec3d9c1a
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/is_permutation.bench.cpp
@@ -0,0 +1,157 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <list>
+#include <string>
+#include <vector>
+
+#include <benchmark/benchmark.h>
+#include "../../GenerateInput.h"
+
+int main(int argc, char** argv) {
+  auto std_is_permutation_3leg = [](auto first1, auto last1, auto first2, auto) {
+    return std::is_permutation(first1, last1, first2);
+  };
+  auto std_is_permutation_4leg = [](auto first1, auto last1, auto first2, auto last2) {
+    return std::is_permutation(first1, last1, first2, last2);
+  };
+  auto std_is_permutation_3leg_pred = [](auto first1, auto last1, auto first2, auto) {
+    return std::is_permutation(first1, last1, first2, [](auto& x, auto& y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x == y;
+    });
+  };
+  auto std_is_permutation_4leg_pred = [](auto first1, auto last1, auto first2, auto last2) {
+    return std::is_permutation(first1, last1, first2, last2, [](auto& x, auto& y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x == y;
+    });
+  };
+  auto ranges_is_permutation_4leg_pred = [](auto first1, auto last1, auto first2, auto last2) {
+    return std::ranges::is_permutation(first1, last1, first2, last2, [](auto& x, auto& y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x == y;
+    });
+  };
+
+  auto register_benchmarks = [&](auto bm, std::string comment) {
+    // std::is_permutation(it, it, it)
+    bm.template operator()<std::vector<int>>(
+        "std::is_permutation(vector<int>) (3leg) (" + comment + ")", std_is_permutation_3leg);
+    bm.template operator()<std::deque<int>>(
+        "std::is_permutation(deque<int>) (3leg) (" + comment + ")", std_is_permutation_3leg);
+    bm.template operator()<std::list<int>>(
+        "std::is_permutation(list<int>) (3leg) (" + comment + ")", std_is_permutation_3leg);
+
+    // std::is_permutation(it, it, it, pred)
+    bm.template operator()<std::vector<int>>(
+        "std::is_permutation(vector<int>) (3leg, pred) (" + comment + ")", std_is_permutation_3leg_pred);
+    bm.template operator()<std::deque<int>>(
+        "std::is_permutation(deque<int>) (3leg, pred) (" + comment + ")", std_is_permutation_3leg_pred);
+    bm.template operator()<std::list<int>>(
+        "std::is_permutation(list<int>) (3leg, pred) (" + comment + ")", std_is_permutation_3leg_pred);
+
+    // {std,ranges}::is_permutation(it, it, it, it)
+    bm.template operator()<std::vector<int>>(
+        "std::is_permutation(vector<int>) (4leg) (" + comment + ")", std_is_permutation_4leg);
+    bm.template operator()<std::deque<int>>(
+        "std::is_permutation(deque<int>) (4leg) (" + comment + ")", std_is_permutation_4leg);
+    bm.template operator()<std::list<int>>(
+        "std::is_permutation(list<int>) (4leg) (" + comment + ")", std_is_permutation_4leg);
+    bm.template operator()<std::vector<int>>(
+        "rng::is_permutation(vector<int>) (4leg) (" + comment + ")", std::ranges::is_permutation);
+    bm.template operator()<std::deque<int>>(
+        "rng::is_permutation(deque<int>) (4leg) (" + comment + ")", std::ranges::is_permutation);
+    bm.template operator()<std::list<int>>(
+        "rng::is_permutation(list<int>) (4leg) (" + comment + ")", std::ranges::is_permutation);
+
+    // {std,ranges}::is_permutation(it, it, it, it, pred)
+    bm.template operator()<std::vector<int>>(
+        "std::is_permutation(vector<int>) (4leg, pred) (" + comment + ")", std_is_permutation_4leg_pred);
+    bm.template operator()<std::deque<int>>(
+        "std::is_permutation(deque<int>) (4leg, pred) (" + comment + ")", std_is_permutation_4leg_pred);
+    bm.template operator()<std::list<int>>(
+        "std::is_permutation(list<int>) (4leg, pred) (" + comment + ")", std_is_permutation_4leg_pred);
+    bm.template operator()<std::vector<int>>(
+        "rng::is_permutation(vector<int>) (4leg, pred) (" + comment + ")", ranges_is_permutation_4leg_pred);
+    bm.template operator()<std::deque<int>>(
+        "rng::is_permutation(deque<int>) (4leg, pred) (" + comment + ")", ranges_is_permutation_4leg_pred);
+    bm.template operator()<std::list<int>>(
+        "rng::is_permutation(list<int>) (4leg, pred) (" + comment + ")", ranges_is_permutation_4leg_pred);
+  };
+
+  // Benchmark {std,ranges}::is_permutation where both sequences share a common prefix (this can be optimized).
+  {
+    auto bm = []<class Container>(std::string name, auto is_permutation) {
+      benchmark::RegisterBenchmark(
+          name,
+          [is_permutation](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            ValueType x            = Generate<ValueType>::random();
+            ValueType y            = random_different_from({x});
+            Container c1(size, x);
+            Container c2(size, x);
+            c2.back() = y;
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c1);
+              benchmark::DoNotOptimize(c2);
+              auto result = is_permutation(c1.begin(), c1.end(), c2.begin(), c2.end());
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(1024)
+          ->Arg(8192);
+    };
+    register_benchmarks(bm, "common prefix");
+  }
+
+  // Benchmark {std,ranges}::is_permutation on fully shuffled sequences.
+  {
+    auto bm = []<class Container>(std::string name, auto is_permutation) {
+      benchmark::RegisterBenchmark(
+          name,
+          [is_permutation](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            std::vector<ValueType> data;
+            std::generate_n(std::back_inserter(data), size, [] { return Generate<ValueType>::random(); });
+            Container c1(data.begin(), data.end());
+
+            std::mt19937 rng;
+            std::shuffle(data.begin(), data.end(), rng);
+            Container c2(data.begin(), data.end());
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c1);
+              benchmark::DoNotOptimize(c2);
+              auto result = is_permutation(c1.begin(), c1.end(), c2.begin(), c2.end());
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(1024); // this one is very slow, no need for large sequences
+    };
+    register_benchmarks(bm, "shuffled");
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/mismatch.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/mismatch.bench.cpp
new file mode 100644
index 0000000000000..630701a0440c8
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/mismatch.bench.cpp
@@ -0,0 +1,111 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <list>
+#include <string>
+#include <vector>
+
+#include <benchmark/benchmark.h>
+#include "../../GenerateInput.h"
+
+int main(int argc, char** argv) {
+  auto std_mismatch_3leg = [](auto first1, auto last1, auto first2, auto) {
+    return std::mismatch(first1, last1, first2);
+  };
+  auto std_mismatch_4leg = [](auto first1, auto last1, auto first2, auto last2) {
+    return std::mismatch(first1, last1, first2, last2);
+  };
+  auto std_mismatch_3leg_pred = [](auto first1, auto last1, auto first2, auto) {
+    return std::mismatch(first1, last1, first2, [](auto& x, auto& y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x == y;
+    });
+  };
+  auto std_mismatch_4leg_pred = [](auto first1, auto last1, auto first2, auto last2) {
+    return std::mismatch(first1, last1, first2, last2, [](auto& x, auto& y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x == y;
+    });
+  };
+  auto ranges_mismatch_4leg_pred = [](auto first1, auto last1, auto first2, auto last2) {
+    return std::ranges::mismatch(first1, last1, first2, last2, [](auto& x, auto& y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x == y;
+    });
+  };
+
+  // Benchmark {std,ranges}::mismatch where we find the mismatching element at the very end (worst case).
+  //
+  // TODO: Look into benchmarking aligned and unaligned memory explicitly
+  // (currently things happen to be aligned because they are malloced that way)
+  {
+    auto bm = []<class Container>(std::string name, auto mismatch) {
+      benchmark::RegisterBenchmark(
+          name,
+          [mismatch](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            ValueType x            = Generate<ValueType>::random();
+            ValueType y            = random_different_from({x});
+            Container c1(size, x);
+            Container c2(size, x);
+            c2.back() = y;
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c1);
+              benchmark::DoNotOptimize(c2);
+              auto result = mismatch(c1.begin(), c1.end(), c2.begin(), c2.end());
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(1024)
+          ->Arg(8192)
+          ->Arg(1 << 20);
+    };
+
+    // std::mismatch(it, it, it)
+    bm.operator()<std::vector<int>>("std::mismatch(vector<int>) (it, it, it)", std_mismatch_3leg);
+    bm.operator()<std::deque<int>>("std::mismatch(deque<int>) (it, it, it)", std_mismatch_3leg);
+    bm.operator()<std::list<int>>("std::mismatch(list<int>) (it, it, it)", std_mismatch_3leg);
+
+    // std::mismatch(it, it, it, pred)
+    bm.operator()<std::vector<int>>("std::mismatch(vector<int>) (it, it, it, pred)", std_mismatch_3leg_pred);
+    bm.operator()<std::deque<int>>("std::mismatch(deque<int>) (it, it, it, pred)", std_mismatch_3leg_pred);
+    bm.operator()<std::list<int>>("std::mismatch(list<int>) (it, it, it, pred)", std_mismatch_3leg_pred);
+
+    // {std,ranges}::mismatch(it, it, it, it)
+    bm.operator()<std::vector<int>>("std::mismatch(vector<int>) (it, it, it, it)", std_mismatch_4leg);
+    bm.operator()<std::deque<int>>("std::mismatch(deque<int>) (it, it, it, it)", std_mismatch_4leg);
+    bm.operator()<std::list<int>>("std::mismatch(list<int>) (it, it, it, it)", std_mismatch_4leg);
+    bm.operator()<std::vector<int>>("rng::mismatch(vector<int>) (it, it, it, it)", std::ranges::mismatch);
+    bm.operator()<std::deque<int>>("rng::mismatch(deque<int>) (it, it, it, it)", std::ranges::mismatch);
+    bm.operator()<std::list<int>>("rng::mismatch(list<int>) (it, it, it, it)", std::ranges::mismatch);
+
+    // {std,ranges}::mismatch(it, it, it, it, pred)
+    bm.operator()<std::vector<int>>("std::mismatch(vector<int>) (it, it, it, it, pred)", std_mismatch_4leg_pred);
+    bm.operator()<std::deque<int>>("std::mismatch(deque<int>) (it, it, it, it, pred)", std_mismatch_4leg_pred);
+    bm.operator()<std::list<int>>("std::mismatch(list<int>) (it, it, it, it, pred)", std_mismatch_4leg_pred);
+    bm.operator()<std::vector<int>>("rng::mismatch(vector<int>) (it, it, it, it, pred)", ranges_mismatch_4leg_pred);
+    bm.operator()<std::deque<int>>("rng::mismatch(deque<int>) (it, it, it, it, pred)", ranges_mismatch_4leg_pred);
+    bm.operator()<std::list<int>>("rng::mismatch(list<int>) (it, it, it, it, pred)", ranges_mismatch_4leg_pred);
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp
new file mode 100644
index 0000000000000..6c6a26509cc62
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp
@@ -0,0 +1,137 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <cassert>
+#include <cstddef>
+#include <deque>
+#include <list>
+#include <string>
+#include <vector>
+
+#include <benchmark/benchmark.h>
+#include "../../GenerateInput.h"
+
+int main(int argc, char** argv) {
+  auto std_search = [](auto first1, auto last1, auto first2, auto last2) {
+    return std::search(first1, last1, first2, last2);
+  };
+  auto std_search_pred = [](auto first1, auto last1, auto first2, auto last2) {
+    return std::search(first1, last1, first2, last2, [](auto& x, auto& y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x == y;
+    });
+  };
+  auto ranges_search_pred = [](auto first1, auto last1, auto first2, auto last2) {
+    return std::ranges::search(first1, last1, first2, last2, [](auto& x, auto& y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x == y;
+    });
+  };
+
+  // Benchmark {std,ranges}::search where the needle is never found
+  {
+    auto bm = []<class Container>(std::string name, auto search) {
+      benchmark::RegisterBenchmark(
+          name,
+          [search](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            ValueType x            = Generate<ValueType>::random();
+            ValueType y            = random_different_from({x});
+            Container haystack(size, x);
+            Container needle(size / 10, y); // needle size is 10% of the haystack
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(haystack);
+              benchmark::DoNotOptimize(needle);
+              auto result = search(haystack.begin(), haystack.end(), needle.begin(), needle.end());
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(1024)
+          ->Arg(8192)
+          ->Arg(1 << 20);
+    };
+    // {std,ranges}::search
+    bm.operator()<std::vector<int>>("std::search(vector<int>) (no match)", std_search);
+    bm.operator()<std::deque<int>>("std::search(deque<int>) (no match)", std_search);
+    bm.operator()<std::list<int>>("std::search(list<int>) (no match)", std_search);
+    bm.operator()<std::vector<int>>("rng::search(vector<int>) (no match)", std::ranges::search);
+    bm.operator()<std::deque<int>>("rng::search(deque<int>) (no match)", std::ranges::search);
+    bm.operator()<std::list<int>>("rng::search(list<int>) (no match)", std::ranges::search);
+
+    // {std,ranges}::search(pred)
+    bm.operator()<std::vector<int>>("std::search(vector<int>, pred) (no match)", std_search_pred);
+    bm.operator()<std::deque<int>>("std::search(deque<int>, pred) (no match)", std_search_pred);
+    bm.operator()<std::list<int>>("std::search(list<int>, pred) (no match)", std_search_pred);
+    bm.operator()<std::vector<int>>("rng::search(vector<int>, pred) (no match)", ranges_search_pred);
+    bm.operator()<std::deque<int>>("rng::search(deque<int>, pred) (no match)", ranges_search_pred);
+    bm.operator()<std::list<int>>("rng::search(list<int>, pred) (no match)", ranges_search_pred);
+  }
+
+  // Benchmark {std,ranges}::search where we intersperse "near matches" inside the haystack
+  {
+    auto bm = []<class Container>(std::string name, auto search) {
+      benchmark::RegisterBenchmark(
+          name,
+          [search](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            ValueType x            = Generate<ValueType>::random();
+            ValueType y            = random_different_from({x});
+            Container haystack(size, x);
+            std::size_t n = size / 10; // needle size is 10% of the haystack
+            assert(n > 0);
+            Container needle(n, y);
+
+            // intersperse near-matches inside the haystack
+            {
+              auto first = haystack.begin();
+              for (int i = 0; i != 10; ++i) {
+                first = std::copy_n(needle.begin(), n - 1, first);
+                ++first; // this causes the subsequence not to match because it has length n-1
+              }
+            }
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(haystack);
+              benchmark::DoNotOptimize(needle);
+              auto result = search(haystack.begin(), haystack.end(), needle.begin(), needle.end());
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(1024)
+          ->Arg(8192);
+    };
+    // {std,ranges}::search
+    bm.operator()<std::vector<int>>("std::search(vector<int>) (near matches)", std_search);
+    bm.operator()<std::deque<int>>("std::search(deque<int>) (near matches)", std_search);
+    bm.operator()<std::list<int>>("std::search(list<int>) (near matches)", std_search);
+    bm.operator()<std::vector<int>>("rng::search(vector<int>) (near matches)", std::ranges::search);
+    bm.operator()<std::deque<int>>("rng::search(deque<int>) (near matches)", std::ranges::search);
+    bm.operator()<std::list<int>>("rng::search(list<int>) (near matches)", std::ranges::search);
+
+    // {std,ranges}::search(pred)
+    bm.operator()<std::vector<int>>("std::search(vector<int>, pred) (near matches)", std_search_pred);
+    bm.operator()<std::deque<int>>("std::search(deque<int>, pred) (near matches)", std_search_pred);
+    bm.operator()<std::list<int>>("std::search(list<int>, pred) (near matches)", std_search_pred);
+    bm.operator()<std::vector<int>>("rng::search(vector<int>, pred) (near matches)", ranges_search_pred);
+    bm.operator()<std::deque<int>>("rng::search(deque<int>, pred) (near matches)", ranges_search_pred);
+    bm.operator()<std::list<int>>("rng::search(list<int>, pred) (near matches)", ranges_search_pred);
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp
new file mode 100644
index 0000000000000..d3127e0b1b023
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp
@@ -0,0 +1,138 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <cassert>
+#include <cstddef>
+#include <deque>
+#include <list>
+#include <string>
+#include <vector>
+
+#include <benchmark/benchmark.h>
+#include "../../GenerateInput.h"
+
+int main(int argc, char** argv) {
+  auto std_search_n = [](auto first, auto last, auto n, auto const& value) {
+    return std::search_n(first, last, n, value);
+  };
+  auto std_search_n_pred = [](auto first, auto last, auto n, auto const& value) {
+    return std::search_n(first, last, n, value, [](auto x, auto y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x == y;
+    });
+  };
+  auto ranges_search_n_pred = [](auto first, auto last, auto n, auto const& value) {
+    return std::ranges::search_n(first, last, n, value, [](auto x, auto y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x == y;
+    });
+  };
+
+  // Benchmark {std,ranges}::search_n where the needle is never found
+  {
+    auto bm = []<class Container>(std::string name, auto search_n) {
+      benchmark::RegisterBenchmark(
+          name,
+          [search_n](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            ValueType x            = Generate<ValueType>::random();
+            ValueType y            = random_different_from({x});
+            Container haystack(size, x);
+            std::size_t n = size / 10; // needle size is 10% of the haystack
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(haystack);
+              benchmark::DoNotOptimize(n);
+              benchmark::DoNotOptimize(y);
+              auto result = search_n(haystack.begin(), haystack.end(), n, y);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(1024)
+          ->Arg(8192)
+          ->Arg(1 << 20);
+    };
+    // {std,ranges}::search_n
+    bm.operator()<std::vector<int>>("std::search_n(vector<int>) (no match)", std_search_n);
+    bm.operator()<std::deque<int>>("std::search_n(deque<int>) (no match)", std_search_n);
+    bm.operator()<std::list<int>>("std::search_n(list<int>) (no match)", std_search_n);
+    bm.operator()<std::vector<int>>("rng::search_n(vector<int>) (no match)", std::ranges::search_n);
+    bm.operator()<std::deque<int>>("rng::search_n(deque<int>) (no match)", std::ranges::search_n);
+    bm.operator()<std::list<int>>("rng::search_n(list<int>) (no match)", std::ranges::search_n);
+
+    // {std,ranges}::search_n(pred)
+    bm.operator()<std::vector<int>>("std::search_n(vector<int>, pred) (no match)", std_search_n_pred);
+    bm.operator()<std::deque<int>>("std::search_n(deque<int>, pred) (no match)", std_search_n_pred);
+    bm.operator()<std::list<int>>("std::search_n(list<int>, pred) (no match)", std_search_n_pred);
+    bm.operator()<std::vector<int>>("rng::search_n(vector<int>, pred) (no match)", ranges_search_n_pred);
+    bm.operator()<std::deque<int>>("rng::search_n(deque<int>, pred) (no match)", ranges_search_n_pred);
+    bm.operator()<std::list<int>>("rng::search_n(list<int>, pred) (no match)", ranges_search_n_pred);
+  }
+
+  // Benchmark {std,ranges}::search_n where we intersperse "near matches" inside the haystack
+  {
+    auto bm = []<class Container>(std::string name, auto search_n) {
+      benchmark::RegisterBenchmark(
+          name,
+          [search_n](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            ValueType x            = Generate<ValueType>::random();
+            ValueType y            = random_different_from({x});
+            Container haystack(size, x);
+            std::size_t n = size / 10; // needle size is 10% of the haystack
+            assert(n > 0);
+
+            // intersperse near-matches inside the haystack
+            {
+              auto first = haystack.begin();
+              for (int i = 0; i != 10; ++i) {
+                first = std::fill_n(first, n - 1, y);
+                ++first; // this causes the subsequence not to match because it has length n-1
+              }
+            }
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(haystack);
+              benchmark::DoNotOptimize(n);
+              benchmark::DoNotOptimize(y);
+              auto result = search_n(haystack.begin(), haystack.end(), n, y);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(1024)
+          ->Arg(8192);
+    };
+    // {std,ranges}::search_n
+    bm.operator()<std::vector<int>>("std::search_n(vector<int>) (near matches)", std_search_n);
+    bm.operator()<std::deque<int>>("std::search_n(deque<int>) (near matches)", std_search_n);
+    bm.operator()<std::list<int>>("std::search_n(list<int>) (near matches)", std_search_n);
+    bm.operator()<std::vector<int>>("rng::search_n(vector<int>) (near matches)", std::ranges::search_n);
+    bm.operator()<std::deque<int>>("rng::search_n(deque<int>) (near matches)", std::ranges::search_n);
+    bm.operator()<std::list<int>>("rng::search_n(list<int>) (near matches)", std::ranges::search_n);
+
+    // {std,ranges}::search_n(pred)
+    bm.operator()<std::vector<int>>("std::search_n(vector<int>, pred) (near matches)", std_search_n_pred);
+    bm.operator()<std::deque<int>>("std::search_n(deque<int>, pred) (near matches)", std_search_n_pred);
+    bm.operator()<std::list<int>>("std::search_n(list<int>, pred) (near matches)", std_search_n_pred);
+    bm.operator()<std::vector<int>>("rng::search_n(vector<int>, pred) (near matches)", ranges_search_n_pred);
+    bm.operator()<std::deque<int>>("rng::search_n(deque<int>, pred) (near matches)", ranges_search_n_pred);
+    bm.operator()<std::list<int>>("rng::search_n(list<int>, pred) (near matches)", ranges_search_n_pred);
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/starts_with.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/starts_with.bench.cpp
new file mode 100644
index 0000000000000..cfaa2c9dec0eb
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/starts_with.bench.cpp
@@ -0,0 +1,69 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <list>
+#include <string>
+#include <vector>
+
+#include <benchmark/benchmark.h>
+#include "../../GenerateInput.h"
+
+int main(int argc, char** argv) {
+  auto ranges_starts_with_pred = [](auto first1, auto last1, auto first2, auto last2) {
+    return std::ranges::starts_with(first1, last1, first2, last2, [](auto x, auto y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x == y;
+    });
+  };
+
+  // Benchmark ranges::starts_with where we find the mismatching element at the very end (worst case).
+  {
+    auto bm = []<class Container>(std::string name, auto starts_with) {
+      benchmark::RegisterBenchmark(
+          name,
+          [starts_with](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            ValueType x            = Generate<ValueType>::random();
+            ValueType y            = random_different_from({x});
+            Container c1(size, x);
+            Container c2(size, x);
+            c2.back() = y;
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c1);
+              benchmark::DoNotOptimize(c2);
+              auto result = starts_with(c1.begin(), c1.end(), c2.begin(), c2.end());
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(1024)
+          ->Arg(8192)
+          ->Arg(1 << 20);
+    };
+    bm.operator()<std::vector<int>>("rng::starts_with(vector<int>)", std::ranges::starts_with);
+    bm.operator()<std::deque<int>>("rng::starts_with(deque<int>)", std::ranges::starts_with);
+    bm.operator()<std::list<int>>("rng::starts_with(list<int>)", std::ranges::starts_with);
+
+    bm.operator()<std::vector<int>>("rng::starts_with(vector<int>, pred)", ranges_starts_with_pred);
+    bm.operator()<std::deque<int>>("rng::starts_with(deque<int>, pred)", ranges_starts_with_pred);
+    bm.operator()<std::list<int>>("rng::starts_with(list<int>, pred)", ranges_starts_with_pred);
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/ranges_contains.bench.cpp b/libcxx/test/benchmarks/algorithms/ranges_contains.bench.cpp
deleted file mode 100644
index c9a10202c8cfc..0000000000000
--- a/libcxx/test/benchmarks/algorithms/ranges_contains.bench.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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, c++20
-
-#include <algorithm>
-#include <benchmark/benchmark.h>
-#include <iterator>
-#include <vector>
-
-#include "test_iterators.h"
-
-static void bm_contains_vector_char(benchmark::State& state) {
-  std::vector<char> a(state.range(), 'a');
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(a);
-
-    benchmark::DoNotOptimize(std::ranges::contains(a.begin(), a.end(), 'B'));
-  }
-}
-BENCHMARK(bm_contains_vector_char)->RangeMultiplier(16)->Range(16, 16 << 20);
-
-static void bm_contains_vector_int(benchmark::State& state) {
-  std::vector<int> a(state.range(), 1);
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(a);
-
-    benchmark::DoNotOptimize(std::ranges::contains(a.begin(), a.end(), 2));
-  }
-}
-BENCHMARK(bm_contains_vector_int)->RangeMultiplier(16)->Range(16, 16 << 20);
-
-static void bm_contains_vector_bool(benchmark::State& state) {
-  std::vector<bool> a(state.range(), true);
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(a);
-
-    benchmark::DoNotOptimize(std::ranges::contains(a.begin(), a.end(), false));
-  }
-}
-BENCHMARK(bm_contains_vector_bool)->RangeMultiplier(16)->Range(16, 16 << 20);
-
-BENCHMARK_MAIN();
diff --git a/libcxx/test/benchmarks/algorithms/ranges_ends_with.bench.cpp b/libcxx/test/benchmarks/algorithms/ranges_ends_with.bench.cpp
deleted file mode 100644
index c975d164c16d4..0000000000000
--- a/libcxx/test/benchmarks/algorithms/ranges_ends_with.bench.cpp
+++ /dev/null
@@ -1,109 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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, c++20
-
-#include <algorithm>
-#include <benchmark/benchmark.h>
-#include <iterator>
-
-#include "test_iterators.h"
-#include <vector>
-
-static void bm_ends_with_contiguous_iter(benchmark::State& state) {
-  std::vector<int> a(state.range(), 1);
-  std::vector<int> p(state.range(), 1);
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(a);
-    benchmark::DoNotOptimize(p);
-
-    auto begin1 = contiguous_iterator(a.data());
-    auto end1   = contiguous_iterator(a.data() + a.size());
-    auto begin2 = contiguous_iterator(p.data());
-    auto end2   = contiguous_iterator(p.data() + p.size());
-
-    benchmark::DoNotOptimize(std::ranges::ends_with(begin1, end1, begin2, end2));
-  }
-}
-BENCHMARK(bm_ends_with_contiguous_iter)->RangeMultiplier(16)->Range(16, 16 << 20);
-
-static void bm_ends_with_random_iter(benchmark::State& state) {
-  std::vector<int> a(state.range(), 1);
-  std::vector<int> p(state.range(), 1);
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(a);
-    benchmark::DoNotOptimize(p);
-
-    auto begin1 = random_access_iterator(a.begin());
-    auto end1   = random_access_iterator(a.end());
-    auto begin2 = random_access_iterator(p.begin());
-    auto end2   = random_access_iterator(p.end());
-
-    benchmark::DoNotOptimize(std::ranges::ends_with(begin1, end1, begin2, end2));
-  }
-}
-BENCHMARK(bm_ends_with_random_iter)->RangeMultiplier(16)->Range(16, 16 << 20);
-
-static void bm_ends_with_bidirectional_iter(benchmark::State& state) {
-  std::vector<int> a(state.range(), 1);
-  std::vector<int> p(state.range(), 1);
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(a);
-    benchmark::DoNotOptimize(p);
-
-    auto begin1 = bidirectional_iterator(a.begin());
-    auto end1   = bidirectional_iterator(a.end());
-    auto begin2 = bidirectional_iterator(p.begin());
-    auto end2   = bidirectional_iterator(p.end());
-
-    benchmark::DoNotOptimize(std::ranges::ends_with(begin1, end1, begin2, end2));
-  }
-}
-BENCHMARK(bm_ends_with_bidirectional_iter)->RangeMultiplier(16)->Range(16, 16 << 20);
-
-static void bm_ends_with_forward_iter(benchmark::State& state) {
-  std::vector<int> a(state.range(), 1);
-  std::vector<int> p(state.range(), 1);
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(a);
-    benchmark::DoNotOptimize(p);
-
-    auto begin1 = forward_iterator(a.begin());
-    auto end1   = forward_iterator(a.end());
-    auto begin2 = forward_iterator(p.begin());
-    auto end2   = forward_iterator(p.end());
-
-    benchmark::DoNotOptimize(std::ranges::ends_with(begin1, end1, begin2, end2));
-  }
-}
-BENCHMARK(bm_ends_with_forward_iter)->RangeMultiplier(16)->Range(16, 16 << 20);
-
-static void bm_ends_with_forward_iter_with_size_optimization(benchmark::State& state) {
-  std::vector<int> a(state.range(), 1);
-  std::vector<int> p(state.range(), 1);
-  p.push_back(2);
-
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(a);
-    benchmark::DoNotOptimize(p);
-
-    auto begin1 = forward_iterator(a.begin());
-    auto end1   = forward_iterator(a.end());
-    auto begin2 = forward_iterator(p.begin());
-    auto end2   = forward_iterator(p.end());
-
-    benchmark::DoNotOptimize(std::ranges::ends_with(begin1, end1, begin2, end2));
-  }
-}
-BENCHMARK(bm_ends_with_forward_iter_with_size_optimization)->RangeMultiplier(16)->Range(16, 16 << 20);
-
-BENCHMARK_MAIN();

>From 63f0c77528dfa421faefd6ba3b11a4585fa3a8a8 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 21 Feb 2025 12:38:46 -0500
Subject: [PATCH 02/20] Fix modules issues

---
 libcxx/include/module.modulemap               | 20 +++++++++++++++----
 .../algorithms/nonmodifying/fold.bench.cpp    |  1 +
 .../nonmodifying/is_permutation.bench.cpp     |  1 +
 3 files changed, 18 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 406b26f86a4c5..ca3f5575d476f 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -543,11 +543,17 @@ module std [system] {
     }
     module ranges_fill_n                          { header "__algorithm/ranges_fill_n.h" }
     module ranges_fill                            { header "__algorithm/ranges_fill.h" }
-    module ranges_find_end                        { header "__algorithm/ranges_find_end.h" }
+    module ranges_find_end {
+      header "__algorithm/ranges_find_end.h"
+      export std.ranges.subrange // return type
+    }
     module ranges_find_first_of                   { header "__algorithm/ranges_find_first_of.h" }
     module ranges_find_if_not                     { header "__algorithm/ranges_find_if_not.h" }
     module ranges_find_if                         { header "__algorithm/ranges_find_if.h" }
-    module ranges_find_last                       { header "__algorithm/ranges_find_last.h" }
+    module ranges_find_last {
+      header "__algorithm/ranges_find_last.h"
+      export std.ranges.subrange // return type
+    }
     module ranges_find                            { header "__algorithm/ranges_find.h" }
     module ranges_fold                            { header "__algorithm/ranges_fold.h" }
     module ranges_for_each_n {
@@ -737,8 +743,14 @@ module std [system] {
     }
     module ranges_rotate                          { header "__algorithm/ranges_rotate.h" }
     module ranges_sample                          { header "__algorithm/ranges_sample.h" }
-    module ranges_search_n                        { header "__algorithm/ranges_search_n.h" }
-    module ranges_search                          { header "__algorithm/ranges_search.h" }
+    module ranges_search_n {
+      header "__algorithm/ranges_search_n.h"
+      export std.ranges.subrange // return type
+    }
+    module ranges_search {
+      header "__algorithm/ranges_search.h"
+      export std.ranges.subrange // return type
+    }
     module ranges_set_difference {
       header "__algorithm/ranges_set_difference.h"
       export std.functional.ranges_operations
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp
index d9b6dbedf3186..c000d01acd540 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp
@@ -12,6 +12,7 @@
 #include <cassert>
 #include <cstddef>
 #include <deque>
+#include <iterator>
 #include <limits>
 #include <list>
 #include <string>
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/is_permutation.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/is_permutation.bench.cpp
index 41d18ec3d9c1a..d79e666836b8b 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/is_permutation.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/is_permutation.bench.cpp
@@ -11,6 +11,7 @@
 #include <algorithm>
 #include <cstddef>
 #include <deque>
+#include <iterator>
 #include <list>
 #include <string>
 #include <vector>

>From 899c1179ff5f2548cebb56626f35a754689e8d98 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 21 Feb 2025 21:57:20 -0500
Subject: [PATCH 03/20] Fix -Wsign-compare

---
 libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp
index c000d01acd540..4e8fde69dc0cb 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp
@@ -31,7 +31,7 @@ int main(int argc, char** argv) {
             std::size_t const size = st.range(0);
             using ValueType        = typename Container::value_type;
             ValueType const limit  = 1000; // ensure we never overflow in the addition
-            assert(size <= std::numeric_limits<ValueType>::max());
+            assert(static_cast<ValueType>(size) <= std::numeric_limits<ValueType>::max());
             assert(std::numeric_limits<ValueType>::max() > static_cast<ValueType>(size) * limit);
             assert(std::numeric_limits<ValueType>::min() < static_cast<ValueType>(size) * limit * -1);
 

>From c5645d028c9527c53f77180f826b617e36facf26 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 25 Feb 2025 12:39:17 -0500
Subject: [PATCH 04/20] Add TODO

---
 libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp
index 4e8fde69dc0cb..84a03582d4cbf 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp
@@ -64,7 +64,7 @@ int main(int argc, char** argv) {
     bm.operator()<std::deque<int>>("rng::fold_left(deque<int>)", std::ranges::fold_left);
     bm.operator()<std::list<int>>("rng::fold_left(list<int>)", std::ranges::fold_left);
 
-    // fold_right not implemented yet
+    // TODO: fold_right not implemented yet
     // bm.operator()<std::vector<int>>("rng::fold_right(vector<int>)", std::ranges::fold_right);
     // bm.operator()<std::deque<int>>("rng::fold_right(deque<int>)", std::ranges::fold_right);
     // bm.operator()<std::list<int>>("rng::fold_right(list<int>)", std::ranges::fold_right);

>From ed226e2ed013930b98eb8be3ea6a3310bebe94a2 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 25 Feb 2025 12:42:41 -0500
Subject: [PATCH 05/20] Use ValueType instead of int

---
 .../nonmodifying/contains.bench.cpp           | 19 ++++++++++++++-----
 .../nonmodifying/contains_subrange.bench.cpp  | 15 +++++++++++----
 2 files changed, 25 insertions(+), 9 deletions(-)

diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp
index c0fbae11a218a..a6301d5598e6c 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp
@@ -16,6 +16,7 @@
 #include <vector>
 
 #include <benchmark/benchmark.h>
+#include "../../GenerateInput.h"
 
 int main(int argc, char** argv) {
   // Benchmark ranges::contains where we bail out early (after visiting 25% of the elements).
@@ -25,14 +26,18 @@ int main(int argc, char** argv) {
           name,
           [](auto& st) {
             std::size_t const size = st.range(0);
-            Container c(size, 1);
-            *std::next(c.begin(), size / 4) = 42; // bail out after checking 25% of values
+            using ValueType        = typename Container::value_type;
+            ValueType x            = Generate<ValueType>::random();
+            ValueType y            = random_different_from({x});
+            Container c(size, x);
+            *std::next(c.begin(), size / 4) = y; // bail out after checking 25% of values
             auto first                      = c.begin();
             auto last                       = c.end();
 
             for (auto _ : st) {
               benchmark::DoNotOptimize(c);
-              auto result = std::ranges::contains(first, last, 42);
+              benchmark::DoNotOptimize(y);
+              auto result = std::ranges::contains(first, last, y);
               benchmark::DoNotOptimize(result);
             }
           })
@@ -53,13 +58,17 @@ int main(int argc, char** argv) {
           name,
           [](auto& st) {
             std::size_t const size = st.range(0);
-            Container c(size, 1);
+            using ValueType        = typename Container::value_type;
+            ValueType x            = Generate<ValueType>::random();
+            ValueType y            = random_different_from({x});
+            Container c(size, x);
             auto first = c.begin();
             auto last  = c.end();
 
             for (auto _ : st) {
               benchmark::DoNotOptimize(c);
-              auto result = std::ranges::contains(first, last, 42);
+              benchmark::DoNotOptimize(y);
+              auto result = std::ranges::contains(first, last, y);
               benchmark::DoNotOptimize(result);
             }
           })
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp
index 6b585acc7c752..456297afe93ba 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp
@@ -16,6 +16,7 @@
 #include <vector>
 
 #include <benchmark/benchmark.h>
+#include "../../GenerateInput.h"
 
 int main(int argc, char** argv) {
   // Benchmark ranges::contains_subrange where we find our target starting at 25% of the elements
@@ -25,8 +26,11 @@ int main(int argc, char** argv) {
           name,
           [](auto& st) {
             std::size_t const size = st.range(0);
-            Container c(size, 1);
-            Container subrange(size / 10, 42); // subrange of length 10% of the full range
+            using ValueType        = typename Container::value_type;
+            ValueType x            = Generate<ValueType>::random();
+            ValueType y            = random_different_from({x});
+            Container c(size, x);
+            Container subrange(size / 10, y); // subrange of length 10% of the full range
 
             // At 25% of the range, put the subrange we're going to find
             std::ranges::copy(subrange, std::next(c.begin(), c.size() / 4));
@@ -55,8 +59,11 @@ int main(int argc, char** argv) {
           name,
           [](auto& st) {
             std::size_t const size = st.range(0);
-            Container c(size, 1);
-            Container subrange(size / 10, 42); // subrange of length 10% of the full range, but we'll never find it
+            using ValueType        = typename Container::value_type;
+            ValueType x            = Generate<ValueType>::random();
+            ValueType y            = random_different_from({x});
+            Container c(size, x);
+            Container subrange(size / 10, y); // subrange of length 10% of the full range, but we'll never find it
 
             for (auto _ : st) {
               benchmark::DoNotOptimize(c);

>From dd8ddd555e2fd81674adc712414299410aef58ae Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 25 Feb 2025 12:49:40 -0500
Subject: [PATCH 06/20] Add missing predicate overloads

---
 .../nonmodifying/find_end.bench.cpp           | 32 ++++++++++++++
 .../nonmodifying/find_first_of.bench.cpp      | 43 +++++++++++++++++++
 2 files changed, 75 insertions(+)

diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp
index 70576acd79792..419e2dd7406cb 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp
@@ -22,6 +22,20 @@ int main(int argc, char** argv) {
   auto std_find_end = [](auto first1, auto last1, auto first2, auto last2) {
     return std::find_end(first1, last1, first2, last2);
   };
+  auto std_find_end_pred = [](auto first1, auto last1, auto first2, auto last2) {
+    return std::find_end(first1, last1, first2, last2, [](auto x, auto y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x == y;
+    });
+  };
+  auto ranges_find_end_pred = [](auto first1, auto last1, auto first2, auto last2) {
+    return std::ranges::find_end(first1, last1, first2, last2, [](auto x, auto y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x == y;
+    });
+  };
 
   // Benchmark {std,ranges}::find_end where the subsequence is found
   // 25% into the sequence
@@ -51,12 +65,21 @@ int main(int argc, char** argv) {
           ->Arg(8192)
           ->Arg(1 << 20);
     };
+    // {std,ranges}::find_end(it1, it1, it2, it2)
     bm.operator()<std::vector<int>>("std::find_end(vector<int>) (bail 25%)", std_find_end);
     bm.operator()<std::deque<int>>("std::find_end(deque<int>) (bail 25%)", std_find_end);
     bm.operator()<std::list<int>>("std::find_end(list<int>) (bail 25%)", std_find_end);
     bm.operator()<std::vector<int>>("rng::find_end(vector<int>) (bail 25%)", std::ranges::find_end);
     bm.operator()<std::deque<int>>("rng::find_end(deque<int>) (bail 25%)", std::ranges::find_end);
     bm.operator()<std::list<int>>("rng::find_end(list<int>) (bail 25%)", std::ranges::find_end);
+
+    // {std,ranges}::find_end(it1, it1, it2, it2, pred)
+    bm.operator()<std::vector<int>>("std::find_end(vector<int>, pred) (bail 25%)", std_find_end_pred);
+    bm.operator()<std::deque<int>>("std::find_end(deque<int>, pred) (bail 25%)", std_find_end_pred);
+    bm.operator()<std::list<int>>("std::find_end(list<int>, pred) (bail 25%)", std_find_end_pred);
+    bm.operator()<std::vector<int>>("rng::find_end(vector<int>, pred) (bail 25%)", ranges_find_end_pred);
+    bm.operator()<std::deque<int>>("rng::find_end(deque<int>, pred) (bail 25%)", ranges_find_end_pred);
+    bm.operator()<std::list<int>>("rng::find_end(list<int>, pred) (bail 25%)", ranges_find_end_pred);
   }
 
   // Benchmark {std,ranges}::find_end where the subsequence is found
@@ -87,12 +110,21 @@ int main(int argc, char** argv) {
           ->Arg(8192)
           ->Arg(1 << 20);
     };
+    // {std,ranges}::find_end(it1, it1, it2, it2)
     bm.operator()<std::vector<int>>("std::find_end(vector<int>) (bail 90%)", std_find_end);
     bm.operator()<std::deque<int>>("std::find_end(deque<int>) (bail 90%)", std_find_end);
     bm.operator()<std::list<int>>("std::find_end(list<int>) (bail 90%)", std_find_end);
     bm.operator()<std::vector<int>>("rng::find_end(vector<int>) (bail 90%)", std::ranges::find_end);
     bm.operator()<std::deque<int>>("rng::find_end(deque<int>) (bail 90%)", std::ranges::find_end);
     bm.operator()<std::list<int>>("rng::find_end(list<int>) (bail 90%)", std::ranges::find_end);
+
+    // {std,ranges}::find_end(it1, it1, it2, it2, pred)
+    bm.operator()<std::vector<int>>("std::find_end(vector<int>, pred) (bail 90%)", std_find_end_pred);
+    bm.operator()<std::deque<int>>("std::find_end(deque<int>, pred) (bail 90%)", std_find_end_pred);
+    bm.operator()<std::list<int>>("std::find_end(list<int>, pred) (bail 90%)", std_find_end_pred);
+    bm.operator()<std::vector<int>>("rng::find_end(vector<int>, pred) (bail 90%)", ranges_find_end_pred);
+    bm.operator()<std::deque<int>>("rng::find_end(deque<int>, pred) (bail 90%)", ranges_find_end_pred);
+    bm.operator()<std::list<int>>("rng::find_end(list<int>, pred) (bail 90%)", ranges_find_end_pred);
   }
 
   benchmark::Initialize(&argc, argv);
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp
index cf83c2761232c..80fe862672299 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp
@@ -23,6 +23,20 @@ int main(int argc, char** argv) {
   auto std_find_first_of = [](auto first1, auto last1, auto first2, auto last2) {
     return std::find_first_of(first1, last1, first2, last2);
   };
+  auto std_find_first_of_pred = [](auto first1, auto last1, auto first2, auto last2) {
+    return std::find_first_of(first1, last1, first2, last2, [](auto x, auto y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x == y;
+    });
+  };
+  auto ranges_find_first_of_pred = [](auto first1, auto last1, auto first2, auto last2) {
+    return std::ranges::find_first_of(first1, last1, first2, last2, [](auto x, auto y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x == y;
+    });
+  };
 
   // Benchmark {std,ranges}::find_first_of where we have a hit at 25% of the haystack
   // and at the end of the needle. This measures how quickly we're able to search inside
@@ -55,6 +69,7 @@ int main(int argc, char** argv) {
           ->Arg(1024)
           ->Arg(8192);
     };
+    // {std,ranges}::find_first_of(it1, it1, it2, it2)
     bm.operator()<std::vector<int>>("std::find_first_of(vector<int>) (25% haystack, late needle)", std_find_first_of);
     bm.operator()<std::deque<int>>("std::find_first_of(deque<int>) (25% haystack, late needle)", std_find_first_of);
     bm.operator()<std::list<int>>("std::find_first_of(list<int>) (25% haystack, late needle)", std_find_first_of);
@@ -64,6 +79,19 @@ int main(int argc, char** argv) {
         "rng::find_first_of(deque<int>) (25% haystack, late needle)", std::ranges::find_first_of);
     bm.operator()<std::list<int>>(
         "rng::find_first_of(list<int>) (25% haystack, late needle)", std::ranges::find_first_of);
+
+    // {std,ranges}::find_first_of(it1, it1, it2, it2, pred)
+    bm.operator()<std::vector<int>>(
+        "std::find_first_of(vector<int>, pred) (25% haystack, late needle)", std_find_first_of);
+    bm.operator()<std::deque<int>>(
+        "std::find_first_of(deque<int>, pred) (25% haystack, late needle)", std_find_first_of);
+    bm.operator()<std::list<int>>("std::find_first_of(list<int>, pred) (25% haystack, late needle)", std_find_first_of);
+    bm.operator()<std::vector<int>>(
+        "rng::find_first_of(vector<int>, pred) (25% haystack, late needle)", std::ranges::find_first_of);
+    bm.operator()<std::deque<int>>(
+        "rng::find_first_of(deque<int>, pred) (25% haystack, late needle)", std::ranges::find_first_of);
+    bm.operator()<std::list<int>>(
+        "rng::find_first_of(list<int>, pred) (25% haystack, late needle)", std::ranges::find_first_of);
   }
 
   // Benchmark {std,ranges}::find_first_of where we have a hit at 90% of the haystack
@@ -97,6 +125,7 @@ int main(int argc, char** argv) {
           ->Arg(1024)
           ->Arg(8192);
     };
+    // {std,ranges}::find_first_of(it1, it1, it2, it2)
     bm.operator()<std::vector<int>>("std::find_first_of(vector<int>) (90% haystack, early needle)", std_find_first_of);
     bm.operator()<std::deque<int>>("std::find_first_of(deque<int>) (90% haystack, early needle)", std_find_first_of);
     bm.operator()<std::list<int>>("std::find_first_of(list<int>) (90% haystack, early needle)", std_find_first_of);
@@ -106,6 +135,20 @@ int main(int argc, char** argv) {
         "rng::find_first_of(deque<int>) (90% haystack, early needle)", std::ranges::find_first_of);
     bm.operator()<std::list<int>>(
         "rng::find_first_of(list<int>) (90% haystack, early needle)", std::ranges::find_first_of);
+
+    // {std,ranges}::find_first_of(it1, it1, it2, it2, pred)
+    bm.operator()<std::vector<int>>(
+        "std::find_first_of(vector<int>, pred) (90% haystack, early needle)", std_find_first_of_pred);
+    bm.operator()<std::deque<int>>(
+        "std::find_first_of(deque<int>, pred) (90% haystack, early needle)", std_find_first_of_pred);
+    bm.operator()<std::list<int>>(
+        "std::find_first_of(list<int>, pred) (90% haystack, early needle)", std_find_first_of_pred);
+    bm.operator()<std::vector<int>>(
+        "rng::find_first_of(vector<int>, pred) (90% haystack, early needle)", ranges_find_first_of_pred);
+    bm.operator()<std::deque<int>>(
+        "rng::find_first_of(deque<int>, pred) (90% haystack, early needle)", ranges_find_first_of_pred);
+    bm.operator()<std::list<int>>(
+        "rng::find_first_of(list<int>, pred) (90% haystack, early needle)", ranges_find_first_of_pred);
   }
 
   benchmark::Initialize(&argc, argv);

>From 84f9db205e175fa44de142bc84a9dedb7e0bfd50 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 25 Feb 2025 12:54:03 -0500
Subject: [PATCH 07/20] Take by value in predicates

---
 .../algorithms/nonmodifying/is_permutation.bench.cpp        | 6 +++---
 .../benchmarks/algorithms/nonmodifying/mismatch.bench.cpp   | 6 +++---
 .../benchmarks/algorithms/nonmodifying/search.bench.cpp     | 4 ++--
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/is_permutation.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/is_permutation.bench.cpp
index d79e666836b8b..3e4d21945bf06 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/is_permutation.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/is_permutation.bench.cpp
@@ -27,21 +27,21 @@ int main(int argc, char** argv) {
     return std::is_permutation(first1, last1, first2, last2);
   };
   auto std_is_permutation_3leg_pred = [](auto first1, auto last1, auto first2, auto) {
-    return std::is_permutation(first1, last1, first2, [](auto& x, auto& y) {
+    return std::is_permutation(first1, last1, first2, [](auto x, auto y) {
       benchmark::DoNotOptimize(x);
       benchmark::DoNotOptimize(y);
       return x == y;
     });
   };
   auto std_is_permutation_4leg_pred = [](auto first1, auto last1, auto first2, auto last2) {
-    return std::is_permutation(first1, last1, first2, last2, [](auto& x, auto& y) {
+    return std::is_permutation(first1, last1, first2, last2, [](auto x, auto y) {
       benchmark::DoNotOptimize(x);
       benchmark::DoNotOptimize(y);
       return x == y;
     });
   };
   auto ranges_is_permutation_4leg_pred = [](auto first1, auto last1, auto first2, auto last2) {
-    return std::ranges::is_permutation(first1, last1, first2, last2, [](auto& x, auto& y) {
+    return std::ranges::is_permutation(first1, last1, first2, last2, [](auto x, auto y) {
       benchmark::DoNotOptimize(x);
       benchmark::DoNotOptimize(y);
       return x == y;
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/mismatch.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/mismatch.bench.cpp
index 630701a0440c8..af797a7aa7e9f 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/mismatch.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/mismatch.bench.cpp
@@ -26,21 +26,21 @@ int main(int argc, char** argv) {
     return std::mismatch(first1, last1, first2, last2);
   };
   auto std_mismatch_3leg_pred = [](auto first1, auto last1, auto first2, auto) {
-    return std::mismatch(first1, last1, first2, [](auto& x, auto& y) {
+    return std::mismatch(first1, last1, first2, [](auto x, auto y) {
       benchmark::DoNotOptimize(x);
       benchmark::DoNotOptimize(y);
       return x == y;
     });
   };
   auto std_mismatch_4leg_pred = [](auto first1, auto last1, auto first2, auto last2) {
-    return std::mismatch(first1, last1, first2, last2, [](auto& x, auto& y) {
+    return std::mismatch(first1, last1, first2, last2, [](auto x, auto y) {
       benchmark::DoNotOptimize(x);
       benchmark::DoNotOptimize(y);
       return x == y;
     });
   };
   auto ranges_mismatch_4leg_pred = [](auto first1, auto last1, auto first2, auto last2) {
-    return std::ranges::mismatch(first1, last1, first2, last2, [](auto& x, auto& y) {
+    return std::ranges::mismatch(first1, last1, first2, last2, [](auto x, auto y) {
       benchmark::DoNotOptimize(x);
       benchmark::DoNotOptimize(y);
       return x == y;
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp
index 6c6a26509cc62..776be2c96ac4d 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp
@@ -24,14 +24,14 @@ int main(int argc, char** argv) {
     return std::search(first1, last1, first2, last2);
   };
   auto std_search_pred = [](auto first1, auto last1, auto first2, auto last2) {
-    return std::search(first1, last1, first2, last2, [](auto& x, auto& y) {
+    return std::search(first1, last1, first2, last2, [](auto x, auto y) {
       benchmark::DoNotOptimize(x);
       benchmark::DoNotOptimize(y);
       return x == y;
     });
   };
   auto ranges_search_pred = [](auto first1, auto last1, auto first2, auto last2) {
-    return std::ranges::search(first1, last1, first2, last2, [](auto& x, auto& y) {
+    return std::ranges::search(first1, last1, first2, last2, [](auto x, auto y) {
       benchmark::DoNotOptimize(x);
       benchmark::DoNotOptimize(y);
       return x == y;

>From 908850765f2294dd4d08d207639cceae4703485f Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 25 Feb 2025 13:00:23 -0500
Subject: [PATCH 08/20] Run benchmarks on non-powers-of-two

---
 .../benchmarks/algorithms/nonmodifying/adjacent_find.bench.cpp  | 1 +
 .../algorithms/nonmodifying/any_all_none_of.bench.cpp           | 2 ++
 .../test/benchmarks/algorithms/nonmodifying/contains.bench.cpp  | 2 ++
 .../algorithms/nonmodifying/contains_subrange.bench.cpp         | 2 ++
 libcxx/test/benchmarks/algorithms/nonmodifying/count.bench.cpp  | 2 ++
 .../test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp | 2 ++
 libcxx/test/benchmarks/algorithms/nonmodifying/equal.bench.cpp  | 1 +
 libcxx/test/benchmarks/algorithms/nonmodifying/find.bench.cpp   | 2 ++
 .../test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp  | 2 ++
 .../benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp  | 2 ++
 .../test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp | 2 ++
 libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp   | 1 +
 .../test/benchmarks/algorithms/nonmodifying/for_each.bench.cpp  | 1 +
 .../test/benchmarks/algorithms/nonmodifying/mismatch.bench.cpp  | 1 +
 libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp | 2 ++
 .../test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp  | 2 ++
 .../benchmarks/algorithms/nonmodifying/starts_with.bench.cpp    | 1 +
 17 files changed, 28 insertions(+)

diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/adjacent_find.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/adjacent_find.bench.cpp
index 1f6e20dfa4527..2bfdffdabdf02 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/adjacent_find.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/adjacent_find.bench.cpp
@@ -58,6 +58,7 @@ int main(int argc, char** argv) {
             }
           })
           ->Arg(8)
+          ->Arg(50) // non power-of-two
           ->Arg(1024)
           ->Arg(8192)
           ->Arg(1 << 20);
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/any_all_none_of.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/any_all_none_of.bench.cpp
index 363ed96f6d201..66eba4e50f83d 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/any_all_none_of.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/any_all_none_of.bench.cpp
@@ -64,6 +64,7 @@ int main(int argc, char** argv) {
           })
           ->Arg(8)
           ->Arg(32)
+          ->Arg(50) // non power-of-two
           ->Arg(8192)
           ->Arg(32768);
     };
@@ -116,6 +117,7 @@ int main(int argc, char** argv) {
           })
           ->Arg(8)
           ->Arg(32)
+          ->Arg(50) // non power-of-two
           ->Arg(8192)
           ->Arg(32768);
     };
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp
index a6301d5598e6c..50d8c5fd331cd 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp
@@ -43,6 +43,7 @@ int main(int argc, char** argv) {
           })
           ->Arg(8)
           ->Arg(32)
+          ->Arg(50) // non power-of-two
           ->Arg(8192)
           ->Arg(1 << 20);
     };
@@ -74,6 +75,7 @@ int main(int argc, char** argv) {
           })
           ->Arg(8)
           ->Arg(32)
+          ->Arg(50) // non power-of-two
           ->Arg(8192)
           ->Arg(1 << 20);
     };
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp
index 456297afe93ba..96676bd2921f5 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp
@@ -44,6 +44,7 @@ int main(int argc, char** argv) {
           })
           ->Arg(16)
           ->Arg(32)
+          ->Arg(50) // non power-of-two
           ->Arg(8192)
           ->Arg(1 << 20);
     };
@@ -74,6 +75,7 @@ int main(int argc, char** argv) {
           })
           ->Arg(16)
           ->Arg(32)
+          ->Arg(50) // non power-of-two
           ->Arg(8192)
           ->Arg(1 << 20);
     };
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/count.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/count.bench.cpp
index 13d8f9f2690e9..a4edd4402ce74 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/count.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/count.bench.cpp
@@ -104,6 +104,7 @@ int main(int argc, char** argv) {
             }
           })
           ->Arg(8)
+          ->Arg(50) // non power-of-two
           ->Arg(1024)
           ->Arg(8192)
           ->Arg(1 << 20);
@@ -141,6 +142,7 @@ int main(int argc, char** argv) {
               benchmark::DoNotOptimize(result);
             }
           })
+          ->Arg(1000) // non power-of-two
           ->Arg(1024)
           ->Arg(8192)
           ->Arg(1 << 20);
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp
index 08ff693916f67..cf4e6b5781ec5 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp
@@ -49,6 +49,7 @@ int main(int argc, char** argv) {
             }
           })
           ->Arg(8)
+          ->Arg(50) // non power-of-two
           ->Arg(1024)
           ->Arg(8192)
           ->Arg(1 << 20);
@@ -84,6 +85,7 @@ int main(int argc, char** argv) {
             }
           })
           ->Arg(8)
+          ->Arg(50) // non power-of-two
           ->Arg(1024)
           ->Arg(8192)
           ->Arg(1 << 20);
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/equal.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/equal.bench.cpp
index ad73ba3610abf..9feef4e1ac22b 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/equal.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/equal.bench.cpp
@@ -67,6 +67,7 @@ int main(int argc, char** argv) {
             }
           })
           ->Arg(8)
+          ->Arg(50) // non power-of-two
           ->Arg(1024)
           ->Arg(8192)
           ->Arg(1 << 20);
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find.bench.cpp
index 3be7df0ac1131..b2ead1cc75585 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/find.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find.bench.cpp
@@ -133,6 +133,7 @@ int main(int argc, char** argv) {
             }
           })
           ->Arg(8)
+          ->Arg(50) // non power-of-two
           ->Arg(1024)
           ->Arg(8192)
           ->Arg(1 << 15);
@@ -158,6 +159,7 @@ int main(int argc, char** argv) {
             }
           })
           ->Arg(8)
+          ->Arg(50) // non power-of-two
           ->Arg(1024)
           ->Arg(8192)
           ->Arg(1 << 20);
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp
index 419e2dd7406cb..19f6ed2d55042 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp
@@ -61,6 +61,7 @@ int main(int argc, char** argv) {
               benchmark::DoNotOptimize(result);
             }
           })
+          ->Arg(1000) // non power-of-two
           ->Arg(1024)
           ->Arg(8192)
           ->Arg(1 << 20);
@@ -106,6 +107,7 @@ int main(int argc, char** argv) {
               benchmark::DoNotOptimize(result);
             }
           })
+          ->Arg(1000) // non power-of-two
           ->Arg(1024)
           ->Arg(8192)
           ->Arg(1 << 20);
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp
index 80fe862672299..6525f8c384f76 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp
@@ -66,6 +66,7 @@ int main(int argc, char** argv) {
             }
           })
           ->Arg(32)
+          ->Arg(50) // non power-of-two
           ->Arg(1024)
           ->Arg(8192);
     };
@@ -122,6 +123,7 @@ int main(int argc, char** argv) {
             }
           })
           ->Arg(32)
+          ->Arg(50) // non power-of-two
           ->Arg(1024)
           ->Arg(8192);
     };
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp
index 536d7d4f0350c..cac999b080f7c 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp
@@ -56,6 +56,7 @@ int main(int argc, char** argv) {
             }
           })
           ->Arg(8)
+          ->Arg(50) // non power-of-two
           ->Arg(1024)
           ->Arg(8192)
           ->Arg(1 << 20);
@@ -104,6 +105,7 @@ int main(int argc, char** argv) {
             }
           })
           ->Arg(8)
+          ->Arg(50) // non power-of-two
           ->Arg(1024)
           ->Arg(8192)
           ->Arg(1 << 20);
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp
index 84a03582d4cbf..6711cec760e2c 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp
@@ -57,6 +57,7 @@ int main(int argc, char** argv) {
           })
           ->Arg(8)
           ->Arg(32)
+          ->Arg(50) // non power-of-two
           ->Arg(8192)
           ->Arg(1 << 20);
     };
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/for_each.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/for_each.bench.cpp
index 693403bc8f671..5387c37d228c5 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/for_each.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/for_each.bench.cpp
@@ -39,6 +39,7 @@ int main(int argc, char** argv) {
           })
           ->Arg(8)
           ->Arg(32)
+          ->Arg(50) // non power-of-two
           ->Arg(8192)
           ->Arg(1 << 20);
     };
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/mismatch.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/mismatch.bench.cpp
index af797a7aa7e9f..5c5961adcd05d 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/mismatch.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/mismatch.bench.cpp
@@ -72,6 +72,7 @@ int main(int argc, char** argv) {
             }
           })
           ->Arg(8)
+          ->Arg(1000) // non power-of-two
           ->Arg(1024)
           ->Arg(8192)
           ->Arg(1 << 20);
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp
index 776be2c96ac4d..9696c77aab649 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp
@@ -58,6 +58,7 @@ int main(int argc, char** argv) {
               benchmark::DoNotOptimize(result);
             }
           })
+          ->Arg(1000) // non power-of-two
           ->Arg(1024)
           ->Arg(8192)
           ->Arg(1 << 20);
@@ -110,6 +111,7 @@ int main(int argc, char** argv) {
               benchmark::DoNotOptimize(result);
             }
           })
+          ->Arg(1000) // non power-of-two
           ->Arg(1024)
           ->Arg(8192);
     };
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp
index d3127e0b1b023..a58386435fe85 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp
@@ -59,6 +59,7 @@ int main(int argc, char** argv) {
               benchmark::DoNotOptimize(result);
             }
           })
+          ->Arg(1000) // non power-of-two
           ->Arg(1024)
           ->Arg(8192)
           ->Arg(1 << 20);
@@ -111,6 +112,7 @@ int main(int argc, char** argv) {
               benchmark::DoNotOptimize(result);
             }
           })
+          ->Arg(1000) // non power-of-two
           ->Arg(1024)
           ->Arg(8192);
     };
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/starts_with.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/starts_with.bench.cpp
index cfaa2c9dec0eb..e141e0c48810c 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/starts_with.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/starts_with.bench.cpp
@@ -49,6 +49,7 @@ int main(int argc, char** argv) {
             }
           })
           ->Arg(8)
+          ->Arg(1000) // non power-of-two
           ->Arg(1024)
           ->Arg(8192)
           ->Arg(1 << 20);

>From a67d700fc509bbdaa8524fb9cb667b7a047733d6 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 25 Feb 2025 13:03:05 -0500
Subject: [PATCH 09/20] Remove unnecessary benchmark for std::count

---
 .../algorithms/nonmodifying/count.bench.cpp   | 47 -------------------
 1 file changed, 47 deletions(-)

diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/count.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/count.bench.cpp
index a4edd4402ce74..c90b23eda8672 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/count.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/count.bench.cpp
@@ -80,53 +80,6 @@ int main(int argc, char** argv) {
     bm.operator()<std::list<int>>("rng::count_if(list<int>) (every other)", ranges_count_if);
   }
 
-  // Benchmark {std,ranges}::{count,count_if} on a sequence where only a few elements are counted.
-  // In theory, we could blaze through contiguous sequences where there are no elements to count.
-  {
-    auto bm = []<class Container>(std::string name, auto count) {
-      benchmark::RegisterBenchmark(
-          name,
-          [count](auto& st) {
-            std::size_t const size = st.range(0);
-            using ValueType        = typename Container::value_type;
-            ValueType x            = Generate<ValueType>::random();
-            ValueType y            = random_different_from({x});
-            Container c;
-            for (std::size_t i = 0; i != size; ++i) {
-              c.push_back(i % (size / 5) == 0 ? x : y); // intersperse 5 elements to count at regular intervals
-            }
-
-            for ([[maybe_unused]] auto _ : st) {
-              benchmark::DoNotOptimize(c);
-              benchmark::DoNotOptimize(x);
-              auto result = count(c.begin(), c.end(), x);
-              benchmark::DoNotOptimize(result);
-            }
-          })
-          ->Arg(8)
-          ->Arg(50) // non power-of-two
-          ->Arg(1024)
-          ->Arg(8192)
-          ->Arg(1 << 20);
-    };
-
-    // count
-    bm.operator()<std::vector<int>>("std::count(vector<int>) (sparse)", std_count);
-    bm.operator()<std::deque<int>>("std::count(deque<int>) (sparse)", std_count);
-    bm.operator()<std::list<int>>("std::count(list<int>) (sparse)", std_count);
-    bm.operator()<std::vector<int>>("rng::count(vector<int>) (sparse)", ranges_count);
-    bm.operator()<std::deque<int>>("rng::count(deque<int>) (sparse)", ranges_count);
-    bm.operator()<std::list<int>>("rng::count(list<int>) (sparse)", ranges_count);
-
-    // count_if
-    bm.operator()<std::vector<int>>("std::count_if(vector<int>) (sparse)", std_count_if);
-    bm.operator()<std::deque<int>>("std::count_if(deque<int>) (sparse)", std_count_if);
-    bm.operator()<std::list<int>>("std::count_if(list<int>) (sparse)", std_count_if);
-    bm.operator()<std::vector<int>>("rng::count_if(vector<int>) (sparse)", ranges_count_if);
-    bm.operator()<std::deque<int>>("rng::count_if(deque<int>) (sparse)", ranges_count_if);
-    bm.operator()<std::list<int>>("rng::count_if(list<int>) (sparse)", ranges_count_if);
-  }
-
   // Benchmark {std,ranges}::count(vector<bool>)
   {
     auto bm = [](std::string name, auto count) {

>From b92ad0cf7760c3f92bde507bb6c62956058c70a3 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 25 Feb 2025 13:12:51 -0500
Subject: [PATCH 10/20] Remove redundant benchmark for search_n

---
 .../nonmodifying/search_n.bench.cpp           | 54 +------------------
 1 file changed, 1 insertion(+), 53 deletions(-)

diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp
index a58386435fe85..de404fedaed3a 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp
@@ -38,7 +38,7 @@ int main(int argc, char** argv) {
     });
   };
 
-  // Benchmark {std,ranges}::search_n where the needle is never found
+  // Benchmark {std,ranges}::search_n where the needle is never found (worst case).
   {
     auto bm = []<class Container>(std::string name, auto search_n) {
       benchmark::RegisterBenchmark(
@@ -81,58 +81,6 @@ int main(int argc, char** argv) {
     bm.operator()<std::list<int>>("rng::search_n(list<int>, pred) (no match)", ranges_search_n_pred);
   }
 
-  // Benchmark {std,ranges}::search_n where we intersperse "near matches" inside the haystack
-  {
-    auto bm = []<class Container>(std::string name, auto search_n) {
-      benchmark::RegisterBenchmark(
-          name,
-          [search_n](auto& st) {
-            std::size_t const size = st.range(0);
-            using ValueType        = typename Container::value_type;
-            ValueType x            = Generate<ValueType>::random();
-            ValueType y            = random_different_from({x});
-            Container haystack(size, x);
-            std::size_t n = size / 10; // needle size is 10% of the haystack
-            assert(n > 0);
-
-            // intersperse near-matches inside the haystack
-            {
-              auto first = haystack.begin();
-              for (int i = 0; i != 10; ++i) {
-                first = std::fill_n(first, n - 1, y);
-                ++first; // this causes the subsequence not to match because it has length n-1
-              }
-            }
-
-            for ([[maybe_unused]] auto _ : st) {
-              benchmark::DoNotOptimize(haystack);
-              benchmark::DoNotOptimize(n);
-              benchmark::DoNotOptimize(y);
-              auto result = search_n(haystack.begin(), haystack.end(), n, y);
-              benchmark::DoNotOptimize(result);
-            }
-          })
-          ->Arg(1000) // non power-of-two
-          ->Arg(1024)
-          ->Arg(8192);
-    };
-    // {std,ranges}::search_n
-    bm.operator()<std::vector<int>>("std::search_n(vector<int>) (near matches)", std_search_n);
-    bm.operator()<std::deque<int>>("std::search_n(deque<int>) (near matches)", std_search_n);
-    bm.operator()<std::list<int>>("std::search_n(list<int>) (near matches)", std_search_n);
-    bm.operator()<std::vector<int>>("rng::search_n(vector<int>) (near matches)", std::ranges::search_n);
-    bm.operator()<std::deque<int>>("rng::search_n(deque<int>) (near matches)", std::ranges::search_n);
-    bm.operator()<std::list<int>>("rng::search_n(list<int>) (near matches)", std::ranges::search_n);
-
-    // {std,ranges}::search_n(pred)
-    bm.operator()<std::vector<int>>("std::search_n(vector<int>, pred) (near matches)", std_search_n_pred);
-    bm.operator()<std::deque<int>>("std::search_n(deque<int>, pred) (near matches)", std_search_n_pred);
-    bm.operator()<std::list<int>>("std::search_n(list<int>, pred) (near matches)", std_search_n_pred);
-    bm.operator()<std::vector<int>>("rng::search_n(vector<int>, pred) (near matches)", ranges_search_n_pred);
-    bm.operator()<std::deque<int>>("rng::search_n(deque<int>, pred) (near matches)", ranges_search_n_pred);
-    bm.operator()<std::list<int>>("rng::search_n(list<int>, pred) (near matches)", ranges_search_n_pred);
-  }
-
   benchmark::Initialize(&argc, argv);
   benchmark::RunSpecifiedBenchmarks();
   benchmark::Shutdown();

>From c2cc283cbbbe8d873d8d76870a6fd1b612b4a23d Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 25 Feb 2025 13:15:24 -0500
Subject: [PATCH 11/20] Simplify fold benchmark by using unsigned arithmetic

---
 .../algorithms/nonmodifying/fold.bench.cpp    | 25 ++++++++-----------
 1 file changed, 10 insertions(+), 15 deletions(-)

diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp
index 6711cec760e2c..379dec046b42f 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp
@@ -9,13 +9,12 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
-#include <cassert>
 #include <cstddef>
 #include <deque>
 #include <iterator>
-#include <limits>
 #include <list>
 #include <string>
+#include <type_traits>
 #include <vector>
 
 #include <benchmark/benchmark.h>
@@ -30,15 +29,11 @@ int main(int argc, char** argv) {
           [fold](auto& st) {
             std::size_t const size = st.range(0);
             using ValueType        = typename Container::value_type;
-            ValueType const limit  = 1000; // ensure we never overflow in the addition
-            assert(static_cast<ValueType>(size) <= std::numeric_limits<ValueType>::max());
-            assert(std::numeric_limits<ValueType>::max() > static_cast<ValueType>(size) * limit);
-            assert(std::numeric_limits<ValueType>::min() < static_cast<ValueType>(size) * limit * -1);
+            static_assert(std::is_unsigned_v<ValueType>,
+                          "We could encounter UB if signed arithmetic overflows in this benchmark");
 
             Container c;
-            std::generate_n(std::back_inserter(c), size, [&] {
-              return std::clamp(Generate<ValueType>::random(), -1 * limit, limit);
-            });
+            std::generate_n(std::back_inserter(c), size, [&] { return Generate<ValueType>::random(); });
             ValueType init = c.back();
             c.pop_back();
 
@@ -61,14 +56,14 @@ int main(int argc, char** argv) {
           ->Arg(8192)
           ->Arg(1 << 20);
     };
-    bm.operator()<std::vector<int>>("rng::fold_left(vector<int>)", std::ranges::fold_left);
-    bm.operator()<std::deque<int>>("rng::fold_left(deque<int>)", std::ranges::fold_left);
-    bm.operator()<std::list<int>>("rng::fold_left(list<int>)", std::ranges::fold_left);
+    bm.operator()<std::vector<unsigned int>>("rng::fold_left(vector<int>)", std::ranges::fold_left);
+    bm.operator()<std::deque<unsigned int>>("rng::fold_left(deque<int>)", std::ranges::fold_left);
+    bm.operator()<std::list<unsigned int>>("rng::fold_left(list<int>)", std::ranges::fold_left);
 
     // TODO: fold_right not implemented yet
-    // bm.operator()<std::vector<int>>("rng::fold_right(vector<int>)", std::ranges::fold_right);
-    // bm.operator()<std::deque<int>>("rng::fold_right(deque<int>)", std::ranges::fold_right);
-    // bm.operator()<std::list<int>>("rng::fold_right(list<int>)", std::ranges::fold_right);
+    // bm.operator()<std::vector<unsigned int>>("rng::fold_right(vector<int>)", std::ranges::fold_right);
+    // bm.operator()<std::deque<unsigned int>>("rng::fold_right(deque<int>)", std::ranges::fold_right);
+    // bm.operator()<std::list<unsigned int>>("rng::fold_right(list<int>)", std::ranges::fold_right);
   }
 
   benchmark::Initialize(&argc, argv);

>From e6075be6d7e241143f03003baa3fb0191dae3b86 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 25 Feb 2025 13:24:41 -0500
Subject: [PATCH 12/20] Add a few benchmarks on forward_list

---
 .../algorithms/nonmodifying/ends_with.bench.cpp    | 14 +++++++++++++-
 .../algorithms/nonmodifying/find_end.bench.cpp     | 13 +++++++++++--
 .../algorithms/nonmodifying/find_last.bench.cpp    |  9 +++++++++
 3 files changed, 33 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp
index cf4e6b5781ec5..f572773b10814 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp
@@ -9,8 +9,10 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
+#include <cassert>
 #include <cstddef>
 #include <deque>
+#include <forward_list>
 #include <list>
 #include <string>
 #include <vector>
@@ -39,7 +41,8 @@ int main(int argc, char** argv) {
             ValueType y            = random_different_from({x});
             Container c1(size, x);
             Container c2(size, x);
-            c2.back() = y;
+            assert(size != 0);
+            *std::next(c2.begin(), size - 1) = y; // set last element to y
 
             for ([[maybe_unused]] auto _ : st) {
               benchmark::DoNotOptimize(c1);
@@ -57,10 +60,14 @@ int main(int argc, char** argv) {
     bm.operator()<std::vector<int>>("rng::ends_with(vector<int>) (mismatch at end)", std::ranges::ends_with);
     bm.operator()<std::deque<int>>("rng::ends_with(deque<int>) (mismatch at end)", std::ranges::ends_with);
     bm.operator()<std::list<int>>("rng::ends_with(list<int>) (mismatch at end)", std::ranges::ends_with);
+    bm.operator()<std::forward_list<int>>(
+        "rng::ends_with(forward_list<int>) (mismatch at end)", std::ranges::ends_with);
 
     bm.operator()<std::vector<int>>("rng::ends_with(vector<int>, pred) (mismatch at end)", ranges_ends_with_pred);
     bm.operator()<std::deque<int>>("rng::ends_with(deque<int>, pred) (mismatch at end)", ranges_ends_with_pred);
     bm.operator()<std::list<int>>("rng::ends_with(list<int>, pred) (mismatch at end)", ranges_ends_with_pred);
+    bm.operator()<std::forward_list<int>>(
+        "rng::ends_with(forward_list<int>, pred) (mismatch at end)", ranges_ends_with_pred);
   }
 
   // Benchmark ranges::ends_with where we find the mismatching element at the very beginning.
@@ -75,6 +82,7 @@ int main(int argc, char** argv) {
             ValueType y            = random_different_from({x});
             Container c1(size, x);
             Container c2(size, x);
+            assert(size != 0);
             c2.front() = y;
 
             for ([[maybe_unused]] auto _ : st) {
@@ -93,10 +101,14 @@ int main(int argc, char** argv) {
     bm.operator()<std::vector<int>>("rng::ends_with(vector<int>) (mismatch at start)", std::ranges::ends_with);
     bm.operator()<std::deque<int>>("rng::ends_with(deque<int>) (mismatch at start)", std::ranges::ends_with);
     bm.operator()<std::list<int>>("rng::ends_with(list<int>) (mismatch at start)", std::ranges::ends_with);
+    bm.operator()<std::forward_list<int>>(
+        "rng::ends_with(forward_list<int>) (mismatch at start)", std::ranges::ends_with);
 
     bm.operator()<std::vector<int>>("rng::ends_with(vector<int>, pred) (mismatch at start)", ranges_ends_with_pred);
     bm.operator()<std::deque<int>>("rng::ends_with(deque<int>, pred) (mismatch at start)", ranges_ends_with_pred);
     bm.operator()<std::list<int>>("rng::ends_with(list<int>, pred) (mismatch at start)", ranges_ends_with_pred);
+    bm.operator()<std::forward_list<int>>(
+        "rng::ends_with(forward_list<int>, pred) (mismatch at start)", ranges_ends_with_pred);
   }
 
   benchmark::Initialize(&argc, argv);
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp
index 19f6ed2d55042..c61002e3617a2 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp
@@ -11,6 +11,7 @@
 #include <algorithm>
 #include <cstddef>
 #include <deque>
+#include <forward_list>
 #include <list>
 #include <string>
 #include <vector>
@@ -52,7 +53,7 @@ int main(int argc, char** argv) {
             Container subrange(size / 10, y); // subrange of length 10% of the full range
 
             // put the element we're searching for at 25% of the sequence
-            std::ranges::copy(subrange, std::next(c.begin(), c.size() / 4));
+            std::ranges::copy(subrange, std::next(c.begin(), size / 4));
 
             for ([[maybe_unused]] auto _ : st) {
               benchmark::DoNotOptimize(c);
@@ -70,17 +71,21 @@ int main(int argc, char** argv) {
     bm.operator()<std::vector<int>>("std::find_end(vector<int>) (bail 25%)", std_find_end);
     bm.operator()<std::deque<int>>("std::find_end(deque<int>) (bail 25%)", std_find_end);
     bm.operator()<std::list<int>>("std::find_end(list<int>) (bail 25%)", std_find_end);
+    bm.operator()<std::forward_list<int>>("std::find_end(forward_list<int>) (bail 25%)", std_find_end);
     bm.operator()<std::vector<int>>("rng::find_end(vector<int>) (bail 25%)", std::ranges::find_end);
     bm.operator()<std::deque<int>>("rng::find_end(deque<int>) (bail 25%)", std::ranges::find_end);
     bm.operator()<std::list<int>>("rng::find_end(list<int>) (bail 25%)", std::ranges::find_end);
+    bm.operator()<std::forward_list<int>>("rng::find_end(forward_list<int>) (bail 25%)", std::ranges::find_end);
 
     // {std,ranges}::find_end(it1, it1, it2, it2, pred)
     bm.operator()<std::vector<int>>("std::find_end(vector<int>, pred) (bail 25%)", std_find_end_pred);
     bm.operator()<std::deque<int>>("std::find_end(deque<int>, pred) (bail 25%)", std_find_end_pred);
     bm.operator()<std::list<int>>("std::find_end(list<int>, pred) (bail 25%)", std_find_end_pred);
+    bm.operator()<std::forward_list<int>>("std::find_end(forward_list<int>, pred) (bail 25%)", std_find_end_pred);
     bm.operator()<std::vector<int>>("rng::find_end(vector<int>, pred) (bail 25%)", ranges_find_end_pred);
     bm.operator()<std::deque<int>>("rng::find_end(deque<int>, pred) (bail 25%)", ranges_find_end_pred);
     bm.operator()<std::list<int>>("rng::find_end(list<int>, pred) (bail 25%)", ranges_find_end_pred);
+    bm.operator()<std::forward_list<int>>("rng::find_end(forward_list<int>, pred) (bail 25%)", ranges_find_end_pred);
   }
 
   // Benchmark {std,ranges}::find_end where the subsequence is found
@@ -98,7 +103,7 @@ int main(int argc, char** argv) {
             Container subrange(size / 10, y); // subrange of length 10% of the full range
 
             // put the element we're searching for at 90% of the sequence
-            std::ranges::copy(subrange, std::next(c.begin(), 9 * (c.size() / 10)));
+            std::ranges::copy(subrange, std::next(c.begin(), (9 * size) / 10));
 
             for ([[maybe_unused]] auto _ : st) {
               benchmark::DoNotOptimize(c);
@@ -116,17 +121,21 @@ int main(int argc, char** argv) {
     bm.operator()<std::vector<int>>("std::find_end(vector<int>) (bail 90%)", std_find_end);
     bm.operator()<std::deque<int>>("std::find_end(deque<int>) (bail 90%)", std_find_end);
     bm.operator()<std::list<int>>("std::find_end(list<int>) (bail 90%)", std_find_end);
+    bm.operator()<std::forward_list<int>>("std::find_end(forward_list<int>) (bail 90%)", std_find_end);
     bm.operator()<std::vector<int>>("rng::find_end(vector<int>) (bail 90%)", std::ranges::find_end);
     bm.operator()<std::deque<int>>("rng::find_end(deque<int>) (bail 90%)", std::ranges::find_end);
     bm.operator()<std::list<int>>("rng::find_end(list<int>) (bail 90%)", std::ranges::find_end);
+    bm.operator()<std::forward_list<int>>("rng::find_end(forward_list<int>) (bail 90%)", std::ranges::find_end);
 
     // {std,ranges}::find_end(it1, it1, it2, it2, pred)
     bm.operator()<std::vector<int>>("std::find_end(vector<int>, pred) (bail 90%)", std_find_end_pred);
     bm.operator()<std::deque<int>>("std::find_end(deque<int>, pred) (bail 90%)", std_find_end_pred);
     bm.operator()<std::list<int>>("std::find_end(list<int>, pred) (bail 90%)", std_find_end_pred);
+    bm.operator()<std::forward_list<int>>("std::find_end(forward_list<int>, pred) (bail 90%)", std_find_end_pred);
     bm.operator()<std::vector<int>>("rng::find_end(vector<int>, pred) (bail 90%)", ranges_find_end_pred);
     bm.operator()<std::deque<int>>("rng::find_end(deque<int>, pred) (bail 90%)", ranges_find_end_pred);
     bm.operator()<std::list<int>>("rng::find_end(list<int>, pred) (bail 90%)", ranges_find_end_pred);
+    bm.operator()<std::forward_list<int>>("rng::find_end(forward_list<int>, pred) (bail 90%)", ranges_find_end_pred);
   }
 
   benchmark::Initialize(&argc, argv);
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp
index cac999b080f7c..ee65ace061941 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp
@@ -11,6 +11,7 @@
 #include <algorithm>
 #include <cstddef>
 #include <deque>
+#include <forward_list>
 #include <list>
 #include <string>
 #include <vector>
@@ -67,18 +68,22 @@ int main(int argc, char** argv) {
     bm.operator()<std::vector<int>>("rng::find_last(vector<int>) (bail 25%)", std::ranges::find_last);
     bm.operator()<std::deque<int>>("rng::find_last(deque<int>) (bail 25%)", std::ranges::find_last);
     bm.operator()<std::list<int>>("rng::find_last(list<int>) (bail 25%)", std::ranges::find_last);
+    bm.operator()<std::forward_list<int>>("rng::find_last(forward_list<int>) (bail 25%)", std::ranges::find_last);
 
     // find_last_if
     bm.operator()<std::vector<char>>("rng::find_last_if(vector<char>) (bail 25%)", ranges_find_last_if);
     bm.operator()<std::vector<int>>("rng::find_last_if(vector<int>) (bail 25%)", ranges_find_last_if);
     bm.operator()<std::deque<int>>("rng::find_last_if(deque<int>) (bail 25%)", ranges_find_last_if);
     bm.operator()<std::list<int>>("rng::find_last_if(list<int>) (bail 25%)", ranges_find_last_if);
+    bm.operator()<std::forward_list<int>>("rng::find_last_if(forward_list<int>) (bail 25%)", ranges_find_last_if);
 
     // find_last_if_not
     bm.operator()<std::vector<char>>("rng::find_last_if_not(vector<char>) (bail 25%)", ranges_find_last_if_not);
     bm.operator()<std::vector<int>>("rng::find_last_if_not(vector<int>) (bail 25%)", ranges_find_last_if_not);
     bm.operator()<std::deque<int>>("rng::find_last_if_not(deque<int>) (bail 25%)", ranges_find_last_if_not);
     bm.operator()<std::list<int>>("rng::find_last_if_not(list<int>) (bail 25%)", ranges_find_last_if_not);
+    bm.operator()<std::forward_list<int>>(
+        "rng::find_last_if_not(forward_list<int>) (bail 25%)", ranges_find_last_if_not);
   }
 
   // Benchmark ranges::{find_last,find_last_if,find_last_if_not} where the last element
@@ -115,18 +120,22 @@ int main(int argc, char** argv) {
     bm.operator()<std::vector<int>>("rng::find_last(vector<int>) (bail 90%)", std::ranges::find_last);
     bm.operator()<std::deque<int>>("rng::find_last(deque<int>) (bail 90%)", std::ranges::find_last);
     bm.operator()<std::list<int>>("rng::find_last(list<int>) (bail 90%)", std::ranges::find_last);
+    bm.operator()<std::forward_list<int>>("rng::find_last(forward_list<int>) (bail 90%)", std::ranges::find_last);
 
     // find_last_if
     bm.operator()<std::vector<char>>("rng::find_last_if(vector<char>) (bail 90%)", ranges_find_last_if);
     bm.operator()<std::vector<int>>("rng::find_last_if(vector<int>) (bail 90%)", ranges_find_last_if);
     bm.operator()<std::deque<int>>("rng::find_last_if(deque<int>) (bail 90%)", ranges_find_last_if);
     bm.operator()<std::list<int>>("rng::find_last_if(list<int>) (bail 90%)", ranges_find_last_if);
+    bm.operator()<std::forward_list<int>>("rng::find_last_if(forward_list<int>) (bail 90%)", ranges_find_last_if);
 
     // find_last_if_not
     bm.operator()<std::vector<char>>("rng::find_last_if_not(vector<char>) (bail 90%)", ranges_find_last_if_not);
     bm.operator()<std::vector<int>>("rng::find_last_if_not(vector<int>) (bail 90%)", ranges_find_last_if_not);
     bm.operator()<std::deque<int>>("rng::find_last_if_not(deque<int>) (bail 90%)", ranges_find_last_if_not);
     bm.operator()<std::list<int>>("rng::find_last_if_not(list<int>) (bail 90%)", ranges_find_last_if_not);
+    bm.operator()<std::forward_list<int>>(
+        "rng::find_last_if_not(forward_list<int>) (bail 90%)", ranges_find_last_if_not);
   }
 
   benchmark::Initialize(&argc, argv);

>From 750b01c1b77213abdf1e66c70ff37038870a3284 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 17 Mar 2025 10:07:46 -0400
Subject: [PATCH 13/20] Re-add benchmark for std::equal with vector<bool>

---
 .../algorithms/nonmodifying/equal.bench.cpp   | 36 +++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/equal.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/equal.bench.cpp
index 9feef4e1ac22b..03533ec4304ab 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/equal.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/equal.bench.cpp
@@ -100,6 +100,42 @@ int main(int argc, char** argv) {
     bm.operator()<std::list<int>>("rng::equal(list<int>) (it, it, it, it, pred)", ranges_equal_4leg_pred);
   }
 
+  // Benchmark {std,ranges}::equal on vector<bool>.
+  {
+    auto bm = [](std::string name, auto equal, bool aligned) {
+      benchmark::RegisterBenchmark(
+          name,
+          [=](auto& st) {
+            std::size_t const size = st.range();
+            std::vector<bool> c1(size, true);
+            std::vector<bool> c2(size + 8, true);
+            auto first1 = c1.begin();
+            auto last1  = c1.end();
+            auto first2 = aligned ? c2.begin() : c2.begin() + 4;
+            auto last2  = aligned ? c2.end() : c2.end() - 4;
+            for (auto _ : st) {
+              benchmark::DoNotOptimize(c1);
+              benchmark::DoNotOptimize(c2);
+              auto result = equal(first1, last1, first2, last2);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(50) // non power-of-two
+          ->Arg(1024)
+          ->Arg(8192)
+          ->Arg(1 << 20);
+    };
+
+    // {std,ranges}::equal(vector<bool>) (aligned)
+    bm("std::equal(vector<bool>) (aligned)", std_equal_4leg, true);
+    bm("rng::equal(vector<bool>) (aligned)", std::ranges::equal, true);
+
+    // {std,ranges}::equal(vector<bool>) (unaligned)
+    bm("std::equal(vector<bool>) (unaligned)", std_equal_4leg, false);
+    bm("rng::equal(vector<bool>) (unaligned)", std::ranges::equal, false);
+  }
+
   benchmark::Initialize(&argc, argv);
   benchmark::RunSpecifiedBenchmarks();
   benchmark::Shutdown();

>From fc02f9266e638166c6e983f6d1ab186683d47909 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 17 Mar 2025 10:12:09 -0400
Subject: [PATCH 14/20] Remove some benchmarks bailing out at 25% where that
 doesn't provide additional information

---
 .../nonmodifying/any_all_none_of.bench.cpp    | 58 +------------------
 .../nonmodifying/contains.bench.cpp           | 36 +-----------
 .../nonmodifying/contains_subrange.bench.cpp  | 37 +-----------
 3 files changed, 6 insertions(+), 125 deletions(-)

diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/any_all_none_of.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/any_all_none_of.bench.cpp
index 66eba4e50f83d..2bbd233655ce5 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/any_all_none_of.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/any_all_none_of.bench.cpp
@@ -39,62 +39,8 @@ int main(int argc, char** argv) {
     return !std::ranges::none_of(first, last, pred);
   };
 
-  // Benchmark {std,ranges}::{any_of,all_of,none_of} where we bail out early
-  // (after visiting 25% of the elements).
-  {
-    auto bm = []<class Container>(std::string name, auto any_of) {
-      benchmark::RegisterBenchmark(
-          name,
-          [any_of](auto& st) {
-            std::size_t const size = st.range(0);
-            using ValueType        = typename Container::value_type;
-            ValueType x            = Generate<ValueType>::random();
-            ValueType y            = random_different_from({x});
-            Container c(size, x);
-            *std::next(c.begin(), size / 4) = y; // bail out after the first 25% elements
-
-            for (auto _ : st) {
-              benchmark::DoNotOptimize(c);
-              auto result = any_of(c.begin(), c.end(), [&](auto element) {
-                benchmark::DoNotOptimize(element);
-                return element == y;
-              });
-              benchmark::DoNotOptimize(result);
-            }
-          })
-          ->Arg(8)
-          ->Arg(32)
-          ->Arg(50) // non power-of-two
-          ->Arg(8192)
-          ->Arg(32768);
-    };
-
-    // any_of
-    bm.operator()<std::vector<int>>("std::any_of(vector<int>) (bail 25%)", std_any_of);
-    bm.operator()<std::deque<int>>("std::any_of(deque<int>) (bail 25%)", std_any_of);
-    bm.operator()<std::list<int>>("std::any_of(list<int>) (bail 25%)", std_any_of);
-    bm.operator()<std::vector<int>>("rng::any_of(vector<int>) (bail 25%)", std::ranges::any_of);
-    bm.operator()<std::deque<int>>("rng::any_of(deque<int>) (bail 25%)", std::ranges::any_of);
-    bm.operator()<std::list<int>>("rng::any_of(list<int>) (bail 25%)", std::ranges::any_of);
-
-    // all_of
-    bm.operator()<std::vector<int>>("std::all_of(vector<int>) (bail 25%)", std_all_of);
-    bm.operator()<std::deque<int>>("std::all_of(deque<int>) (bail 25%)", std_all_of);
-    bm.operator()<std::list<int>>("std::all_of(list<int>) (bail 25%)", std_all_of);
-    bm.operator()<std::vector<int>>("rng::all_of(vector<int>) (bail 25%)", ranges_all_of);
-    bm.operator()<std::deque<int>>("rng::all_of(deque<int>) (bail 25%)", ranges_all_of);
-    bm.operator()<std::list<int>>("rng::all_of(list<int>) (bail 25%)", ranges_all_of);
-
-    // none_of
-    bm.operator()<std::vector<int>>("std::none_of(vector<int>) (bail 25%)", std_none_of);
-    bm.operator()<std::deque<int>>("std::none_of(deque<int>) (bail 25%)", std_none_of);
-    bm.operator()<std::list<int>>("std::none_of(list<int>) (bail 25%)", std_none_of);
-    bm.operator()<std::vector<int>>("rng::none_of(vector<int>) (bail 25%)", ranges_none_of);
-    bm.operator()<std::deque<int>>("rng::none_of(deque<int>) (bail 25%)", ranges_none_of);
-    bm.operator()<std::list<int>>("rng::none_of(list<int>) (bail 25%)", ranges_none_of);
-  }
-
-  // Benchmark {std,ranges}::{any_of,all_of,none_of} where we process the whole sequence.
+  // Benchmark {std,ranges}::{any_of,all_of,none_of} where we process the whole sequence,
+  // which is the worst case.
   {
     auto bm = []<class Container>(std::string name, auto any_of) {
       benchmark::RegisterBenchmark(
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp
index 50d8c5fd331cd..5b41adbd1f4d9 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp
@@ -19,40 +19,8 @@
 #include "../../GenerateInput.h"
 
 int main(int argc, char** argv) {
-  // Benchmark ranges::contains where we bail out early (after visiting 25% of the elements).
-  {
-    auto bm = []<class Container>(std::string name) {
-      benchmark::RegisterBenchmark(
-          name,
-          [](auto& st) {
-            std::size_t const size = st.range(0);
-            using ValueType        = typename Container::value_type;
-            ValueType x            = Generate<ValueType>::random();
-            ValueType y            = random_different_from({x});
-            Container c(size, x);
-            *std::next(c.begin(), size / 4) = y; // bail out after checking 25% of values
-            auto first                      = c.begin();
-            auto last                       = c.end();
-
-            for (auto _ : st) {
-              benchmark::DoNotOptimize(c);
-              benchmark::DoNotOptimize(y);
-              auto result = std::ranges::contains(first, last, y);
-              benchmark::DoNotOptimize(result);
-            }
-          })
-          ->Arg(8)
-          ->Arg(32)
-          ->Arg(50) // non power-of-two
-          ->Arg(8192)
-          ->Arg(1 << 20);
-    };
-    bm.operator()<std::vector<int>>("rng::contains(vector<int>) (bail 25%)");
-    bm.operator()<std::deque<int>>("rng::contains(deque<int>) (bail 25%)");
-    bm.operator()<std::list<int>>("rng::contains(list<int>) (bail 25%)");
-  }
-
-  // Benchmark ranges::contains where we process the whole sequence.
+  // Benchmark ranges::contains where we process the whole sequence, which is the
+  // worst case.
   {
     auto bm = []<class Container>(std::string name) {
       benchmark::RegisterBenchmark(
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp
index 96676bd2921f5..b203bb45fa3a2 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp
@@ -19,41 +19,8 @@
 #include "../../GenerateInput.h"
 
 int main(int argc, char** argv) {
-  // Benchmark ranges::contains_subrange where we find our target starting at 25% of the elements
-  {
-    auto bm = []<class Container>(std::string name) {
-      benchmark::RegisterBenchmark(
-          name,
-          [](auto& st) {
-            std::size_t const size = st.range(0);
-            using ValueType        = typename Container::value_type;
-            ValueType x            = Generate<ValueType>::random();
-            ValueType y            = random_different_from({x});
-            Container c(size, x);
-            Container subrange(size / 10, y); // subrange of length 10% of the full range
-
-            // At 25% of the range, put the subrange we're going to find
-            std::ranges::copy(subrange, std::next(c.begin(), c.size() / 4));
-
-            for (auto _ : st) {
-              benchmark::DoNotOptimize(c);
-              benchmark::DoNotOptimize(subrange);
-              auto result = std::ranges::contains_subrange(c, subrange);
-              benchmark::DoNotOptimize(result);
-            }
-          })
-          ->Arg(16)
-          ->Arg(32)
-          ->Arg(50) // non power-of-two
-          ->Arg(8192)
-          ->Arg(1 << 20);
-    };
-    bm.operator()<std::vector<int>>("rng::contains_subrange(vector<int>) (bail 25%)");
-    bm.operator()<std::deque<int>>("rng::contains_subrange(deque<int>) (bail 25%)");
-    bm.operator()<std::list<int>>("rng::contains_subrange(list<int>) (bail 25%)");
-  }
-
-  // Benchmark ranges::contains_subrange where we never find our target
+  // Benchmark ranges::contains_subrange where we never find our target, which is the
+  // worst case.
   {
     auto bm = []<class Container>(std::string name) {
       benchmark::RegisterBenchmark(

>From 76d15cc101c55dcd8c7a7b7435cf99eb3faeefee Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 17 Mar 2025 10:20:31 -0400
Subject: [PATCH 15/20] Use 90% and 10% for symmetry

---
 .../nonmodifying/find_first_of.bench.cpp      | 20 +++++-----
 .../nonmodifying/find_last.bench.cpp          | 38 +++++++++----------
 2 files changed, 29 insertions(+), 29 deletions(-)

diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp
index 6525f8c384f76..9a8818038fe16 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp
@@ -38,7 +38,7 @@ int main(int argc, char** argv) {
     });
   };
 
-  // Benchmark {std,ranges}::find_first_of where we have a hit at 25% of the haystack
+  // Benchmark {std,ranges}::find_first_of where we have a hit at 10% of the haystack
   // and at the end of the needle. This measures how quickly we're able to search inside
   // the needle.
   {
@@ -55,8 +55,8 @@ int main(int argc, char** argv) {
             Container needle(size, y);
             needle.back() = z; // hit at the very end of the needle
 
-            // put the needle at 25% of the haystack
-            *std::next(haystack.begin(), haystack.size() / 4) = z;
+            // put the needle at 10% of the haystack
+            *std::next(haystack.begin(), haystack.size() / 10) = z;
 
             for ([[maybe_unused]] auto _ : st) {
               benchmark::DoNotOptimize(haystack);
@@ -71,15 +71,15 @@ int main(int argc, char** argv) {
           ->Arg(8192);
     };
     // {std,ranges}::find_first_of(it1, it1, it2, it2)
-    bm.operator()<std::vector<int>>("std::find_first_of(vector<int>) (25% haystack, late needle)", std_find_first_of);
-    bm.operator()<std::deque<int>>("std::find_first_of(deque<int>) (25% haystack, late needle)", std_find_first_of);
-    bm.operator()<std::list<int>>("std::find_first_of(list<int>) (25% haystack, late needle)", std_find_first_of);
+    bm.operator()<std::vector<int>>("std::find_first_of(vector<int>) (10% haystack, late needle)", std_find_first_of);
+    bm.operator()<std::deque<int>>("std::find_first_of(deque<int>) (10% haystack, late needle)", std_find_first_of);
+    bm.operator()<std::list<int>>("std::find_first_of(list<int>) (10% haystack, late needle)", std_find_first_of);
     bm.operator()<std::vector<int>>(
-        "rng::find_first_of(vector<int>) (25% haystack, late needle)", std::ranges::find_first_of);
+        "rng::find_first_of(vector<int>) (10% haystack, late needle)", std::ranges::find_first_of);
     bm.operator()<std::deque<int>>(
-        "rng::find_first_of(deque<int>) (25% haystack, late needle)", std::ranges::find_first_of);
+        "rng::find_first_of(deque<int>) (10% haystack, late needle)", std::ranges::find_first_of);
     bm.operator()<std::list<int>>(
-        "rng::find_first_of(list<int>) (25% haystack, late needle)", std::ranges::find_first_of);
+        "rng::find_first_of(list<int>) (10% haystack, late needle)", std::ranges::find_first_of);
 
     // {std,ranges}::find_first_of(it1, it1, it2, it2, pred)
     bm.operator()<std::vector<int>>(
@@ -113,7 +113,7 @@ int main(int argc, char** argv) {
             *std::next(needle.begin(), needle.size() / 10) = z; // hit at 10% of the needle
 
             // put the needle at 90% of the haystack
-            *std::next(haystack.begin(), 9 * (haystack.size() / 10)) = z;
+            *std::next(haystack.begin(), (9 * haystack.size()) / 10) = z;
 
             for ([[maybe_unused]] auto _ : st) {
               benchmark::DoNotOptimize(haystack);
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp
index ee65ace061941..f0a48a830c2c6 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp
@@ -34,7 +34,7 @@ int main(int argc, char** argv) {
   };
 
   // Benchmark ranges::{find_last,find_last_if,find_last_if_not} where the last element
-  // is found 25% into the sequence
+  // is found 10% into the sequence
   {
     auto bm = []<class Container>(std::string name, auto find_last) {
       benchmark::RegisterBenchmark(
@@ -46,8 +46,8 @@ int main(int argc, char** argv) {
             ValueType y            = random_different_from({x});
             Container c(size, x);
 
-            // put the element we're searching for at 25% of the sequence
-            *std::next(c.begin(), size / 4) = y;
+            // put the element we're searching for at 10% of the sequence
+            *std::next(c.begin(), size / 10) = y;
 
             for ([[maybe_unused]] auto _ : st) {
               benchmark::DoNotOptimize(c);
@@ -64,26 +64,26 @@ int main(int argc, char** argv) {
     };
 
     // find_last
-    bm.operator()<std::vector<char>>("rng::find_last(vector<char>) (bail 25%)", std::ranges::find_last);
-    bm.operator()<std::vector<int>>("rng::find_last(vector<int>) (bail 25%)", std::ranges::find_last);
-    bm.operator()<std::deque<int>>("rng::find_last(deque<int>) (bail 25%)", std::ranges::find_last);
-    bm.operator()<std::list<int>>("rng::find_last(list<int>) (bail 25%)", std::ranges::find_last);
-    bm.operator()<std::forward_list<int>>("rng::find_last(forward_list<int>) (bail 25%)", std::ranges::find_last);
+    bm.operator()<std::vector<char>>("rng::find_last(vector<char>) (bail 10%)", std::ranges::find_last);
+    bm.operator()<std::vector<int>>("rng::find_last(vector<int>) (bail 10%)", std::ranges::find_last);
+    bm.operator()<std::deque<int>>("rng::find_last(deque<int>) (bail 10%)", std::ranges::find_last);
+    bm.operator()<std::list<int>>("rng::find_last(list<int>) (bail 10%)", std::ranges::find_last);
+    bm.operator()<std::forward_list<int>>("rng::find_last(forward_list<int>) (bail 10%)", std::ranges::find_last);
 
     // find_last_if
-    bm.operator()<std::vector<char>>("rng::find_last_if(vector<char>) (bail 25%)", ranges_find_last_if);
-    bm.operator()<std::vector<int>>("rng::find_last_if(vector<int>) (bail 25%)", ranges_find_last_if);
-    bm.operator()<std::deque<int>>("rng::find_last_if(deque<int>) (bail 25%)", ranges_find_last_if);
-    bm.operator()<std::list<int>>("rng::find_last_if(list<int>) (bail 25%)", ranges_find_last_if);
-    bm.operator()<std::forward_list<int>>("rng::find_last_if(forward_list<int>) (bail 25%)", ranges_find_last_if);
+    bm.operator()<std::vector<char>>("rng::find_last_if(vector<char>) (bail 10%)", ranges_find_last_if);
+    bm.operator()<std::vector<int>>("rng::find_last_if(vector<int>) (bail 10%)", ranges_find_last_if);
+    bm.operator()<std::deque<int>>("rng::find_last_if(deque<int>) (bail 10%)", ranges_find_last_if);
+    bm.operator()<std::list<int>>("rng::find_last_if(list<int>) (bail 10%)", ranges_find_last_if);
+    bm.operator()<std::forward_list<int>>("rng::find_last_if(forward_list<int>) (bail 10%)", ranges_find_last_if);
 
     // find_last_if_not
-    bm.operator()<std::vector<char>>("rng::find_last_if_not(vector<char>) (bail 25%)", ranges_find_last_if_not);
-    bm.operator()<std::vector<int>>("rng::find_last_if_not(vector<int>) (bail 25%)", ranges_find_last_if_not);
-    bm.operator()<std::deque<int>>("rng::find_last_if_not(deque<int>) (bail 25%)", ranges_find_last_if_not);
-    bm.operator()<std::list<int>>("rng::find_last_if_not(list<int>) (bail 25%)", ranges_find_last_if_not);
+    bm.operator()<std::vector<char>>("rng::find_last_if_not(vector<char>) (bail 10%)", ranges_find_last_if_not);
+    bm.operator()<std::vector<int>>("rng::find_last_if_not(vector<int>) (bail 10%)", ranges_find_last_if_not);
+    bm.operator()<std::deque<int>>("rng::find_last_if_not(deque<int>) (bail 10%)", ranges_find_last_if_not);
+    bm.operator()<std::list<int>>("rng::find_last_if_not(list<int>) (bail 10%)", ranges_find_last_if_not);
     bm.operator()<std::forward_list<int>>(
-        "rng::find_last_if_not(forward_list<int>) (bail 25%)", ranges_find_last_if_not);
+        "rng::find_last_if_not(forward_list<int>) (bail 10%)", ranges_find_last_if_not);
   }
 
   // Benchmark ranges::{find_last,find_last_if,find_last_if_not} where the last element
@@ -100,7 +100,7 @@ int main(int argc, char** argv) {
             Container c(size, x);
 
             // put the element we're searching for at 90% of the sequence
-            *std::next(c.begin(), 9 * (size / 10)) = y;
+            *std::next(c.begin(), (9 * size) / 10) = y;
 
             for ([[maybe_unused]] auto _ : st) {
               benchmark::DoNotOptimize(c);

>From 252941e71c2957074ae121e17ff6070145c20ce8 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 17 Mar 2025 15:12:59 -0400
Subject: [PATCH 16/20] Fix benchmarks for contains_subrange and find_end

---
 .../nonmodifying/contains_subrange.bench.cpp  | 116 +++++++++-
 .../nonmodifying/find_end.bench.cpp           | 199 ++++++++++++------
 2 files changed, 250 insertions(+), 65 deletions(-)

diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp
index b203bb45fa3a2..4e20173aa5859 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp
@@ -9,6 +9,7 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
 #include <algorithm>
+#include <cassert>
 #include <cstddef>
 #include <deque>
 #include <iterator>
@@ -19,7 +20,7 @@
 #include "../../GenerateInput.h"
 
 int main(int argc, char** argv) {
-  // Benchmark ranges::contains_subrange where we never find our target, which is the
+  // Benchmark ranges::contains_subrange where we never find the needle, which is the
   // worst case.
   {
     auto bm = []<class Container>(std::string name) {
@@ -30,13 +31,15 @@ int main(int argc, char** argv) {
             using ValueType        = typename Container::value_type;
             ValueType x            = Generate<ValueType>::random();
             ValueType y            = random_different_from({x});
-            Container c(size, x);
-            Container subrange(size / 10, y); // subrange of length 10% of the full range, but we'll never find it
+            Container haystack(size, x);
+            std::size_t n = size / 10; // needle size is 10% of the haystack, but we'll never find it
+            assert(n > 0);
+            Container needle(n, y);
 
             for (auto _ : st) {
-              benchmark::DoNotOptimize(c);
-              benchmark::DoNotOptimize(subrange);
-              auto result = std::ranges::contains_subrange(c, subrange);
+              benchmark::DoNotOptimize(haystack);
+              benchmark::DoNotOptimize(needle);
+              auto result = std::ranges::contains_subrange(haystack, needle);
               benchmark::DoNotOptimize(result);
             }
           })
@@ -51,6 +54,107 @@ int main(int argc, char** argv) {
     bm.operator()<std::list<int>>("rng::contains_subrange(list<int>) (process all)");
   }
 
+  // Benchmark ranges::contains_subrange where we intersperse "near matches" inside the haystack.
+  {
+    auto bm = []<class Container>(std::string name) {
+      benchmark::RegisterBenchmark(
+          name,
+          [](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            ValueType x            = Generate<ValueType>::random();
+            ValueType y            = random_different_from({x});
+            Container haystack(size, x);
+            std::size_t n = size / 10; // needle size is 10% of the haystack, but we'll never find it
+            assert(n > 0);
+            Container needle(n, y);
+
+            // intersperse near-matches inside the haystack
+            {
+              auto first = haystack.begin();
+              for (int i = 0; i != 10; ++i) {
+                first = std::copy_n(needle.begin(), n - 1, first);
+                ++first; // this causes the subsequence not to match because it has length n-1
+              }
+            }
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(haystack);
+              benchmark::DoNotOptimize(needle);
+              auto result = std::ranges::contains_subrange(haystack, needle);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(1000) // non power-of-two
+          ->Arg(1024)
+          ->Arg(8192);
+    };
+    bm.operator()<std::vector<int>>("rng::contains_subrange(vector<int>) (near matches)");
+    bm.operator()<std::deque<int>>("rng::contains_subrange(deque<int>) (near matches)");
+    bm.operator()<std::list<int>>("rng::contains_subrange(list<int>) (near matches)");
+  }
+
+  // Special case: the two ranges are the same length (and they are equal, which is the worst case).
+  {
+    auto bm = []<class Container>(std::string name) {
+      benchmark::RegisterBenchmark(
+          name,
+          [](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            ValueType x            = Generate<ValueType>::random();
+            Container haystack(size, x);
+            Container needle(size, x);
+
+            for (auto _ : st) {
+              benchmark::DoNotOptimize(haystack);
+              benchmark::DoNotOptimize(needle);
+              auto result = std::ranges::contains_subrange(haystack, needle);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(16)
+          ->Arg(32)
+          ->Arg(50) // non power-of-two
+          ->Arg(8192)
+          ->Arg(1 << 20);
+    };
+    bm.operator()<std::vector<int>>("rng::contains_subrange(vector<int>) (same length)");
+    bm.operator()<std::deque<int>>("rng::contains_subrange(deque<int>) (same length)");
+    bm.operator()<std::list<int>>("rng::contains_subrange(list<int>) (same length)");
+  }
+
+  // Special case: the needle contains a single element (which we never find, i.e. the worst case).
+  {
+    auto bm = []<class Container>(std::string name) {
+      benchmark::RegisterBenchmark(
+          name,
+          [](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            ValueType x            = Generate<ValueType>::random();
+            ValueType y            = random_different_from({x});
+            Container haystack(size, x);
+            Container needle(1, y);
+
+            for (auto _ : st) {
+              benchmark::DoNotOptimize(haystack);
+              benchmark::DoNotOptimize(needle);
+              auto result = std::ranges::contains_subrange(haystack, needle);
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(16)
+          ->Arg(32)
+          ->Arg(50) // non power-of-two
+          ->Arg(8192)
+          ->Arg(1 << 20);
+    };
+    bm.operator()<std::vector<int>>("rng::contains_subrange(vector<int>) (single element)");
+    bm.operator()<std::deque<int>>("rng::contains_subrange(deque<int>) (single element)");
+    bm.operator()<std::list<int>>("rng::contains_subrange(list<int>) (single element)");
+  }
+
   benchmark::Initialize(&argc, argv);
   benchmark::RunSpecifiedBenchmarks();
   benchmark::Shutdown();
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp
index c61002e3617a2..32a4d1e91d7d6 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp
@@ -9,6 +9,7 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 
 #include <algorithm>
+#include <cassert>
 #include <cstddef>
 #include <deque>
 #include <forward_list>
@@ -38,8 +39,34 @@ int main(int argc, char** argv) {
     });
   };
 
-  // Benchmark {std,ranges}::find_end where the subsequence is found
-  // 25% into the sequence
+  auto register_benchmarks = [&](auto bm, std::string comment) {
+    // {std,ranges}::find_end(it1, it1, it2, it2)
+    bm.template operator()<std::vector<int>>("std::find_end(vector<int>) (" + comment + ")", std_find_end);
+    bm.template operator()<std::deque<int>>("std::find_end(deque<int>) (" + comment + ")", std_find_end);
+    bm.template operator()<std::list<int>>("std::find_end(list<int>) (" + comment + ")", std_find_end);
+    bm.template operator()<std::forward_list<int>>("std::find_end(forward_list<int>) (" + comment + ")", std_find_end);
+    bm.template operator()<std::vector<int>>("rng::find_end(vector<int>) (" + comment + ")", std::ranges::find_end);
+    bm.template operator()<std::deque<int>>("rng::find_end(deque<int>) (" + comment + ")", std::ranges::find_end);
+    bm.template operator()<std::list<int>>("rng::find_end(list<int>) (" + comment + ")", std::ranges::find_end);
+    bm.template operator()<std::forward_list<int>>(
+        "rng::find_end(forward_list<int>) (" + comment + ")", std::ranges::find_end);
+
+    // {std,ranges}::find_end(it1, it1, it2, it2, pred)
+    bm.template operator()<std::vector<int>>("std::find_end(vector<int>, pred) (" + comment + ")", std_find_end_pred);
+    bm.template operator()<std::deque<int>>("std::find_end(deque<int>, pred) (" + comment + ")", std_find_end_pred);
+    bm.template operator()<std::list<int>>("std::find_end(list<int>, pred) (" + comment + ")", std_find_end_pred);
+    bm.template operator()<std::forward_list<int>>(
+        "std::find_end(forward_list<int>, pred) (" + comment + ")", std_find_end_pred);
+    bm.template operator()<std::vector<int>>(
+        "rng::find_end(vector<int>, pred) (" + comment + ")", ranges_find_end_pred);
+    bm.template operator()<std::deque<int>>("rng::find_end(deque<int>, pred) (" + comment + ")", ranges_find_end_pred);
+    bm.template operator()<std::list<int>>("rng::find_end(list<int>, pred) (" + comment + ")", ranges_find_end_pred);
+    bm.template operator()<std::forward_list<int>>(
+        "rng::find_end(forward_list<int>, pred) (" + comment + ")", ranges_find_end_pred);
+  };
+
+  // Benchmark {std,ranges}::find_end where we never find the needle, which is the
+  // worst case.
   {
     auto bm = []<class Container>(std::string name, auto find_end) {
       benchmark::RegisterBenchmark(
@@ -49,16 +76,15 @@ int main(int argc, char** argv) {
             using ValueType        = typename Container::value_type;
             ValueType x            = Generate<ValueType>::random();
             ValueType y            = random_different_from({x});
-            Container c(size, x);
-            Container subrange(size / 10, y); // subrange of length 10% of the full range
+            Container haystack(size, x);
+            std::size_t n = size / 10; // needle size is 10% of the haystack, but we'll never find it
+            assert(n > 0);
+            Container needle(n, y);
 
-            // put the element we're searching for at 25% of the sequence
-            std::ranges::copy(subrange, std::next(c.begin(), size / 4));
-
-            for ([[maybe_unused]] auto _ : st) {
-              benchmark::DoNotOptimize(c);
-              benchmark::DoNotOptimize(subrange);
-              auto result = find_end(c.begin(), c.end(), subrange.begin(), subrange.end());
+            for (auto _ : st) {
+              benchmark::DoNotOptimize(haystack);
+              benchmark::DoNotOptimize(needle);
+              auto result = find_end(haystack.begin(), haystack.end(), needle.begin(), needle.end());
               benchmark::DoNotOptimize(result);
             }
           })
@@ -67,29 +93,10 @@ int main(int argc, char** argv) {
           ->Arg(8192)
           ->Arg(1 << 20);
     };
-    // {std,ranges}::find_end(it1, it1, it2, it2)
-    bm.operator()<std::vector<int>>("std::find_end(vector<int>) (bail 25%)", std_find_end);
-    bm.operator()<std::deque<int>>("std::find_end(deque<int>) (bail 25%)", std_find_end);
-    bm.operator()<std::list<int>>("std::find_end(list<int>) (bail 25%)", std_find_end);
-    bm.operator()<std::forward_list<int>>("std::find_end(forward_list<int>) (bail 25%)", std_find_end);
-    bm.operator()<std::vector<int>>("rng::find_end(vector<int>) (bail 25%)", std::ranges::find_end);
-    bm.operator()<std::deque<int>>("rng::find_end(deque<int>) (bail 25%)", std::ranges::find_end);
-    bm.operator()<std::list<int>>("rng::find_end(list<int>) (bail 25%)", std::ranges::find_end);
-    bm.operator()<std::forward_list<int>>("rng::find_end(forward_list<int>) (bail 25%)", std::ranges::find_end);
-
-    // {std,ranges}::find_end(it1, it1, it2, it2, pred)
-    bm.operator()<std::vector<int>>("std::find_end(vector<int>, pred) (bail 25%)", std_find_end_pred);
-    bm.operator()<std::deque<int>>("std::find_end(deque<int>, pred) (bail 25%)", std_find_end_pred);
-    bm.operator()<std::list<int>>("std::find_end(list<int>, pred) (bail 25%)", std_find_end_pred);
-    bm.operator()<std::forward_list<int>>("std::find_end(forward_list<int>, pred) (bail 25%)", std_find_end_pred);
-    bm.operator()<std::vector<int>>("rng::find_end(vector<int>, pred) (bail 25%)", ranges_find_end_pred);
-    bm.operator()<std::deque<int>>("rng::find_end(deque<int>, pred) (bail 25%)", ranges_find_end_pred);
-    bm.operator()<std::list<int>>("rng::find_end(list<int>, pred) (bail 25%)", ranges_find_end_pred);
-    bm.operator()<std::forward_list<int>>("rng::find_end(forward_list<int>, pred) (bail 25%)", ranges_find_end_pred);
+    register_benchmarks(bm, "process all");
   }
 
-  // Benchmark {std,ranges}::find_end where the subsequence is found
-  // 90% into the sequence (i.e. near the end)
+  // Benchmark {std,ranges}::find_end where we intersperse "near matches" inside the haystack.
   {
     auto bm = []<class Container>(std::string name, auto find_end) {
       benchmark::RegisterBenchmark(
@@ -99,43 +106,117 @@ int main(int argc, char** argv) {
             using ValueType        = typename Container::value_type;
             ValueType x            = Generate<ValueType>::random();
             ValueType y            = random_different_from({x});
-            Container c(size, x);
-            Container subrange(size / 10, y); // subrange of length 10% of the full range
+            Container haystack(size, x);
+            std::size_t n = size / 10; // needle size is 10% of the haystack
+            assert(n > 0);
+            Container needle(n, y);
 
-            // put the element we're searching for at 90% of the sequence
-            std::ranges::copy(subrange, std::next(c.begin(), (9 * size) / 10));
+            // intersperse near-matches inside the haystack
+            {
+              auto first = haystack.begin();
+              for (int i = 0; i != 10; ++i) {
+                first = std::copy_n(needle.begin(), n - 1, first);
+                ++first; // this causes the subsequence not to match because it has length n-1
+              }
+            }
 
             for ([[maybe_unused]] auto _ : st) {
-              benchmark::DoNotOptimize(c);
-              benchmark::DoNotOptimize(subrange);
-              auto result = find_end(c.begin(), c.end(), subrange.begin(), subrange.end());
+              benchmark::DoNotOptimize(haystack);
+              benchmark::DoNotOptimize(needle);
+              auto result = find_end(haystack.begin(), haystack.end(), needle.begin(), needle.end());
               benchmark::DoNotOptimize(result);
             }
           })
           ->Arg(1000) // non power-of-two
           ->Arg(1024)
-          ->Arg(8192)
-          ->Arg(1 << 20);
+          ->Arg(8192);
     };
-    // {std,ranges}::find_end(it1, it1, it2, it2)
-    bm.operator()<std::vector<int>>("std::find_end(vector<int>) (bail 90%)", std_find_end);
-    bm.operator()<std::deque<int>>("std::find_end(deque<int>) (bail 90%)", std_find_end);
-    bm.operator()<std::list<int>>("std::find_end(list<int>) (bail 90%)", std_find_end);
-    bm.operator()<std::forward_list<int>>("std::find_end(forward_list<int>) (bail 90%)", std_find_end);
-    bm.operator()<std::vector<int>>("rng::find_end(vector<int>) (bail 90%)", std::ranges::find_end);
-    bm.operator()<std::deque<int>>("rng::find_end(deque<int>) (bail 90%)", std::ranges::find_end);
-    bm.operator()<std::list<int>>("rng::find_end(list<int>) (bail 90%)", std::ranges::find_end);
-    bm.operator()<std::forward_list<int>>("rng::find_end(forward_list<int>) (bail 90%)", std::ranges::find_end);
+    register_benchmarks(bm, "near matches");
+  }
 
-    // {std,ranges}::find_end(it1, it1, it2, it2, pred)
-    bm.operator()<std::vector<int>>("std::find_end(vector<int>, pred) (bail 90%)", std_find_end_pred);
-    bm.operator()<std::deque<int>>("std::find_end(deque<int>, pred) (bail 90%)", std_find_end_pred);
-    bm.operator()<std::list<int>>("std::find_end(list<int>, pred) (bail 90%)", std_find_end_pred);
-    bm.operator()<std::forward_list<int>>("std::find_end(forward_list<int>, pred) (bail 90%)", std_find_end_pred);
-    bm.operator()<std::vector<int>>("rng::find_end(vector<int>, pred) (bail 90%)", ranges_find_end_pred);
-    bm.operator()<std::deque<int>>("rng::find_end(deque<int>, pred) (bail 90%)", ranges_find_end_pred);
-    bm.operator()<std::list<int>>("rng::find_end(list<int>, pred) (bail 90%)", ranges_find_end_pred);
-    bm.operator()<std::forward_list<int>>("rng::find_end(forward_list<int>, pred) (bail 90%)", ranges_find_end_pred);
+  // Special case: the two ranges are the same length (and they are equal, which is the worst case).
+  {
+    auto bm = []<class Container>(std::string name, auto find_end) {
+      benchmark::RegisterBenchmark(
+          name,
+          [find_end](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            ValueType x            = Generate<ValueType>::random();
+            Container haystack(size, x);
+            Container needle(size, x);
+
+            for (auto _ : st) {
+              benchmark::DoNotOptimize(haystack);
+              benchmark::DoNotOptimize(needle);
+              auto result = find_end(haystack.begin(), haystack.end(), needle.begin(), needle.end());
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(1000) // non power-of-two
+          ->Arg(1024)
+          ->Arg(8192);
+    };
+    register_benchmarks(bm, "same length");
+  }
+
+  // Special case: the needle contains a single element (which we never find, i.e. the worst case).
+  {
+    auto bm = []<class Container>(std::string name, auto find_end) {
+      benchmark::RegisterBenchmark(
+          name,
+          [find_end](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            ValueType x            = Generate<ValueType>::random();
+            ValueType y            = random_different_from({x});
+            Container haystack(size, x);
+            Container needle(1, y);
+
+            for (auto _ : st) {
+              benchmark::DoNotOptimize(haystack);
+              benchmark::DoNotOptimize(needle);
+              auto result = find_end(haystack.begin(), haystack.end(), needle.begin(), needle.end());
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(1000) // non power-of-two
+          ->Arg(1024)
+          ->Arg(8192);
+    };
+    register_benchmarks(bm, "single element");
+  }
+
+  // Special case: we have a match close to the end of the haystack (ideal case if we start searching from the end).
+  {
+    auto bm = []<class Container>(std::string name, auto find_end) {
+      benchmark::RegisterBenchmark(
+          name,
+          [find_end](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            ValueType x            = Generate<ValueType>::random();
+            ValueType y            = random_different_from({x});
+            Container haystack(size, x);
+            std::size_t n = size / 10; // needle size is 10% of the haystack
+            assert(n > 0);
+            Container needle(n, y);
+
+            // put the needle at 90% of the haystack
+            std::ranges::copy(needle, std::next(haystack.begin(), (9 * size) / 10));
+
+            for (auto _ : st) {
+              benchmark::DoNotOptimize(haystack);
+              benchmark::DoNotOptimize(needle);
+              auto result = find_end(haystack.begin(), haystack.end(), needle.begin(), needle.end());
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(1000) // non power-of-two
+          ->Arg(1024)
+          ->Arg(8192);
+    };
+    register_benchmarks(bm, "match near end");
   }
 
   benchmark::Initialize(&argc, argv);

>From 86e9b0efb3999e80dcf14e9857453744c1f7e3e6 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 17 Mar 2025 15:14:32 -0400
Subject: [PATCH 17/20] Add UNSUPPORTED for starts_with

---
 .../benchmarks/algorithms/nonmodifying/starts_with.bench.cpp    | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/starts_with.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/starts_with.bench.cpp
index e141e0c48810c..2950ad5322e84 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/starts_with.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/starts_with.bench.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
 #include <algorithm>
 #include <cstddef>

>From a302cf81f9abc4a38d03b5c3967ffac599992437 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 17 Mar 2025 16:16:19 -0400
Subject: [PATCH 18/20] Review comments for std::search

---
 .../algorithms/nonmodifying/search.bench.cpp  | 85 ++++++++++++++++++-
 1 file changed, 83 insertions(+), 2 deletions(-)

diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp
index 9696c77aab649..a9d4a788d1b03 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp
@@ -38,7 +38,7 @@ int main(int argc, char** argv) {
     });
   };
 
-  // Benchmark {std,ranges}::search where the needle is never found
+  // Benchmark {std,ranges}::search where the needle is never found (worst case).
   {
     auto bm = []<class Container>(std::string name, auto search) {
       benchmark::RegisterBenchmark(
@@ -80,7 +80,7 @@ int main(int argc, char** argv) {
     bm.operator()<std::list<int>>("rng::search(list<int>, pred) (no match)", ranges_search_pred);
   }
 
-  // Benchmark {std,ranges}::search where we intersperse "near matches" inside the haystack
+  // Benchmark {std,ranges}::search where we intersperse "near matches" inside the haystack.
   {
     auto bm = []<class Container>(std::string name, auto search) {
       benchmark::RegisterBenchmark(
@@ -132,6 +132,87 @@ int main(int argc, char** argv) {
     bm.operator()<std::list<int>>("rng::search(list<int>, pred) (near matches)", ranges_search_pred);
   }
 
+  // Special case: the two ranges are the same length (and they are equal, which is the worst case).
+  {
+    auto bm = []<class Container>(std::string name, auto search) {
+      benchmark::RegisterBenchmark(
+          name,
+          [search](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            ValueType x            = Generate<ValueType>::random();
+            Container haystack(size, x);
+            Container needle(size, x);
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(haystack);
+              benchmark::DoNotOptimize(needle);
+              auto result = search(haystack.begin(), haystack.end(), needle.begin(), needle.end());
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(1000) // non power-of-two
+          ->Arg(1024)
+          ->Arg(8192);
+    };
+    // {std,ranges}::search
+    bm.operator()<std::vector<int>>("std::search(vector<int>) (same length)", std_search);
+    bm.operator()<std::deque<int>>("std::search(deque<int>) (same length)", std_search);
+    bm.operator()<std::list<int>>("std::search(list<int>) (same length)", std_search);
+    bm.operator()<std::vector<int>>("rng::search(vector<int>) (same length)", std::ranges::search);
+    bm.operator()<std::deque<int>>("rng::search(deque<int>) (same length)", std::ranges::search);
+    bm.operator()<std::list<int>>("rng::search(list<int>) (same length)", std::ranges::search);
+
+    // {std,ranges}::search(pred)
+    bm.operator()<std::vector<int>>("std::search(vector<int>, pred) (same length)", std_search_pred);
+    bm.operator()<std::deque<int>>("std::search(deque<int>, pred) (same length)", std_search_pred);
+    bm.operator()<std::list<int>>("std::search(list<int>, pred) (same length)", std_search_pred);
+    bm.operator()<std::vector<int>>("rng::search(vector<int>, pred) (same length)", ranges_search_pred);
+    bm.operator()<std::deque<int>>("rng::search(deque<int>, pred) (same length)", ranges_search_pred);
+    bm.operator()<std::list<int>>("rng::search(list<int>, pred) (same length)", ranges_search_pred);
+  }
+
+  // Special case: the needle contains a single element (which we never find, i.e. the worst case).
+  {
+    auto bm = []<class Container>(std::string name, auto search) {
+      benchmark::RegisterBenchmark(
+          name,
+          [search](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            ValueType x            = Generate<ValueType>::random();
+            ValueType y            = random_different_from({x});
+            Container haystack(size, x);
+            Container needle(1, y);
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(haystack);
+              benchmark::DoNotOptimize(needle);
+              auto result = search(haystack.begin(), haystack.end(), needle.begin(), needle.end());
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(1000) // non power-of-two
+          ->Arg(1024)
+          ->Arg(8192);
+    };
+    // {std,ranges}::search
+    bm.operator()<std::vector<int>>("std::search(vector<int>) (single element)", std_search);
+    bm.operator()<std::deque<int>>("std::search(deque<int>) (single element)", std_search);
+    bm.operator()<std::list<int>>("std::search(list<int>) (single element)", std_search);
+    bm.operator()<std::vector<int>>("rng::search(vector<int>) (single element)", std::ranges::search);
+    bm.operator()<std::deque<int>>("rng::search(deque<int>) (single element)", std::ranges::search);
+    bm.operator()<std::list<int>>("rng::search(list<int>) (single element)", std::ranges::search);
+
+    // {std,ranges}::search(pred)
+    bm.operator()<std::vector<int>>("std::search(vector<int>, pred) (single element)", std_search_pred);
+    bm.operator()<std::deque<int>>("std::search(deque<int>, pred) (single element)", std_search_pred);
+    bm.operator()<std::list<int>>("std::search(list<int>, pred) (single element)", std_search_pred);
+    bm.operator()<std::vector<int>>("rng::search(vector<int>, pred) (single element)", ranges_search_pred);
+    bm.operator()<std::deque<int>>("rng::search(deque<int>, pred) (single element)", ranges_search_pred);
+    bm.operator()<std::list<int>>("rng::search(list<int>, pred) (single element)", ranges_search_pred);
+  }
+
   benchmark::Initialize(&argc, argv);
   benchmark::RunSpecifiedBenchmarks();
   benchmark::Shutdown();

>From e8c40d47cfe5184d634a0e7550486d423ae95d4a Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 17 Mar 2025 22:27:39 -0400
Subject: [PATCH 19/20] Tweak find_first_of per comments

---
 .../nonmodifying/find_first_of.bench.cpp      | 89 +++++++------------
 1 file changed, 31 insertions(+), 58 deletions(-)

diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp
index 9a8818038fe16..fca1aaf233988 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp
@@ -38,9 +38,8 @@ int main(int argc, char** argv) {
     });
   };
 
-  // Benchmark {std,ranges}::find_first_of where we have a hit at 10% of the haystack
-  // and at the end of the needle. This measures how quickly we're able to search inside
-  // the needle.
+  // Benchmark {std,ranges}::find_first_of where we never find a match in the needle, and the needle is small.
+  // This is the worst case of the most common case (a small needle).
   {
     auto bm = []<class Container>(std::string name, auto find_first_of) {
       benchmark::RegisterBenchmark(
@@ -50,13 +49,8 @@ int main(int argc, char** argv) {
             using ValueType        = typename Container::value_type;
             ValueType x            = Generate<ValueType>::random();
             ValueType y            = random_different_from({x});
-            ValueType z            = random_different_from({x, y});
             Container haystack(size, x);
-            Container needle(size, y);
-            needle.back() = z; // hit at the very end of the needle
-
-            // put the needle at 10% of the haystack
-            *std::next(haystack.begin(), haystack.size() / 10) = z;
+            Container needle(10, y);
 
             for ([[maybe_unused]] auto _ : st) {
               benchmark::DoNotOptimize(haystack);
@@ -71,33 +65,23 @@ int main(int argc, char** argv) {
           ->Arg(8192);
     };
     // {std,ranges}::find_first_of(it1, it1, it2, it2)
-    bm.operator()<std::vector<int>>("std::find_first_of(vector<int>) (10% haystack, late needle)", std_find_first_of);
-    bm.operator()<std::deque<int>>("std::find_first_of(deque<int>) (10% haystack, late needle)", std_find_first_of);
-    bm.operator()<std::list<int>>("std::find_first_of(list<int>) (10% haystack, late needle)", std_find_first_of);
-    bm.operator()<std::vector<int>>(
-        "rng::find_first_of(vector<int>) (10% haystack, late needle)", std::ranges::find_first_of);
-    bm.operator()<std::deque<int>>(
-        "rng::find_first_of(deque<int>) (10% haystack, late needle)", std::ranges::find_first_of);
-    bm.operator()<std::list<int>>(
-        "rng::find_first_of(list<int>) (10% haystack, late needle)", std::ranges::find_first_of);
+    bm.operator()<std::vector<int>>("std::find_first_of(vector<int>) (small needle)", std_find_first_of);
+    bm.operator()<std::deque<int>>("std::find_first_of(deque<int>) (small needle)", std_find_first_of);
+    bm.operator()<std::list<int>>("std::find_first_of(list<int>) (small needle)", std_find_first_of);
+    bm.operator()<std::vector<int>>("rng::find_first_of(vector<int>) (small needle)", std::ranges::find_first_of);
+    bm.operator()<std::deque<int>>("rng::find_first_of(deque<int>) (small needle)", std::ranges::find_first_of);
+    bm.operator()<std::list<int>>("rng::find_first_of(list<int>) (small needle)", std::ranges::find_first_of);
 
     // {std,ranges}::find_first_of(it1, it1, it2, it2, pred)
-    bm.operator()<std::vector<int>>(
-        "std::find_first_of(vector<int>, pred) (25% haystack, late needle)", std_find_first_of);
-    bm.operator()<std::deque<int>>(
-        "std::find_first_of(deque<int>, pred) (25% haystack, late needle)", std_find_first_of);
-    bm.operator()<std::list<int>>("std::find_first_of(list<int>, pred) (25% haystack, late needle)", std_find_first_of);
-    bm.operator()<std::vector<int>>(
-        "rng::find_first_of(vector<int>, pred) (25% haystack, late needle)", std::ranges::find_first_of);
-    bm.operator()<std::deque<int>>(
-        "rng::find_first_of(deque<int>, pred) (25% haystack, late needle)", std::ranges::find_first_of);
-    bm.operator()<std::list<int>>(
-        "rng::find_first_of(list<int>, pred) (25% haystack, late needle)", std::ranges::find_first_of);
+    bm.operator()<std::vector<int>>("std::find_first_of(vector<int>, pred) (small needle)", std_find_first_of_pred);
+    bm.operator()<std::deque<int>>("std::find_first_of(deque<int>, pred) (small needle)", std_find_first_of_pred);
+    bm.operator()<std::list<int>>("std::find_first_of(list<int>, pred) (small needle)", std_find_first_of_pred);
+    bm.operator()<std::vector<int>>("rng::find_first_of(vector<int>, pred) (small needle)", ranges_find_first_of_pred);
+    bm.operator()<std::deque<int>>("rng::find_first_of(deque<int>, pred) (small needle)", ranges_find_first_of_pred);
+    bm.operator()<std::list<int>>("rng::find_first_of(list<int>, pred) (small needle)", ranges_find_first_of_pred);
   }
 
-  // Benchmark {std,ranges}::find_first_of where we have a hit at 90% of the haystack
-  // but at the beginning of the needle. This measures how quickly we're able to search
-  // inside the haystack.
+  // Special case: the needle is large compared to the haystack, and we find a match early in the haystack.
   {
     auto bm = []<class Container>(std::string name, auto find_first_of) {
       benchmark::RegisterBenchmark(
@@ -107,13 +91,11 @@ int main(int argc, char** argv) {
             using ValueType        = typename Container::value_type;
             ValueType x            = Generate<ValueType>::random();
             ValueType y            = random_different_from({x});
-            ValueType z            = random_different_from({x, y});
             Container haystack(size, x);
-            Container needle(size, y);
-            *std::next(needle.begin(), needle.size() / 10) = z; // hit at 10% of the needle
+            Container needle(size * 10, y);
 
-            // put the needle at 90% of the haystack
-            *std::next(haystack.begin(), (9 * haystack.size()) / 10) = z;
+            // put a match at 10% of the haystack
+            *std::next(haystack.begin(), haystack.size() / 10) = y;
 
             for ([[maybe_unused]] auto _ : st) {
               benchmark::DoNotOptimize(haystack);
@@ -128,29 +110,20 @@ int main(int argc, char** argv) {
           ->Arg(8192);
     };
     // {std,ranges}::find_first_of(it1, it1, it2, it2)
-    bm.operator()<std::vector<int>>("std::find_first_of(vector<int>) (90% haystack, early needle)", std_find_first_of);
-    bm.operator()<std::deque<int>>("std::find_first_of(deque<int>) (90% haystack, early needle)", std_find_first_of);
-    bm.operator()<std::list<int>>("std::find_first_of(list<int>) (90% haystack, early needle)", std_find_first_of);
-    bm.operator()<std::vector<int>>(
-        "rng::find_first_of(vector<int>) (90% haystack, early needle)", std::ranges::find_first_of);
-    bm.operator()<std::deque<int>>(
-        "rng::find_first_of(deque<int>) (90% haystack, early needle)", std::ranges::find_first_of);
-    bm.operator()<std::list<int>>(
-        "rng::find_first_of(list<int>) (90% haystack, early needle)", std::ranges::find_first_of);
+    bm.operator()<std::vector<int>>("std::find_first_of(vector<int>) (large needle)", std_find_first_of);
+    bm.operator()<std::deque<int>>("std::find_first_of(deque<int>) (large needle)", std_find_first_of);
+    bm.operator()<std::list<int>>("std::find_first_of(list<int>) (large needle)", std_find_first_of);
+    bm.operator()<std::vector<int>>("rng::find_first_of(vector<int>) (large needle)", std::ranges::find_first_of);
+    bm.operator()<std::deque<int>>("rng::find_first_of(deque<int>) (large needle)", std::ranges::find_first_of);
+    bm.operator()<std::list<int>>("rng::find_first_of(list<int>) (large needle)", std::ranges::find_first_of);
 
     // {std,ranges}::find_first_of(it1, it1, it2, it2, pred)
-    bm.operator()<std::vector<int>>(
-        "std::find_first_of(vector<int>, pred) (90% haystack, early needle)", std_find_first_of_pred);
-    bm.operator()<std::deque<int>>(
-        "std::find_first_of(deque<int>, pred) (90% haystack, early needle)", std_find_first_of_pred);
-    bm.operator()<std::list<int>>(
-        "std::find_first_of(list<int>, pred) (90% haystack, early needle)", std_find_first_of_pred);
-    bm.operator()<std::vector<int>>(
-        "rng::find_first_of(vector<int>, pred) (90% haystack, early needle)", ranges_find_first_of_pred);
-    bm.operator()<std::deque<int>>(
-        "rng::find_first_of(deque<int>, pred) (90% haystack, early needle)", ranges_find_first_of_pred);
-    bm.operator()<std::list<int>>(
-        "rng::find_first_of(list<int>, pred) (90% haystack, early needle)", ranges_find_first_of_pred);
+    bm.operator()<std::vector<int>>("std::find_first_of(vector<int>, pred) (large needle)", std_find_first_of_pred);
+    bm.operator()<std::deque<int>>("std::find_first_of(deque<int>, pred) (large needle)", std_find_first_of_pred);
+    bm.operator()<std::list<int>>("std::find_first_of(list<int>, pred) (large needle)", std_find_first_of_pred);
+    bm.operator()<std::vector<int>>("rng::find_first_of(vector<int>, pred) (large needle)", ranges_find_first_of_pred);
+    bm.operator()<std::deque<int>>("rng::find_first_of(deque<int>, pred) (large needle)", ranges_find_first_of_pred);
+    bm.operator()<std::list<int>>("rng::find_first_of(list<int>, pred) (large needle)", ranges_find_first_of_pred);
   }
 
   benchmark::Initialize(&argc, argv);

>From 6502e381c49dc660c9d425b937655aa9283ace93 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 18 Mar 2025 01:01:24 -0400
Subject: [PATCH 20/20] Fix unsupported

---
 .../test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp | 2 +-
 .../test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp | 2 +-
 libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp   | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp
index f572773b10814..0c01833453156 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
 #include <algorithm>
 #include <cassert>
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp
index f0a48a830c2c6..17074250c6c72 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
 #include <algorithm>
 #include <cstddef>
diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp
index 379dec046b42f..3da151d4c1b12 100644
--- a/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
 
 #include <algorithm>
 #include <cstddef>



More information about the libcxx-commits mailing list