[libcxx-commits] [libcxx] [libc++] Add remaining benchmarks from [alg.modifying.operations] (PR #127354)

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Sat Feb 15 15:42:27 PST 2025


https://github.com/ldionne created https://github.com/llvm/llvm-project/pull/127354

This patch adds benchmarks for all the remaining algorithms in [alg.modifying.operations] that we didn't already have a benchmark for.

>From 7d00280453938054323df90a13177483819f8088 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Sat, 15 Feb 2025 21:36:38 +0100
Subject: [PATCH] [libc++] Add remaining benchmarks from
 [alg.modifying.operations]

This patch adds benchmarks for all the remaining algorithms in
[alg.modifying.operations] that we didn't already have a benchmark
for.
---
 libcxx/include/module.modulemap               |   3 +-
 .../test/benchmarks/algorithms/fill.bench.cpp |  51 -------
 .../algorithms/modifying/fill.bench.cpp       |  86 +++++++++++
 .../algorithms/modifying/fill_n.bench.cpp     |  86 +++++++++++
 .../algorithms/modifying/generate.bench.cpp   |  59 +++++++
 .../algorithms/modifying/generate_n.bench.cpp |  59 +++++++
 .../algorithms/modifying/remove.bench.cpp     | 133 ++++++++++++++++
 .../modifying/remove_copy.bench.cpp           | 109 +++++++++++++
 .../modifying/remove_copy_if.bench.cpp        | 119 +++++++++++++++
 .../algorithms/modifying/remove_if.bench.cpp  | 143 +++++++++++++++++
 .../algorithms/modifying/replace.bench.cpp    | 107 +++++++++++++
 .../algorithms/modifying/replace_if.bench.cpp | 117 ++++++++++++++
 .../algorithms/modifying/reverse.bench.cpp    |  57 +++++++
 .../modifying/reverse_copy.bench.cpp          |  61 ++++++++
 .../algorithms/modifying/rotate.bench.cpp     |  59 +++++++
 .../modifying/rotate_copy.bench.cpp           |  65 ++++++++
 .../algorithms/modifying/sample.bench.cpp     |  68 +++++++++
 .../algorithms/modifying/shift_left.bench.cpp |  56 +++++++
 .../modifying/shift_right.bench.cpp           |  56 +++++++
 .../algorithms/modifying/shuffle.bench.cpp    |  56 +++++++
 .../modifying/swap_ranges.bench.cpp           |  64 ++++++++
 .../modifying/transform.binary.bench.cpp      |  73 +++++++++
 .../modifying/transform.unary.bench.cpp       |  68 +++++++++
 .../algorithms/modifying/unique.bench.cpp     | 132 ++++++++++++++++
 .../modifying/unique_copy.bench.cpp           | 105 +++++++++++++
 .../modifying/unique_copy_pred.bench.cpp      | 121 +++++++++++++++
 .../modifying/unique_pred.bench.cpp           | 144 ++++++++++++++++++
 .../benchmarks/algorithms/reverse.bench.cpp   |  48 ------
 28 files changed, 2205 insertions(+), 100 deletions(-)
 delete mode 100644 libcxx/test/benchmarks/algorithms/fill.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/modifying/fill.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/modifying/fill_n.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/modifying/generate.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/modifying/generate_n.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/modifying/remove.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/modifying/remove_copy.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/modifying/remove_copy_if.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/modifying/remove_if.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/modifying/replace.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/modifying/replace_if.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/modifying/reverse.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/modifying/reverse_copy.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/modifying/rotate.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/modifying/rotate_copy.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/modifying/sample.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/modifying/shift_left.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/modifying/shift_right.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/modifying/shuffle.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/modifying/swap_ranges.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/modifying/transform.binary.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/modifying/transform.unary.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/modifying/unique.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/modifying/unique_copy.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/modifying/unique_copy_pred.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/modifying/unique_pred.bench.cpp
 delete mode 100644 libcxx/test/benchmarks/algorithms/reverse.bench.cpp

diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index b0720703bd0de..6af6e96af7d75 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -700,6 +700,7 @@ module std [system] {
     }
     module ranges_remove {
       header "__algorithm/ranges_remove.h"
+      export std.ranges.subrange // return type
     }
     module ranges_replace_copy_if {
       header "__algorithm/ranges_replace_copy_if.h"
@@ -724,7 +725,7 @@ module std [system] {
     }
     module ranges_rotate_copy {
       header "__algorithm/ranges_rotate_copy.h"
-      export std.algorithm.in_out_result
+      export std.ranges.subrange // return type
     }
     module ranges_rotate                          { header "__algorithm/ranges_rotate.h" }
     module ranges_sample                          { header "__algorithm/ranges_sample.h" }
diff --git a/libcxx/test/benchmarks/algorithms/fill.bench.cpp b/libcxx/test/benchmarks/algorithms/fill.bench.cpp
deleted file mode 100644
index 6a48b25b7eb63..0000000000000
--- a/libcxx/test/benchmarks/algorithms/fill.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 <vector>
-
-static void bm_fill_n_vector_bool(benchmark::State& state) {
-  std::vector<bool> vec1(state.range());
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(vec1);
-    benchmark::DoNotOptimize(std::fill_n(vec1.begin(), vec1.size(), false));
-  }
-}
-BENCHMARK(bm_fill_n_vector_bool)->DenseRange(1, 8)->Range(16, 1 << 20);
-
-static void bm_ranges_fill_n_vector_bool(benchmark::State& state) {
-  std::vector<bool> vec1(state.range());
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(vec1);
-    benchmark::DoNotOptimize(std::ranges::fill_n(vec1.begin(), vec1.size(), false));
-  }
-}
-BENCHMARK(bm_ranges_fill_n_vector_bool)->DenseRange(1, 8)->Range(16, 1 << 20);
-
-static void bm_fill_vector_bool(benchmark::State& state) {
-  std::vector<bool> vec1(state.range());
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(vec1);
-    std::fill(vec1.begin(), vec1.end(), false);
-  }
-}
-BENCHMARK(bm_fill_vector_bool)->DenseRange(1, 8)->Range(16, 1 << 20);
-
-static void bm_ranges_fill_vector_bool(benchmark::State& state) {
-  std::vector<bool> vec1(state.range());
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(vec1);
-    benchmark::DoNotOptimize(std::ranges::fill(vec1, false));
-  }
-}
-BENCHMARK(bm_ranges_fill_vector_bool)->DenseRange(1, 8)->Range(16, 1 << 20);
-
-BENCHMARK_MAIN();
diff --git a/libcxx/test/benchmarks/algorithms/modifying/fill.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/fill.bench.cpp
new file mode 100644
index 0000000000000..64c7364be6549
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/modifying/fill.bench.cpp
@@ -0,0 +1,86 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+#include "test_macros.h"
+
+template <class Container, class Operation>
+void bm(std::string operation_name, Operation fill) {
+  auto bench = [fill](auto& st) {
+    std::size_t const size = st.range(0);
+    using ValueType        = typename Container::value_type;
+    ValueType x            = Generate<ValueType>::random();
+    ValueType y            = Generate<ValueType>::random();
+    Container c(size, y);
+
+    for ([[maybe_unused]] auto _ : st) {
+      fill(c.begin(), c.end(), x);
+      std::swap(x, y);
+      benchmark::DoNotOptimize(c);
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Arg(32)->Arg(1024)->Arg(8192);
+}
+
+template <class Operation>
+void bm_vector_bool(std::string operation_name, Operation fill) {
+  auto bench = [fill](auto& st) {
+    std::size_t const size = st.range(0);
+    bool x                 = true;
+    bool y                 = false;
+    std::vector<bool> c(size, y);
+
+    for ([[maybe_unused]] auto _ : st) {
+      fill(c.begin(), c.end(), x);
+      std::swap(x, y);
+      benchmark::DoNotOptimize(c);
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Arg(32)->Arg(1024)->Arg(8192);
+}
+
+int main(int argc, char** argv) {
+  auto std_fill    = [](auto first, auto last, auto const& value) { return std::fill(first, last, value); };
+  auto ranges_fill = [](auto first, auto last, auto const& value) { return std::ranges::fill(first, last, value); };
+
+  // std::fill
+  bm<std::vector<int>>("std::fill(vector<int>)", std_fill);
+  bm<std::deque<int>>("std::fill(deque<int>)", std_fill);
+  bm<std::list<int>>("std::fill(list<int>)", std_fill);
+  bm_vector_bool("std::fill(vector<bool>)", std_fill);
+
+  // ranges::fill
+  bm<std::vector<int>>("ranges::fill(vector<int>)", ranges_fill);
+  bm<std::deque<int>>("ranges::fill(deque<int>)", ranges_fill);
+  bm<std::list<int>>("ranges::fill(list<int>)", ranges_fill);
+#if TEST_STD_VER >= 23 // vector<bool>::iterator is not an output_iterator before C++23
+  bm_vector_bool("ranges::fill(vector<bool>)", ranges_fill);
+#endif
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/modifying/fill_n.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/fill_n.bench.cpp
new file mode 100644
index 0000000000000..aaeb0982956f9
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/modifying/fill_n.bench.cpp
@@ -0,0 +1,86 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+#include "test_macros.h"
+
+template <class Container, class Operation>
+void bm(std::string operation_name, Operation fill_n) {
+  auto bench = [fill_n](auto& st) {
+    std::size_t const size = st.range(0);
+    using ValueType        = typename Container::value_type;
+    ValueType x            = Generate<ValueType>::random();
+    ValueType y            = Generate<ValueType>::random();
+    Container c(size, y);
+
+    for ([[maybe_unused]] auto _ : st) {
+      fill_n(c.begin(), size, x);
+      std::swap(x, y);
+      benchmark::DoNotOptimize(c);
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Arg(32)->Arg(1024)->Arg(8192);
+}
+
+template <class Operation>
+void bm_vector_bool(std::string operation_name, Operation fill_n) {
+  auto bench = [fill_n](auto& st) {
+    std::size_t const size = st.range(0);
+    bool x                 = true;
+    bool y                 = false;
+    std::vector<bool> c(size, y);
+
+    for ([[maybe_unused]] auto _ : st) {
+      fill_n(c.begin(), size, x);
+      std::swap(x, y);
+      benchmark::DoNotOptimize(c);
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Arg(32)->Arg(1024)->Arg(8192);
+}
+
+int main(int argc, char** argv) {
+  auto std_fill_n    = [](auto out, auto n, auto const& value) { return std::fill_n(out, n, value); };
+  auto ranges_fill_n = [](auto out, auto n, auto const& value) { return std::ranges::fill_n(out, n, value); };
+
+  // std::fill_n
+  bm<std::vector<int>>("std::fill_n(vector<int>)", std_fill_n);
+  bm<std::deque<int>>("std::fill_n(deque<int>)", std_fill_n);
+  bm<std::list<int>>("std::fill_n(list<int>)", std_fill_n);
+  bm_vector_bool("std::fill_n(vector<bool>)", std_fill_n);
+
+  // ranges::fill_n
+  bm<std::vector<int>>("ranges::fill_n(vector<int>)", ranges_fill_n);
+  bm<std::deque<int>>("ranges::fill_n(deque<int>)", ranges_fill_n);
+  bm<std::list<int>>("ranges::fill_n(list<int>)", ranges_fill_n);
+#if TEST_STD_VER >= 23 // vector<bool>::iterator is not an output_iterator before C++23
+  bm_vector_bool("ranges::fill_n(vector<bool>)", ranges_fill_n);
+#endif
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/modifying/generate.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/generate.bench.cpp
new file mode 100644
index 0000000000000..15b039a4d3009
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/modifying/generate.bench.cpp
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+template <class Container, class Operation>
+void bm(std::string operation_name, Operation generate) {
+  auto bench = [generate](auto& st) {
+    std::size_t const size = st.range(0);
+    Container c(size);
+    using ValueType = typename Container::value_type;
+    ValueType x     = Generate<ValueType>::random();
+
+    for ([[maybe_unused]] auto _ : st) {
+      auto f = [&x] { return x; };
+      generate(c.begin(), c.end(), f);
+      benchmark::DoNotOptimize(c);
+      benchmark::DoNotOptimize(x);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Arg(32)->Arg(1024)->Arg(8192);
+}
+
+int main(int argc, char** argv) {
+  auto std_generate    = [](auto first, auto last, auto f) { return std::generate(first, last, f); };
+  auto ranges_generate = [](auto first, auto last, auto f) { return std::ranges::generate(first, last, f); };
+
+  // std::generate
+  bm<std::vector<int>>("std::generate(vector<int>)", std_generate);
+  bm<std::deque<int>>("std::generate(deque<int>)", std_generate);
+  bm<std::list<int>>("std::generate(list<int>)", std_generate);
+
+  // ranges::generate
+  bm<std::vector<int>>("ranges::generate(vector<int>)", ranges_generate);
+  bm<std::deque<int>>("ranges::generate(deque<int>)", ranges_generate);
+  bm<std::list<int>>("ranges::generate(list<int>)", ranges_generate);
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/modifying/generate_n.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/generate_n.bench.cpp
new file mode 100644
index 0000000000000..75b088411810f
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/modifying/generate_n.bench.cpp
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+template <class Container, class Operation>
+void bm(std::string operation_name, Operation generate_n) {
+  auto bench = [generate_n](auto& st) {
+    std::size_t const size = st.range(0);
+    Container c(size);
+    using ValueType = typename Container::value_type;
+    ValueType x     = Generate<ValueType>::random();
+
+    for ([[maybe_unused]] auto _ : st) {
+      auto f = [&x] { return x; };
+      generate_n(c.begin(), size, f);
+      benchmark::DoNotOptimize(c);
+      benchmark::DoNotOptimize(x);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Arg(32)->Arg(1024)->Arg(8192);
+}
+
+int main(int argc, char** argv) {
+  auto std_generate_n    = [](auto out, auto n, auto f) { return std::generate_n(out, n, f); };
+  auto ranges_generate_n = [](auto out, auto n, auto f) { return std::ranges::generate_n(out, n, f); };
+
+  // std::generate_n
+  bm<std::vector<int>>("std::generate_n(vector<int>)", std_generate_n);
+  bm<std::deque<int>>("std::generate_n(deque<int>)", std_generate_n);
+  bm<std::list<int>>("std::generate_n(list<int>)", std_generate_n);
+
+  // ranges::generate_n
+  bm<std::vector<int>>("ranges::generate_n(vector<int>)", ranges_generate_n);
+  bm<std::deque<int>>("ranges::generate_n(deque<int>)", ranges_generate_n);
+  bm<std::list<int>>("ranges::generate_n(list<int>)", ranges_generate_n);
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/modifying/remove.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/remove.bench.cpp
new file mode 100644
index 0000000000000..764dfe73ab70c
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/modifying/remove.bench.cpp
@@ -0,0 +1,133 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+// Create a sequence of the form xxxxxxxxxxyyyyyyyyyy and remove
+// the prefix of x's from it.
+//
+// We perform this benchmark in a batch because we need to restore the
+// state of the container after the operation.
+template <class Container, class Operation>
+void bm_prefix(std::string operation_name, Operation remove) {
+  auto bench = [remove](auto& st) {
+    std::size_t const size          = st.range(0);
+    constexpr std::size_t BatchSize = 10;
+    using ValueType                 = typename Container::value_type;
+    Container c[BatchSize];
+    ValueType x = Generate<ValueType>::random();
+    ValueType y = Generate<ValueType>::random();
+    for (std::size_t i = 0; i != BatchSize; ++i) {
+      c[i]      = Container(size);
+      auto half = size / 2;
+      std::fill_n(std::fill_n(c[i].begin(), half, x), half, y);
+    }
+
+    while (st.KeepRunningBatch(BatchSize)) {
+      for (std::size_t i = 0; i != BatchSize; ++i) {
+        auto result = remove(c[i].begin(), c[i].end(), x);
+        benchmark::DoNotOptimize(result);
+        benchmark::DoNotOptimize(c[i]);
+        benchmark::DoNotOptimize(x);
+        benchmark::ClobberMemory();
+      }
+
+      st.PauseTiming();
+      for (std::size_t i = 0; i != BatchSize; ++i) {
+        auto half = size / 2;
+        std::fill_n(std::fill_n(c[i].begin(), half, x), half, y);
+      }
+      st.ResumeTiming();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Arg(32)->Arg(1024)->Arg(8192);
+}
+
+// Create a sequence of the form xyxyxyxyxyxyxyxyxyxy and remove
+// the x's from it.
+//
+// We perform this benchmark in a batch because we need to restore the
+// state of the container after the operation.
+template <class Container, class Operation>
+void bm_sprinkled(std::string operation_name, Operation remove) {
+  auto bench = [remove](auto& st) {
+    std::size_t const size          = st.range(0);
+    constexpr std::size_t BatchSize = 10;
+    using ValueType                 = typename Container::value_type;
+    Container c[BatchSize];
+    ValueType x    = Generate<ValueType>::random();
+    ValueType y    = Generate<ValueType>::random();
+    auto alternate = [&](auto out, auto n) {
+      for (std::size_t i = 0; i != n; ++i) {
+        *out++ = (i % 2 == 0 ? x : y);
+      }
+    };
+    for (std::size_t i = 0; i != BatchSize; ++i) {
+      c[i] = Container(size);
+      alternate(c[i].begin(), size);
+    }
+
+    while (st.KeepRunningBatch(BatchSize)) {
+      for (std::size_t i = 0; i != BatchSize; ++i) {
+        auto result = remove(c[i].begin(), c[i].end(), x);
+        benchmark::DoNotOptimize(result);
+        benchmark::DoNotOptimize(c[i]);
+        benchmark::DoNotOptimize(x);
+        benchmark::ClobberMemory();
+      }
+
+      st.PauseTiming();
+      for (std::size_t i = 0; i != BatchSize; ++i) {
+        alternate(c[i].begin(), size);
+      }
+      st.ResumeTiming();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Arg(32)->Arg(1024)->Arg(8192);
+}
+
+int main(int argc, char** argv) {
+  auto std_remove    = [](auto first, auto last, auto const& value) { return std::remove(first, last, value); };
+  auto ranges_remove = [](auto first, auto last, auto const& value) { return std::ranges::remove(first, last, value); };
+
+  // std::remove
+  bm_prefix<std::vector<int>>("std::remove(vector<int>) (prefix)", std_remove);
+  bm_sprinkled<std::vector<int>>("std::remove(vector<int>) (sprinkled)", std_remove);
+
+  bm_prefix<std::deque<int>>("std::remove(deque<int>) (prefix)", std_remove);
+  bm_sprinkled<std::deque<int>>("std::remove(deque<int>) (sprinkled)", std_remove);
+
+  bm_prefix<std::list<int>>("std::remove(list<int>) (prefix)", std_remove);
+  bm_sprinkled<std::list<int>>("std::remove(list<int>) (sprinkled)", std_remove);
+
+  // ranges::remove
+  bm_prefix<std::vector<int>>("ranges::remove(vector<int>) (prefix)", ranges_remove);
+  bm_sprinkled<std::vector<int>>("ranges::remove(vector<int>) (sprinkled)", ranges_remove);
+
+  bm_prefix<std::deque<int>>("ranges::remove(deque<int>) (prefix)", ranges_remove);
+  bm_sprinkled<std::deque<int>>("ranges::remove(deque<int>) (sprinkled)", ranges_remove);
+
+  bm_prefix<std::list<int>>("ranges::remove(list<int>) (prefix)", ranges_remove);
+  bm_sprinkled<std::list<int>>("ranges::remove(list<int>) (sprinkled)", ranges_remove);
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/modifying/remove_copy.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/remove_copy.bench.cpp
new file mode 100644
index 0000000000000..88c05384eb7bd
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/modifying/remove_copy.bench.cpp
@@ -0,0 +1,109 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+// Create a sequence of the form xxxxxxxxxxyyyyyyyyyy and remove
+// the prefix of x's from it.
+template <class Container, class Operation>
+void bm_prefix(std::string operation_name, Operation remove_copy) {
+  auto bench = [remove_copy](auto& st) {
+    std::size_t const size = st.range(0);
+    using ValueType        = typename Container::value_type;
+    Container c;
+    ValueType x = Generate<ValueType>::random();
+    ValueType y = Generate<ValueType>::random();
+    std::fill_n(std::back_inserter(c), size / 2, x);
+    std::fill_n(std::back_inserter(c), size / 2, y);
+
+    std::vector<ValueType> out(size);
+
+    for ([[maybe_unused]] auto _ : st) {
+      auto result = remove_copy(c.begin(), c.end(), out.begin(), x);
+      benchmark::DoNotOptimize(result);
+      benchmark::DoNotOptimize(c);
+      benchmark::DoNotOptimize(out);
+      benchmark::DoNotOptimize(x);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Arg(32)->Arg(1024)->Arg(8192);
+}
+
+// Create a sequence of the form xyxyxyxyxyxyxyxyxyxy and remove
+// the x's from it.
+template <class Container, class Operation>
+void bm_sprinkled(std::string operation_name, Operation remove_copy) {
+  auto bench = [remove_copy](auto& st) {
+    std::size_t const size = st.range(0);
+    using ValueType        = typename Container::value_type;
+    Container c;
+    ValueType x = Generate<ValueType>::random();
+    ValueType y = Generate<ValueType>::random();
+    for (std::size_t i = 0; i != size; ++i) {
+      c.push_back(i % 2 == 0 ? x : y);
+    }
+
+    std::vector<ValueType> out(size);
+
+    for ([[maybe_unused]] auto _ : st) {
+      auto result = remove_copy(c.begin(), c.end(), out.begin(), x);
+      benchmark::DoNotOptimize(result);
+      benchmark::DoNotOptimize(c);
+      benchmark::DoNotOptimize(out);
+      benchmark::DoNotOptimize(x);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Arg(32)->Arg(1024)->Arg(8192);
+}
+
+int main(int argc, char** argv) {
+  auto std_remove_copy = [](auto first, auto last, auto out, auto const& value) {
+    return std::remove_copy(first, last, out, value);
+  };
+  auto ranges_remove_copy = [](auto first, auto last, auto out, auto const& value) {
+    return std::ranges::remove_copy(first, last, out, value);
+  };
+
+  // std::remove_copy
+  bm_prefix<std::vector<int>>("std::remove_copy(vector<int>) (prefix)", std_remove_copy);
+  bm_sprinkled<std::vector<int>>("std::remove_copy(vector<int>) (sprinkled)", std_remove_copy);
+
+  bm_prefix<std::deque<int>>("std::remove_copy(deque<int>) (prefix)", std_remove_copy);
+  bm_sprinkled<std::deque<int>>("std::remove_copy(deque<int>) (sprinkled)", std_remove_copy);
+
+  bm_prefix<std::list<int>>("std::remove_copy(list<int>) (prefix)", std_remove_copy);
+  bm_sprinkled<std::list<int>>("std::remove_copy(list<int>) (sprinkled)", std_remove_copy);
+
+  // ranges::remove_copy
+  bm_prefix<std::vector<int>>("ranges::remove_copy(vector<int>) (prefix)", ranges_remove_copy);
+  bm_sprinkled<std::vector<int>>("ranges::remove_copy(vector<int>) (sprinkled)", ranges_remove_copy);
+
+  bm_prefix<std::deque<int>>("ranges::remove_copy(deque<int>) (prefix)", ranges_remove_copy);
+  bm_sprinkled<std::deque<int>>("ranges::remove_copy(deque<int>) (sprinkled)", ranges_remove_copy);
+
+  bm_prefix<std::list<int>>("ranges::remove_copy(list<int>) (prefix)", ranges_remove_copy);
+  bm_sprinkled<std::list<int>>("ranges::remove_copy(list<int>) (sprinkled)", ranges_remove_copy);
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/modifying/remove_copy_if.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/remove_copy_if.bench.cpp
new file mode 100644
index 0000000000000..95b0e93362bab
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/modifying/remove_copy_if.bench.cpp
@@ -0,0 +1,119 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+// Create a sequence of the form xxxxxxxxxxyyyyyyyyyy and remove
+// the prefix of x's from it.
+template <class Container, class Operation>
+void bm_prefix(std::string operation_name, Operation remove_copy_if) {
+  auto bench = [remove_copy_if](auto& st) {
+    std::size_t const size = st.range(0);
+    using ValueType        = typename Container::value_type;
+    Container c;
+    ValueType x = Generate<ValueType>::random();
+    ValueType y = Generate<ValueType>::random();
+    std::fill_n(std::back_inserter(c), size / 2, x);
+    std::fill_n(std::back_inserter(c), size / 2, y);
+
+    auto pred = [&](auto& element) {
+      benchmark::DoNotOptimize(element);
+      return element == x;
+    };
+
+    std::vector<ValueType> out(size);
+
+    for ([[maybe_unused]] auto _ : st) {
+      auto result = remove_copy_if(c.begin(), c.end(), out.begin(), pred);
+      benchmark::DoNotOptimize(result);
+      benchmark::DoNotOptimize(c);
+      benchmark::DoNotOptimize(out);
+      benchmark::DoNotOptimize(x);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Arg(32)->Arg(1024)->Arg(8192);
+}
+
+// Create a sequence of the form xyxyxyxyxyxyxyxyxyxy and remove
+// the x's from it.
+template <class Container, class Operation>
+void bm_sprinkled(std::string operation_name, Operation remove_copy_if) {
+  auto bench = [remove_copy_if](auto& st) {
+    std::size_t const size = st.range(0);
+    using ValueType        = typename Container::value_type;
+    Container c;
+    ValueType x = Generate<ValueType>::random();
+    ValueType y = Generate<ValueType>::random();
+    for (std::size_t i = 0; i != size; ++i) {
+      c.push_back(i % 2 == 0 ? x : y);
+    }
+
+    auto pred = [&](auto& element) {
+      benchmark::DoNotOptimize(element);
+      return element == x;
+    };
+
+    std::vector<ValueType> out(size);
+
+    for ([[maybe_unused]] auto _ : st) {
+      auto result = remove_copy_if(c.begin(), c.end(), out.begin(), pred);
+      benchmark::DoNotOptimize(result);
+      benchmark::DoNotOptimize(c);
+      benchmark::DoNotOptimize(out);
+      benchmark::DoNotOptimize(x);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Arg(32)->Arg(1024)->Arg(8192);
+}
+
+int main(int argc, char** argv) {
+  auto std_remove_copy_if = [](auto first, auto last, auto out, auto pred) {
+    return std::remove_copy_if(first, last, out, pred);
+  };
+  auto ranges_remove_copy_if = [](auto first, auto last, auto out, auto pred) {
+    return std::ranges::remove_copy_if(first, last, out, pred);
+  };
+
+  // std::remove_copy_if
+  bm_prefix<std::vector<int>>("std::remove_copy_if(vector<int>) (prefix)", std_remove_copy_if);
+  bm_sprinkled<std::vector<int>>("std::remove_copy_if(vector<int>) (sprinkled)", std_remove_copy_if);
+
+  bm_prefix<std::deque<int>>("std::remove_copy_if(deque<int>) (prefix)", std_remove_copy_if);
+  bm_sprinkled<std::deque<int>>("std::remove_copy_if(deque<int>) (sprinkled)", std_remove_copy_if);
+
+  bm_prefix<std::list<int>>("std::remove_copy_if(list<int>) (prefix)", std_remove_copy_if);
+  bm_sprinkled<std::list<int>>("std::remove_copy_if(list<int>) (sprinkled)", std_remove_copy_if);
+
+  // ranges::remove_copy_if
+  bm_prefix<std::vector<int>>("ranges::remove_copy_if(vector<int>) (prefix)", ranges_remove_copy_if);
+  bm_sprinkled<std::vector<int>>("ranges::remove_copy_if(vector<int>) (sprinkled)", ranges_remove_copy_if);
+
+  bm_prefix<std::deque<int>>("ranges::remove_copy_if(deque<int>) (prefix)", ranges_remove_copy_if);
+  bm_sprinkled<std::deque<int>>("ranges::remove_copy_if(deque<int>) (sprinkled)", ranges_remove_copy_if);
+
+  bm_prefix<std::list<int>>("ranges::remove_copy_if(list<int>) (prefix)", ranges_remove_copy_if);
+  bm_sprinkled<std::list<int>>("ranges::remove_copy_if(list<int>) (sprinkled)", ranges_remove_copy_if);
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/modifying/remove_if.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/remove_if.bench.cpp
new file mode 100644
index 0000000000000..db0dabfbaa2c3
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/modifying/remove_if.bench.cpp
@@ -0,0 +1,143 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+// Create a sequence of the form xxxxxxxxxxyyyyyyyyyy and remove
+// the prefix of x's from it.
+//
+// We perform this benchmark in a batch because we need to restore the
+// state of the container after the operation.
+template <class Container, class Operation>
+void bm_prefix(std::string operation_name, Operation remove_if) {
+  auto bench = [remove_if](auto& st) {
+    std::size_t const size          = st.range(0);
+    constexpr std::size_t BatchSize = 10;
+    using ValueType                 = typename Container::value_type;
+    Container c[BatchSize];
+    ValueType x = Generate<ValueType>::random();
+    ValueType y = Generate<ValueType>::random();
+    for (std::size_t i = 0; i != BatchSize; ++i) {
+      c[i]      = Container(size);
+      auto half = size / 2;
+      std::fill_n(std::fill_n(c[i].begin(), half, x), half, y);
+    }
+
+    auto pred = [&](auto& element) {
+      benchmark::DoNotOptimize(element);
+      return element == x;
+    };
+
+    while (st.KeepRunningBatch(BatchSize)) {
+      for (std::size_t i = 0; i != BatchSize; ++i) {
+        auto result = remove_if(c[i].begin(), c[i].end(), pred);
+        benchmark::DoNotOptimize(result);
+        benchmark::DoNotOptimize(c[i]);
+        benchmark::DoNotOptimize(x);
+        benchmark::ClobberMemory();
+      }
+
+      st.PauseTiming();
+      for (std::size_t i = 0; i != BatchSize; ++i) {
+        auto half = size / 2;
+        std::fill_n(std::fill_n(c[i].begin(), half, x), half, y);
+      }
+      st.ResumeTiming();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Arg(32)->Arg(1024)->Arg(8192);
+}
+
+// Create a sequence of the form xyxyxyxyxyxyxyxyxyxy and remove
+// the x's from it.
+//
+// We perform this benchmark in a batch because we need to restore the
+// state of the container after the operation.
+template <class Container, class Operation>
+void bm_sprinkled(std::string operation_name, Operation remove_if) {
+  auto bench = [remove_if](auto& st) {
+    std::size_t const size          = st.range(0);
+    constexpr std::size_t BatchSize = 10;
+    using ValueType                 = typename Container::value_type;
+    Container c[BatchSize];
+    ValueType x    = Generate<ValueType>::random();
+    ValueType y    = Generate<ValueType>::random();
+    auto alternate = [&](auto out, auto n) {
+      for (std::size_t i = 0; i != n; ++i) {
+        *out++ = (i % 2 == 0 ? x : y);
+      }
+    };
+    for (std::size_t i = 0; i != BatchSize; ++i) {
+      c[i] = Container(size);
+      alternate(c[i].begin(), size);
+    }
+
+    auto pred = [&](auto& element) {
+      benchmark::DoNotOptimize(element);
+      return element == x;
+    };
+
+    while (st.KeepRunningBatch(BatchSize)) {
+      for (std::size_t i = 0; i != BatchSize; ++i) {
+        auto result = remove_if(c[i].begin(), c[i].end(), pred);
+        benchmark::DoNotOptimize(result);
+        benchmark::DoNotOptimize(c[i]);
+        benchmark::DoNotOptimize(x);
+        benchmark::ClobberMemory();
+      }
+
+      st.PauseTiming();
+      for (std::size_t i = 0; i != BatchSize; ++i) {
+        alternate(c[i].begin(), size);
+      }
+      st.ResumeTiming();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Arg(32)->Arg(1024)->Arg(8192);
+}
+
+int main(int argc, char** argv) {
+  auto std_remove_if    = [](auto first, auto last, auto pred) { return std::remove_if(first, last, pred); };
+  auto ranges_remove_if = [](auto first, auto last, auto pred) { return std::ranges::remove_if(first, last, pred); };
+
+  // std::remove_if
+  bm_prefix<std::vector<int>>("std::remove_if(vector<int>) (prefix)", std_remove_if);
+  bm_sprinkled<std::vector<int>>("std::remove_if(vector<int>) (sprinkled)", std_remove_if);
+
+  bm_prefix<std::deque<int>>("std::remove_if(deque<int>) (prefix)", std_remove_if);
+  bm_sprinkled<std::deque<int>>("std::remove_if(deque<int>) (sprinkled)", std_remove_if);
+
+  bm_prefix<std::list<int>>("std::remove_if(list<int>) (prefix)", std_remove_if);
+  bm_sprinkled<std::list<int>>("std::remove_if(list<int>) (sprinkled)", std_remove_if);
+
+  // ranges::remove_if
+  bm_prefix<std::vector<int>>("ranges::remove_if(vector<int>) (prefix)", ranges_remove_if);
+  bm_sprinkled<std::vector<int>>("ranges::remove_if(vector<int>) (sprinkled)", ranges_remove_if);
+
+  bm_prefix<std::deque<int>>("ranges::remove_if(deque<int>) (prefix)", ranges_remove_if);
+  bm_sprinkled<std::deque<int>>("ranges::remove_if(deque<int>) (sprinkled)", ranges_remove_if);
+
+  bm_prefix<std::list<int>>("ranges::remove_if(list<int>) (prefix)", ranges_remove_if);
+  bm_sprinkled<std::list<int>>("ranges::remove_if(list<int>) (sprinkled)", ranges_remove_if);
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/modifying/replace.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/replace.bench.cpp
new file mode 100644
index 0000000000000..b55790d4db017
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/modifying/replace.bench.cpp
@@ -0,0 +1,107 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+// Create a sequence of the form xxxxxxxxxxyyyyyyyyyy, replace
+// into zzzzzzzzzzzyyyyyyyyyy and then back.
+//
+// This measures the performance of replace() when replacing a large
+// contiguous sequence of equal values.
+template <class Container, class Operation>
+void bm_prefix(std::string operation_name, Operation replace) {
+  auto bench = [replace](auto& st) {
+    std::size_t const size = st.range(0);
+    using ValueType        = typename Container::value_type;
+    Container c;
+    ValueType x = Generate<ValueType>::random();
+    ValueType y = Generate<ValueType>::random();
+    ValueType z = Generate<ValueType>::random();
+    std::fill_n(std::back_inserter(c), size / 2, x);
+    std::fill_n(std::back_inserter(c), size / 2, y);
+
+    for ([[maybe_unused]] auto _ : st) {
+      replace(c.begin(), c.end(), x, z);
+      std::swap(x, z);
+      benchmark::DoNotOptimize(c);
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(z);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Arg(32)->Arg(1024)->Arg(8192);
+}
+
+// Sprinkle elements to replace inside the range, like xyxyxyxyxyxyxyxyxyxy.
+template <class Container, class Operation>
+void bm_sprinkled(std::string operation_name, Operation replace) {
+  auto bench = [replace](auto& st) {
+    std::size_t const size = st.range(0);
+    using ValueType        = typename Container::value_type;
+    Container c;
+    ValueType x = Generate<ValueType>::random();
+    ValueType y = Generate<ValueType>::random();
+    ValueType z = Generate<ValueType>::random();
+    for (std::size_t i = 0; i != size; ++i) {
+      c.push_back(i % 2 == 0 ? x : y);
+    }
+
+    for ([[maybe_unused]] auto _ : st) {
+      replace(c.begin(), c.end(), x, z);
+      std::swap(x, z);
+      benchmark::DoNotOptimize(c);
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(z);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Arg(32)->Arg(1024)->Arg(8192);
+}
+
+int main(int argc, char** argv) {
+  auto std_replace    = [](auto first, auto last, auto old, auto new_) { return std::replace(first, last, old, new_); };
+  auto ranges_replace = [](auto first, auto last, auto old, auto new_) {
+    return std::ranges::replace(first, last, old, new_);
+  };
+
+  // std::replace
+  bm_prefix<std::vector<int>>("std::replace(vector<int>) (prefix)", std_replace);
+  bm_sprinkled<std::vector<int>>("std::replace(vector<int>) (sprinkled)", std_replace);
+
+  bm_prefix<std::deque<int>>("std::replace(deque<int>) (prefix)", std_replace);
+  bm_sprinkled<std::deque<int>>("std::replace(deque<int>) (sprinkled)", std_replace);
+
+  bm_prefix<std::list<int>>("std::replace(list<int>) (prefix)", std_replace);
+  bm_sprinkled<std::list<int>>("std::replace(list<int>) (sprinkled)", std_replace);
+
+  // ranges::replace
+  bm_prefix<std::vector<int>>("ranges::replace(vector<int>) (prefix)", ranges_replace);
+  bm_sprinkled<std::vector<int>>("ranges::replace(vector<int>) (sprinkled)", ranges_replace);
+
+  bm_prefix<std::deque<int>>("ranges::replace(deque<int>) (prefix)", ranges_replace);
+  bm_sprinkled<std::deque<int>>("ranges::replace(deque<int>) (sprinkled)", ranges_replace);
+
+  bm_prefix<std::list<int>>("ranges::replace(list<int>) (prefix)", ranges_replace);
+  bm_sprinkled<std::list<int>>("ranges::replace(list<int>) (sprinkled)", ranges_replace);
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/modifying/replace_if.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/replace_if.bench.cpp
new file mode 100644
index 0000000000000..820cf08e5a901
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/modifying/replace_if.bench.cpp
@@ -0,0 +1,117 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+// Create a sequence of the form xxxxxxxxxxyyyyyyyyyy, replace
+// into zzzzzzzzzzzyyyyyyyyyy and then back.
+//
+// This measures the performance of replace_if() when replacing a large
+// contiguous sequence of equal values.
+template <class Container, class Operation>
+void bm_prefix(std::string operation_name, Operation replace_if) {
+  auto bench = [replace_if](auto& st) {
+    std::size_t const size = st.range(0);
+    using ValueType        = typename Container::value_type;
+    Container c;
+    ValueType x = Generate<ValueType>::random();
+    ValueType y = Generate<ValueType>::random();
+    ValueType z = Generate<ValueType>::random();
+    std::fill_n(std::back_inserter(c), size / 2, x);
+    std::fill_n(std::back_inserter(c), size / 2, y);
+
+    for ([[maybe_unused]] auto _ : st) {
+      auto pred = [&x](auto& element) {
+        benchmark::DoNotOptimize(element);
+        return element == x;
+      };
+      replace_if(c.begin(), c.end(), pred, z);
+      std::swap(x, z);
+      benchmark::DoNotOptimize(c);
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(z);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Arg(32)->Arg(1024)->Arg(8192);
+}
+
+// Sprinkle elements to replace inside the range, like xyxyxyxyxyxyxyxyxyxy.
+template <class Container, class Operation>
+void bm_sprinkled(std::string operation_name, Operation replace_if) {
+  auto bench = [replace_if](auto& st) {
+    std::size_t const size = st.range(0);
+    using ValueType        = typename Container::value_type;
+    Container c;
+    ValueType x = Generate<ValueType>::random();
+    ValueType y = Generate<ValueType>::random();
+    ValueType z = Generate<ValueType>::random();
+    for (std::size_t i = 0; i != size; ++i) {
+      c.push_back(i % 2 == 0 ? x : y);
+    }
+
+    for ([[maybe_unused]] auto _ : st) {
+      auto pred = [&x](auto& element) {
+        benchmark::DoNotOptimize(element);
+        return element == x;
+      };
+      replace_if(c.begin(), c.end(), pred, z);
+      std::swap(x, z);
+      benchmark::DoNotOptimize(c);
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(z);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Arg(32)->Arg(1024)->Arg(8192);
+}
+
+int main(int argc, char** argv) {
+  auto std_replace_if = [](auto first, auto last, auto pred, auto new_) {
+    return std::replace_if(first, last, pred, new_);
+  };
+  auto ranges_replace_if = [](auto first, auto last, auto pred, auto new_) {
+    return std::ranges::replace_if(first, last, pred, new_);
+  };
+
+  // std::replace_if
+  bm_prefix<std::vector<int>>("std::replace_if(vector<int>) (prefix)", std_replace_if);
+  bm_sprinkled<std::vector<int>>("std::replace_if(vector<int>) (sprinkled)", std_replace_if);
+
+  bm_prefix<std::deque<int>>("std::replace_if(deque<int>) (prefix)", std_replace_if);
+  bm_sprinkled<std::deque<int>>("std::replace_if(deque<int>) (sprinkled)", std_replace_if);
+
+  bm_prefix<std::list<int>>("std::replace_if(list<int>) (prefix)", std_replace_if);
+  bm_sprinkled<std::list<int>>("std::replace_if(list<int>) (sprinkled)", std_replace_if);
+
+  // ranges::replace_if
+  bm_prefix<std::vector<int>>("ranges::replace_if(vector<int>) (prefix)", ranges_replace_if);
+  bm_sprinkled<std::vector<int>>("ranges::replace_if(vector<int>) (sprinkled)", ranges_replace_if);
+
+  bm_prefix<std::deque<int>>("ranges::replace_if(deque<int>) (prefix)", ranges_replace_if);
+  bm_sprinkled<std::deque<int>>("ranges::replace_if(deque<int>) (sprinkled)", ranges_replace_if);
+
+  bm_prefix<std::list<int>>("ranges::replace_if(list<int>) (prefix)", ranges_replace_if);
+  bm_sprinkled<std::list<int>>("ranges::replace_if(list<int>) (sprinkled)", ranges_replace_if);
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/modifying/reverse.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/reverse.bench.cpp
new file mode 100644
index 0000000000000..dbb0d43d091c3
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/modifying/reverse.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 <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+template <class Container, class Operation>
+void bm(std::string operation_name, Operation reverse) {
+  auto bench = [reverse](auto& st) {
+    std::size_t const size = st.range(0);
+    using ValueType        = typename Container::value_type;
+    Container c;
+    std::generate_n(std::back_inserter(c), size, [] { return Generate<ValueType>::random(); });
+
+    for ([[maybe_unused]] auto _ : st) {
+      reverse(c.begin(), c.end());
+      benchmark::DoNotOptimize(c);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Range(8, 1 << 15);
+}
+
+int main(int argc, char** argv) {
+  auto std_reverse    = [](auto first, auto last) { return std::reverse(first, last); };
+  auto ranges_reverse = [](auto first, auto last) { return std::ranges::reverse(first, last); };
+
+  // std::reverse
+  bm<std::vector<int>>("std::reverse(vector<int>)", std_reverse);
+  bm<std::deque<int>>("std::reverse(deque<int>)", std_reverse);
+  bm<std::list<int>>("std::reverse(list<int>)", std_reverse);
+
+  // ranges::reverse
+  bm<std::vector<int>>("ranges::reverse(vector<int>)", ranges_reverse);
+  bm<std::deque<int>>("ranges::reverse(deque<int>)", ranges_reverse);
+  bm<std::list<int>>("ranges::reverse(list<int>)", ranges_reverse);
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/modifying/reverse_copy.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/reverse_copy.bench.cpp
new file mode 100644
index 0000000000000..84322fbfff40c
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/modifying/reverse_copy.bench.cpp
@@ -0,0 +1,61 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+template <class Container, class Operation>
+void bm(std::string operation_name, Operation reverse_copy) {
+  auto bench = [reverse_copy](auto& st) {
+    std::size_t const size = st.range(0);
+    using ValueType        = typename Container::value_type;
+    Container c;
+    std::generate_n(std::back_inserter(c), size, [] { return Generate<ValueType>::random(); });
+
+    std::vector<ValueType> out(size);
+
+    for ([[maybe_unused]] auto _ : st) {
+      reverse_copy(c.begin(), c.end(), out.begin());
+      benchmark::DoNotOptimize(c);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Range(8, 1 << 15);
+}
+
+int main(int argc, char** argv) {
+  auto std_reverse_copy    = [](auto first, auto last, auto out) { return std::reverse_copy(first, last, out); };
+  auto ranges_reverse_copy = [](auto first, auto last, auto out) {
+    return std::ranges::reverse_copy(first, last, out);
+  };
+
+  // std::reverse_copy
+  bm<std::vector<int>>("std::reverse_copy(vector<int>)", std_reverse_copy);
+  bm<std::deque<int>>("std::reverse_copy(deque<int>)", std_reverse_copy);
+  bm<std::list<int>>("std::reverse_copy(list<int>)", std_reverse_copy);
+
+  // ranges::reverse_copy
+  bm<std::vector<int>>("ranges::reverse_copy(vector<int>)", ranges_reverse_copy);
+  bm<std::deque<int>>("ranges::reverse_copy(deque<int>)", ranges_reverse_copy);
+  bm<std::list<int>>("ranges::reverse_copy(list<int>)", ranges_reverse_copy);
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/modifying/rotate.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/rotate.bench.cpp
new file mode 100644
index 0000000000000..eb91a011d863a
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/modifying/rotate.bench.cpp
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+template <class Container, class Operation>
+void bm(std::string operation_name, Operation rotate) {
+  auto bench = [rotate](auto& st) {
+    std::size_t const size = st.range(0);
+    using ValueType        = typename Container::value_type;
+    Container c;
+    std::generate_n(std::back_inserter(c), size, [] { return Generate<ValueType>::random(); });
+
+    auto middle = std::next(c.begin(), size / 2);
+    for ([[maybe_unused]] auto _ : st) {
+      auto result = rotate(c.begin(), middle, c.end());
+      benchmark::DoNotOptimize(result);
+      benchmark::DoNotOptimize(c);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Arg(32)->Arg(1024)->Arg(8192);
+}
+
+int main(int argc, char** argv) {
+  auto std_rotate    = [](auto first, auto middle, auto last) { return std::rotate(first, middle, last); };
+  auto ranges_rotate = [](auto first, auto middle, auto last) { return std::ranges::rotate(first, middle, last); };
+
+  // std::rotate
+  bm<std::vector<int>>("std::rotate(vector<int>)", std_rotate);
+  bm<std::deque<int>>("std::rotate(deque<int>)", std_rotate);
+  bm<std::list<int>>("std::rotate(list<int>)", std_rotate);
+
+  // ranges::rotate
+  bm<std::vector<int>>("ranges::rotate(vector<int>)", ranges_rotate);
+  bm<std::deque<int>>("ranges::rotate(deque<int>)", ranges_rotate);
+  bm<std::list<int>>("ranges::rotate(list<int>)", ranges_rotate);
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/modifying/rotate_copy.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/rotate_copy.bench.cpp
new file mode 100644
index 0000000000000..f5c14640fa87d
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/modifying/rotate_copy.bench.cpp
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+template <class Container, class Operation>
+void bm(std::string operation_name, Operation rotate_copy) {
+  auto bench = [rotate_copy](auto& st) {
+    std::size_t const size = st.range(0);
+    using ValueType        = typename Container::value_type;
+    Container c;
+    std::generate_n(std::back_inserter(c), size, [] { return Generate<ValueType>::random(); });
+
+    std::vector<ValueType> out(size);
+
+    auto middle = std::next(c.begin(), size / 2);
+    for ([[maybe_unused]] auto _ : st) {
+      auto result = rotate_copy(c.begin(), middle, c.end(), out.begin());
+      benchmark::DoNotOptimize(result);
+      benchmark::DoNotOptimize(c);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Arg(32)->Arg(1024)->Arg(8192);
+}
+
+int main(int argc, char** argv) {
+  auto std_rotate_copy = [](auto first, auto middle, auto last, auto out) {
+    return std::rotate_copy(first, middle, last, out);
+  };
+  auto ranges_rotate_copy = [](auto first, auto middle, auto last, auto out) {
+    return std::ranges::rotate_copy(first, middle, last, out);
+  };
+
+  // std::rotate_copy
+  bm<std::vector<int>>("std::rotate_copy(vector<int>)", std_rotate_copy);
+  bm<std::deque<int>>("std::rotate_copy(deque<int>)", std_rotate_copy);
+  bm<std::list<int>>("std::rotate_copy(list<int>)", std_rotate_copy);
+
+  // ranges::rotate_copy
+  bm<std::vector<int>>("ranges::rotate_copy(vector<int>)", ranges_rotate_copy);
+  bm<std::deque<int>>("ranges::rotate_copy(deque<int>)", ranges_rotate_copy);
+  bm<std::list<int>>("ranges::rotate_copy(list<int>)", ranges_rotate_copy);
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/modifying/sample.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/sample.bench.cpp
new file mode 100644
index 0000000000000..5f783efe8f627
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/modifying/sample.bench.cpp
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <list>
+#include <random>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+template <class Container, class Operation>
+void bm(std::string operation_name, Operation sample) {
+  auto bench = [sample](auto& st) {
+    std::size_t const size = st.range(0);
+    using ValueType        = typename Container::value_type;
+    Container c;
+    std::generate_n(std::back_inserter(c), size, [] { return Generate<ValueType>::random(); });
+
+    std::vector<ValueType> out(size);
+    auto const n = size / 4; // sample 1/4 of the range
+    std::mt19937 rng;
+
+    for ([[maybe_unused]] auto _ : st) {
+      auto result = sample(c.begin(), c.end(), out.begin(), n, rng);
+      benchmark::DoNotOptimize(result);
+      benchmark::DoNotOptimize(c);
+      benchmark::DoNotOptimize(out);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Arg(32)->Arg(1024)->Arg(8192);
+}
+
+int main(int argc, char** argv) {
+  auto std_sample = [](auto first, auto last, auto out, auto n, auto& rng) {
+    return std::sample(first, last, out, n, rng);
+  };
+  auto ranges_sample = [](auto first, auto last, auto out, auto n, auto& rng) {
+    return std::ranges::sample(first, last, out, n, rng);
+  };
+
+  // std::sample
+  bm<std::vector<int>>("std::sample(vector<int>)", std_sample);
+  bm<std::deque<int>>("std::sample(deque<int>)", std_sample);
+  bm<std::list<int>>("std::sample(list<int>)", std_sample);
+
+  // ranges::sample
+  bm<std::vector<int>>("ranges::sample(vector<int>)", ranges_sample);
+  bm<std::deque<int>>("ranges::sample(deque<int>)", ranges_sample);
+  bm<std::list<int>>("ranges::sample(list<int>)", ranges_sample);
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/modifying/shift_left.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/shift_left.bench.cpp
new file mode 100644
index 0000000000000..9cf428f67e03e
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/modifying/shift_left.bench.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+template <class Container, class Operation>
+void bm(std::string operation_name, Operation shift_left) {
+  auto bench = [shift_left](auto& st) {
+    std::size_t const size = st.range(0);
+    using ValueType        = typename Container::value_type;
+    Container c;
+    std::generate_n(std::back_inserter(c), size, [] { return Generate<ValueType>::random(); });
+
+    auto const n = 9 * (size / 10); // shift all but 10% of the range
+
+    for ([[maybe_unused]] auto _ : st) {
+      auto result = shift_left(c.begin(), c.end(), n);
+      benchmark::DoNotOptimize(result);
+      benchmark::DoNotOptimize(c);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Arg(32)->Arg(1024)->Arg(8192);
+}
+
+int main(int argc, char** argv) {
+  auto std_shift_left = [](auto first, auto last, auto n) { return std::shift_left(first, last, n); };
+
+  // std::shift_left
+  bm<std::vector<int>>("std::shift_left(vector<int>)", std_shift_left);
+  bm<std::deque<int>>("std::shift_left(deque<int>)", std_shift_left);
+  bm<std::list<int>>("std::shift_left(list<int>)", std_shift_left);
+
+  // ranges::shift_left not implemented yet
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/modifying/shift_right.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/shift_right.bench.cpp
new file mode 100644
index 0000000000000..31a980ebdc5c1
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/modifying/shift_right.bench.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+template <class Container, class Operation>
+void bm(std::string operation_name, Operation shift_right) {
+  auto bench = [shift_right](auto& st) {
+    std::size_t const size = st.range(0);
+    using ValueType        = typename Container::value_type;
+    Container c;
+    std::generate_n(std::back_inserter(c), size, [] { return Generate<ValueType>::random(); });
+
+    auto const n = 9 * (size / 10); // shift all but 10% of the range
+
+    for ([[maybe_unused]] auto _ : st) {
+      auto result = shift_right(c.begin(), c.end(), n);
+      benchmark::DoNotOptimize(result);
+      benchmark::DoNotOptimize(c);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Arg(32)->Arg(1024)->Arg(8192);
+}
+
+int main(int argc, char** argv) {
+  auto std_shift_right = [](auto first, auto last, auto n) { return std::shift_right(first, last, n); };
+
+  // std::shift_right
+  bm<std::vector<int>>("std::shift_right(vector<int>)", std_shift_right);
+  bm<std::deque<int>>("std::shift_right(deque<int>)", std_shift_right);
+  bm<std::list<int>>("std::shift_right(list<int>)", std_shift_right);
+
+  // ranges::shift_right not implemented yet
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/modifying/shuffle.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/shuffle.bench.cpp
new file mode 100644
index 0000000000000..cc483b631c2a4
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/modifying/shuffle.bench.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <random>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+template <class Container, class Operation>
+void bm(std::string operation_name, Operation shuffle) {
+  auto bench = [shuffle](auto& st) {
+    std::size_t const size = st.range(0);
+    using ValueType        = typename Container::value_type;
+    Container c;
+    std::generate_n(std::back_inserter(c), size, [] { return Generate<ValueType>::random(); });
+    std::mt19937 rng;
+
+    for ([[maybe_unused]] auto _ : st) {
+      shuffle(c.begin(), c.end(), rng);
+      benchmark::DoNotOptimize(c);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Arg(32)->Arg(1024)->Arg(8192);
+}
+
+int main(int argc, char** argv) {
+  auto std_shuffle    = [](auto first, auto last, auto& rng) { return std::shuffle(first, last, rng); };
+  auto ranges_shuffle = [](auto first, auto last, auto& rng) { return std::ranges::shuffle(first, last, rng); };
+
+  // std::shuffle
+  bm<std::vector<int>>("std::shuffle(vector<int>)", std_shuffle);
+  bm<std::deque<int>>("std::shuffle(deque<int>)", std_shuffle);
+
+  // ranges::shuffle
+  bm<std::vector<int>>("ranges::shuffle(vector<int>)", ranges_shuffle);
+  bm<std::deque<int>>("ranges::shuffle(deque<int>)", ranges_shuffle);
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/modifying/swap_ranges.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/swap_ranges.bench.cpp
new file mode 100644
index 0000000000000..05b733fecc70d
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/modifying/swap_ranges.bench.cpp
@@ -0,0 +1,64 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+template <class Container, class Operation>
+void bm(std::string operation_name, Operation swap_ranges) {
+  auto bench = [swap_ranges](auto& st) {
+    std::size_t const size = st.range(0);
+    using ValueType        = typename Container::value_type;
+    Container c1, c2;
+    std::generate_n(std::back_inserter(c1), size, [] { return Generate<ValueType>::random(); });
+    std::generate_n(std::back_inserter(c2), size, [] { return Generate<ValueType>::random(); });
+
+    for ([[maybe_unused]] auto _ : st) {
+      auto result = swap_ranges(c1.begin(), c1.end(), c2.begin(), c2.end());
+      benchmark::DoNotOptimize(result);
+      benchmark::DoNotOptimize(c1);
+      benchmark::DoNotOptimize(c2);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Arg(32)->Arg(1024)->Arg(8192);
+}
+
+int main(int argc, char** argv) {
+  auto std_swap_ranges = [](auto first1, auto last1, auto first2, auto) {
+    return std::swap_ranges(first1, last1, first2);
+  };
+  auto ranges_swap_ranges = [](auto first1, auto last1, auto first2, auto last2) {
+    return std::ranges::swap_ranges(first1, last1, first2, last2);
+  };
+
+  // std::swap_ranges
+  bm<std::vector<int>>("std::swap_ranges(vector<int>)", std_swap_ranges);
+  bm<std::deque<int>>("std::swap_ranges(deque<int>)", std_swap_ranges);
+  bm<std::list<int>>("std::swap_ranges(list<int>)", std_swap_ranges);
+
+  // ranges::swap_ranges
+  bm<std::vector<int>>("ranges::swap_ranges(vector<int>)", ranges_swap_ranges);
+  bm<std::deque<int>>("ranges::swap_ranges(deque<int>)", ranges_swap_ranges);
+  bm<std::list<int>>("ranges::swap_ranges(list<int>)", ranges_swap_ranges);
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/modifying/transform.binary.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/transform.binary.bench.cpp
new file mode 100644
index 0000000000000..bdc913356d3c9
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/modifying/transform.binary.bench.cpp
@@ -0,0 +1,73 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+template <class Container, class Operation>
+void bm(std::string operation_name, Operation transform) {
+  auto bench = [transform](auto& st) {
+    std::size_t const size = st.range(0);
+    using ValueType        = typename Container::value_type;
+    Container c1, c2;
+    std::generate_n(std::back_inserter(c1), size, [] { return Generate<ValueType>::random(); });
+    std::generate_n(std::back_inserter(c2), size, [] { return Generate<ValueType>::random(); });
+
+    std::vector<ValueType> out(size);
+
+    auto f = [](auto& x, auto& y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x + y;
+    };
+
+    for ([[maybe_unused]] auto _ : st) {
+      auto result = transform(c1.begin(), c1.end(), c2.begin(), c2.end(), out.begin(), f);
+      benchmark::DoNotOptimize(result);
+      benchmark::DoNotOptimize(out);
+      benchmark::DoNotOptimize(c1);
+      benchmark::DoNotOptimize(c2);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Arg(32)->Arg(1024)->Arg(8192);
+}
+
+int main(int argc, char** argv) {
+  auto std_transform = [](auto first1, auto last1, auto first2, auto, auto out, auto f) {
+    return std::transform(first1, last1, first2, out, f);
+  };
+  auto ranges_transform = [](auto first1, auto last1, auto first2, auto last2, auto out, auto f) {
+    return std::ranges::transform(first1, last1, first2, last2, out, f);
+  };
+
+  // std::transform
+  bm<std::vector<int>>("std::transform(vector<int>, vector<int>)", std_transform);
+  bm<std::deque<int>>("std::transform(deque<int>, deque<int>)", std_transform);
+  bm<std::list<int>>("std::transform(list<int>, list<int>)", std_transform);
+
+  // ranges::transform
+  bm<std::vector<int>>("ranges::transform(vector<int>, vector<int>)", ranges_transform);
+  bm<std::deque<int>>("ranges::transform(deque<int>, deque<int>)", ranges_transform);
+  bm<std::list<int>>("ranges::transform(list<int>, list<int>)", ranges_transform);
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/modifying/transform.unary.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/transform.unary.bench.cpp
new file mode 100644
index 0000000000000..6cf56368f63f3
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/modifying/transform.unary.bench.cpp
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+template <class Container, class Operation>
+void bm(std::string operation_name, Operation transform) {
+  auto bench = [transform](auto& st) {
+    std::size_t const size = st.range(0);
+    using ValueType        = typename Container::value_type;
+    Container c;
+    std::generate_n(std::back_inserter(c), size, [] { return Generate<ValueType>::random(); });
+
+    std::vector<ValueType> out(size);
+
+    auto f = [](auto& element) {
+      benchmark::DoNotOptimize(element);
+      return element;
+    };
+
+    for ([[maybe_unused]] auto _ : st) {
+      auto result = transform(c.begin(), c.end(), out.begin(), f);
+      benchmark::DoNotOptimize(result);
+      benchmark::DoNotOptimize(out);
+      benchmark::DoNotOptimize(c);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Arg(32)->Arg(1024)->Arg(8192);
+}
+
+int main(int argc, char** argv) {
+  auto std_transform    = [](auto first, auto last, auto out, auto f) { return std::transform(first, last, out, f); };
+  auto ranges_transform = [](auto first, auto last, auto out, auto f) {
+    return std::ranges::transform(first, last, out, f);
+  };
+
+  // std::transform
+  bm<std::vector<int>>("std::transform(vector<int>) (identity transform)", std_transform);
+  bm<std::deque<int>>("std::transform(deque<int>) (identity transform)", std_transform);
+  bm<std::list<int>>("std::transform(list<int>) (identity transform)", std_transform);
+
+  // ranges::transform
+  bm<std::vector<int>>("ranges::transform(vector<int>) (identity transform)", ranges_transform);
+  bm<std::deque<int>>("ranges::transform(deque<int>) (identity transform)", ranges_transform);
+  bm<std::list<int>>("ranges::transform(list<int>) (identity transform)", ranges_transform);
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/modifying/unique.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/unique.bench.cpp
new file mode 100644
index 0000000000000..1d9cca2f60688
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/modifying/unique.bench.cpp
@@ -0,0 +1,132 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+// Create a sequence of the form xxxxxxxxxxyyyyyyyyyy and unique the
+// adjacent equal elements.
+//
+// We perform this benchmark in a batch because we need to restore the
+// state of the container after the operation.
+template <class Container, class Operation>
+void bm_contiguous(std::string operation_name, Operation unique) {
+  auto bench = [unique](auto& st) {
+    std::size_t const size          = st.range(0);
+    constexpr std::size_t BatchSize = 10;
+    using ValueType                 = typename Container::value_type;
+    Container c[BatchSize];
+    ValueType x = Generate<ValueType>::random();
+    ValueType y = Generate<ValueType>::random();
+    for (std::size_t i = 0; i != BatchSize; ++i) {
+      c[i]      = Container(size);
+      auto half = size / 2;
+      std::fill_n(std::fill_n(c[i].begin(), half, x), half, y);
+    }
+
+    while (st.KeepRunningBatch(BatchSize)) {
+      for (std::size_t i = 0; i != BatchSize; ++i) {
+        auto result = unique(c[i].begin(), c[i].end());
+        benchmark::DoNotOptimize(result);
+        benchmark::DoNotOptimize(c[i]);
+        benchmark::ClobberMemory();
+      }
+
+      st.PauseTiming();
+      for (std::size_t i = 0; i != BatchSize; ++i) {
+        auto half = size / 2;
+        std::fill_n(std::fill_n(c[i].begin(), half, x), half, y);
+      }
+      st.ResumeTiming();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Arg(32)->Arg(1024)->Arg(8192);
+}
+
+// Create a sequence of the form xxyyxxyyxxyyxxyyxxyy and unique
+// adjacent equal elements.
+//
+// We perform this benchmark in a batch because we need to restore the
+// state of the container after the operation.
+template <class Container, class Operation>
+void bm_sprinkled(std::string operation_name, Operation unique) {
+  auto bench = [unique](auto& st) {
+    std::size_t const size          = st.range(0);
+    constexpr std::size_t BatchSize = 10;
+    using ValueType                 = typename Container::value_type;
+    Container c[BatchSize];
+    ValueType x    = Generate<ValueType>::random();
+    ValueType y    = Generate<ValueType>::random();
+    auto alternate = [&](auto out, auto n) {
+      for (std::size_t i = 0; i != n; i += 2) {
+        *out++ = (i % 4 == 0 ? x : y);
+        *out++ = (i % 4 == 0 ? x : y);
+      }
+    };
+    for (std::size_t i = 0; i != BatchSize; ++i) {
+      c[i] = Container(size);
+      alternate(c[i].begin(), size);
+    }
+
+    while (st.KeepRunningBatch(BatchSize)) {
+      for (std::size_t i = 0; i != BatchSize; ++i) {
+        auto result = unique(c[i].begin(), c[i].end());
+        benchmark::DoNotOptimize(result);
+        benchmark::DoNotOptimize(c[i]);
+        benchmark::ClobberMemory();
+      }
+
+      st.PauseTiming();
+      for (std::size_t i = 0; i != BatchSize; ++i) {
+        alternate(c[i].begin(), size);
+      }
+      st.ResumeTiming();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Arg(32)->Arg(1024)->Arg(8192);
+}
+
+int main(int argc, char** argv) {
+  auto std_unique    = [](auto first, auto last) { return std::unique(first, last); };
+  auto ranges_unique = [](auto first, auto last) { return std::ranges::unique(first, last); };
+
+  // std::unique
+  bm_contiguous<std::vector<int>>("std::unique(vector<int>) (contiguous)", std_unique);
+  bm_sprinkled<std::vector<int>>("std::unique(vector<int>) (sprinkled)", std_unique);
+
+  bm_contiguous<std::deque<int>>("std::unique(deque<int>) (contiguous)", std_unique);
+  bm_sprinkled<std::deque<int>>("std::unique(deque<int>) (sprinkled)", std_unique);
+
+  bm_contiguous<std::list<int>>("std::unique(list<int>) (contiguous)", std_unique);
+  bm_sprinkled<std::list<int>>("std::unique(list<int>) (sprinkled)", std_unique);
+
+  // ranges::unique
+  bm_contiguous<std::vector<int>>("ranges::unique(vector<int>) (contiguous)", ranges_unique);
+  bm_sprinkled<std::vector<int>>("ranges::unique(vector<int>) (sprinkled)", ranges_unique);
+
+  bm_contiguous<std::deque<int>>("ranges::unique(deque<int>) (contiguous)", ranges_unique);
+  bm_sprinkled<std::deque<int>>("ranges::unique(deque<int>) (sprinkled)", ranges_unique);
+
+  bm_contiguous<std::list<int>>("ranges::unique(list<int>) (contiguous)", ranges_unique);
+  bm_sprinkled<std::list<int>>("ranges::unique(list<int>) (sprinkled)", ranges_unique);
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/modifying/unique_copy.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/unique_copy.bench.cpp
new file mode 100644
index 0000000000000..c7b217ffd960d
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/modifying/unique_copy.bench.cpp
@@ -0,0 +1,105 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+// Create a sequence of the form xxxxxxxxxxyyyyyyyyyy and unique the
+// adjacent equal elements.
+template <class Container, class Operation>
+void bm_contiguous(std::string operation_name, Operation unique_copy) {
+  auto bench = [unique_copy](auto& st) {
+    std::size_t const size = st.range(0);
+    using ValueType        = typename Container::value_type;
+    Container c(size);
+    ValueType x = Generate<ValueType>::random();
+    ValueType y = Generate<ValueType>::random();
+    auto half   = size / 2;
+    std::fill_n(std::fill_n(c.begin(), half, x), half, y);
+
+    std::vector<ValueType> out(size);
+
+    for ([[maybe_unused]] auto _ : st) {
+      auto result = unique_copy(c.begin(), c.end(), out.begin());
+      benchmark::DoNotOptimize(result);
+      benchmark::DoNotOptimize(c);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Arg(32)->Arg(1024)->Arg(8192);
+}
+
+// Create a sequence of the form xxyyxxyyxxyyxxyyxxyy and unique
+// adjacent equal elements.
+template <class Container, class Operation>
+void bm_sprinkled(std::string operation_name, Operation unique_copy) {
+  auto bench = [unique_copy](auto& st) {
+    std::size_t const size = st.range(0);
+    using ValueType        = typename Container::value_type;
+    Container c(size);
+    ValueType x    = Generate<ValueType>::random();
+    ValueType y    = Generate<ValueType>::random();
+    auto alternate = [&](auto out, auto n) {
+      for (std::size_t i = 0; i != n; i += 2) {
+        *out++ = (i % 4 == 0 ? x : y);
+        *out++ = (i % 4 == 0 ? x : y);
+      }
+    };
+    alternate(c.begin(), size);
+
+    std::vector<ValueType> out(size);
+
+    for ([[maybe_unused]] auto _ : st) {
+      auto result = unique_copy(c.begin(), c.end(), out.begin());
+      benchmark::DoNotOptimize(result);
+      benchmark::DoNotOptimize(c);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Arg(32)->Arg(1024)->Arg(8192);
+}
+
+int main(int argc, char** argv) {
+  auto std_unique_copy    = [](auto first, auto last, auto out) { return std::unique_copy(first, last, out); };
+  auto ranges_unique_copy = [](auto first, auto last, auto out) { return std::ranges::unique_copy(first, last, out); };
+
+  // std::unique_copy
+  bm_contiguous<std::vector<int>>("std::unique_copy(vector<int>) (contiguous)", std_unique_copy);
+  bm_sprinkled<std::vector<int>>("std::unique_copy(vector<int>) (sprinkled)", std_unique_copy);
+
+  bm_contiguous<std::deque<int>>("std::unique_copy(deque<int>) (contiguous)", std_unique_copy);
+  bm_sprinkled<std::deque<int>>("std::unique_copy(deque<int>) (sprinkled)", std_unique_copy);
+
+  bm_contiguous<std::list<int>>("std::unique_copy(list<int>) (contiguous)", std_unique_copy);
+  bm_sprinkled<std::list<int>>("std::unique_copy(list<int>) (sprinkled)", std_unique_copy);
+
+  // ranges::unique_copy
+  bm_contiguous<std::vector<int>>("ranges::unique_copy(vector<int>) (contiguous)", ranges_unique_copy);
+  bm_sprinkled<std::vector<int>>("ranges::unique_copy(vector<int>) (sprinkled)", ranges_unique_copy);
+
+  bm_contiguous<std::deque<int>>("ranges::unique_copy(deque<int>) (contiguous)", ranges_unique_copy);
+  bm_sprinkled<std::deque<int>>("ranges::unique_copy(deque<int>) (sprinkled)", ranges_unique_copy);
+
+  bm_contiguous<std::list<int>>("ranges::unique_copy(list<int>) (contiguous)", ranges_unique_copy);
+  bm_sprinkled<std::list<int>>("ranges::unique_copy(list<int>) (sprinkled)", ranges_unique_copy);
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/modifying/unique_copy_pred.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/unique_copy_pred.bench.cpp
new file mode 100644
index 0000000000000..24ca442fb3de4
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/modifying/unique_copy_pred.bench.cpp
@@ -0,0 +1,121 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+// Create a sequence of the form xxxxxxxxxxyyyyyyyyyy and unique the
+// adjacent equal elements.
+template <class Container, class Operation>
+void bm_contiguous(std::string operation_name, Operation unique_copy) {
+  auto bench = [unique_copy](auto& st) {
+    std::size_t const size = st.range(0);
+    using ValueType        = typename Container::value_type;
+    Container c(size);
+    ValueType x = Generate<ValueType>::random();
+    ValueType y = Generate<ValueType>::random();
+    auto half   = size / 2;
+    std::fill_n(std::fill_n(c.begin(), half, x), half, y);
+
+    std::vector<ValueType> out(size);
+
+    auto pred = [](auto& a, auto& b) {
+      benchmark::DoNotOptimize(a);
+      benchmark::DoNotOptimize(b);
+      return a == b;
+    };
+
+    for ([[maybe_unused]] auto _ : st) {
+      auto result = unique_copy(c.begin(), c.end(), out.begin(), pred);
+      benchmark::DoNotOptimize(result);
+      benchmark::DoNotOptimize(c);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Arg(32)->Arg(1024)->Arg(8192);
+}
+
+// Create a sequence of the form xxyyxxyyxxyyxxyyxxyy and unique
+// adjacent equal elements.
+template <class Container, class Operation>
+void bm_sprinkled(std::string operation_name, Operation unique_copy) {
+  auto bench = [unique_copy](auto& st) {
+    std::size_t const size = st.range(0);
+    using ValueType        = typename Container::value_type;
+    Container c(size);
+    ValueType x    = Generate<ValueType>::random();
+    ValueType y    = Generate<ValueType>::random();
+    auto alternate = [&](auto out, auto n) {
+      for (std::size_t i = 0; i != n; i += 2) {
+        *out++ = (i % 4 == 0 ? x : y);
+        *out++ = (i % 4 == 0 ? x : y);
+      }
+    };
+    alternate(c.begin(), size);
+
+    std::vector<ValueType> out(size);
+
+    auto pred = [](auto& a, auto& b) {
+      benchmark::DoNotOptimize(a);
+      benchmark::DoNotOptimize(b);
+      return a == b;
+    };
+
+    for ([[maybe_unused]] auto _ : st) {
+      auto result = unique_copy(c.begin(), c.end(), out.begin(), pred);
+      benchmark::DoNotOptimize(result);
+      benchmark::DoNotOptimize(c);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Arg(32)->Arg(1024)->Arg(8192);
+}
+
+int main(int argc, char** argv) {
+  auto std_unique_copy = [](auto first, auto last, auto out, auto pred) {
+    return std::unique_copy(first, last, out, pred);
+  };
+  auto ranges_unique_copy = [](auto first, auto last, auto out, auto pred) {
+    return std::ranges::unique_copy(first, last, out, pred);
+  };
+
+  // std::unique_copy
+  bm_contiguous<std::vector<int>>("std::unique_copy(vector<int>, pred) (contiguous)", std_unique_copy);
+  bm_sprinkled<std::vector<int>>("std::unique_copy(vector<int>, pred) (sprinkled)", std_unique_copy);
+
+  bm_contiguous<std::deque<int>>("std::unique_copy(deque<int>, pred) (contiguous)", std_unique_copy);
+  bm_sprinkled<std::deque<int>>("std::unique_copy(deque<int>, pred) (sprinkled)", std_unique_copy);
+
+  bm_contiguous<std::list<int>>("std::unique_copy(list<int>, pred) (contiguous)", std_unique_copy);
+  bm_sprinkled<std::list<int>>("std::unique_copy(list<int>, pred) (sprinkled)", std_unique_copy);
+
+  // ranges::unique_copy
+  bm_contiguous<std::vector<int>>("ranges::unique_copy(vector<int>, pred) (contiguous)", ranges_unique_copy);
+  bm_sprinkled<std::vector<int>>("ranges::unique_copy(vector<int>, pred) (sprinkled)", ranges_unique_copy);
+
+  bm_contiguous<std::deque<int>>("ranges::unique_copy(deque<int>, pred) (contiguous)", ranges_unique_copy);
+  bm_sprinkled<std::deque<int>>("ranges::unique_copy(deque<int>, pred) (sprinkled)", ranges_unique_copy);
+
+  bm_contiguous<std::list<int>>("ranges::unique_copy(list<int>, pred) (contiguous)", ranges_unique_copy);
+  bm_sprinkled<std::list<int>>("ranges::unique_copy(list<int>, pred) (sprinkled)", ranges_unique_copy);
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/modifying/unique_pred.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/unique_pred.bench.cpp
new file mode 100644
index 0000000000000..0b258ea5cbecc
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/modifying/unique_pred.bench.cpp
@@ -0,0 +1,144 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+// Create a sequence of the form xxxxxxxxxxyyyyyyyyyy and unique the
+// adjacent equal elements.
+//
+// We perform this benchmark in a batch because we need to restore the
+// state of the container after the operation.
+template <class Container, class Operation>
+void bm_contiguous(std::string operation_name, Operation unique) {
+  auto bench = [unique](auto& st) {
+    std::size_t const size          = st.range(0);
+    constexpr std::size_t BatchSize = 10;
+    using ValueType                 = typename Container::value_type;
+    Container c[BatchSize];
+    ValueType x = Generate<ValueType>::random();
+    ValueType y = Generate<ValueType>::random();
+    for (std::size_t i = 0; i != BatchSize; ++i) {
+      c[i]      = Container(size);
+      auto half = size / 2;
+      std::fill_n(std::fill_n(c[i].begin(), half, x), half, y);
+    }
+
+    auto pred = [](auto& a, auto& b) {
+      benchmark::DoNotOptimize(a);
+      benchmark::DoNotOptimize(b);
+      return a == b;
+    };
+
+    while (st.KeepRunningBatch(BatchSize)) {
+      for (std::size_t i = 0; i != BatchSize; ++i) {
+        auto result = unique(c[i].begin(), c[i].end(), pred);
+        benchmark::DoNotOptimize(result);
+        benchmark::DoNotOptimize(c[i]);
+        benchmark::ClobberMemory();
+      }
+
+      st.PauseTiming();
+      for (std::size_t i = 0; i != BatchSize; ++i) {
+        auto half = size / 2;
+        std::fill_n(std::fill_n(c[i].begin(), half, x), half, y);
+      }
+      st.ResumeTiming();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Arg(32)->Arg(1024)->Arg(8192);
+}
+
+// Create a sequence of the form xxyyxxyyxxyyxxyyxxyy and unique
+// adjacent equal elements.
+//
+// We perform this benchmark in a batch because we need to restore the
+// state of the container after the operation.
+template <class Container, class Operation>
+void bm_sprinkled(std::string operation_name, Operation unique) {
+  auto bench = [unique](auto& st) {
+    std::size_t const size          = st.range(0);
+    constexpr std::size_t BatchSize = 10;
+    using ValueType                 = typename Container::value_type;
+    Container c[BatchSize];
+    ValueType x    = Generate<ValueType>::random();
+    ValueType y    = Generate<ValueType>::random();
+    auto alternate = [&](auto out, auto n) {
+      for (std::size_t i = 0; i != n; i += 2) {
+        *out++ = (i % 4 == 0 ? x : y);
+        *out++ = (i % 4 == 0 ? x : y);
+      }
+    };
+    for (std::size_t i = 0; i != BatchSize; ++i) {
+      c[i] = Container(size);
+      alternate(c[i].begin(), size);
+    }
+
+    auto pred = [](auto& a, auto& b) {
+      benchmark::DoNotOptimize(a);
+      benchmark::DoNotOptimize(b);
+      return a == b;
+    };
+
+    while (st.KeepRunningBatch(BatchSize)) {
+      for (std::size_t i = 0; i != BatchSize; ++i) {
+        auto result = unique(c[i].begin(), c[i].end(), pred);
+        benchmark::DoNotOptimize(result);
+        benchmark::DoNotOptimize(c[i]);
+        benchmark::ClobberMemory();
+      }
+
+      st.PauseTiming();
+      for (std::size_t i = 0; i != BatchSize; ++i) {
+        alternate(c[i].begin(), size);
+      }
+      st.ResumeTiming();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Arg(32)->Arg(1024)->Arg(8192);
+}
+
+int main(int argc, char** argv) {
+  auto std_unique    = [](auto first, auto last, auto pred) { return std::unique(first, last, pred); };
+  auto ranges_unique = [](auto first, auto last, auto pred) { return std::ranges::unique(first, last, pred); };
+
+  // std::unique
+  bm_contiguous<std::vector<int>>("std::unique(vector<int>, pred) (contiguous)", std_unique);
+  bm_sprinkled<std::vector<int>>("std::unique(vector<int>, pred) (sprinkled)", std_unique);
+
+  bm_contiguous<std::deque<int>>("std::unique(deque<int>, pred) (contiguous)", std_unique);
+  bm_sprinkled<std::deque<int>>("std::unique(deque<int>, pred) (sprinkled)", std_unique);
+
+  bm_contiguous<std::list<int>>("std::unique(list<int>, pred) (contiguous)", std_unique);
+  bm_sprinkled<std::list<int>>("std::unique(list<int>, pred) (sprinkled)", std_unique);
+
+  // ranges::unique
+  bm_contiguous<std::vector<int>>("ranges::unique(vector<int>, pred) (contiguous)", ranges_unique);
+  bm_sprinkled<std::vector<int>>("ranges::unique(vector<int>, pred) (sprinkled)", ranges_unique);
+
+  bm_contiguous<std::deque<int>>("ranges::unique(deque<int>, pred) (contiguous)", ranges_unique);
+  bm_sprinkled<std::deque<int>>("ranges::unique(deque<int>, pred) (sprinkled)", ranges_unique);
+
+  bm_contiguous<std::list<int>>("ranges::unique(list<int>, pred) (contiguous)", ranges_unique);
+  bm_sprinkled<std::list<int>>("ranges::unique(list<int>, pred) (sprinkled)", ranges_unique);
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/reverse.bench.cpp b/libcxx/test/benchmarks/algorithms/reverse.bench.cpp
deleted file mode 100644
index 2d8dd819ac24c..0000000000000
--- a/libcxx/test/benchmarks/algorithms/reverse.bench.cpp
+++ /dev/null
@@ -1,48 +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 <iterator>
-#include <string>
-#include <vector>
-
-#include <benchmark/benchmark.h>
-#include "../GenerateInput.h"
-
-template <class T>
-static void bm_reverse(benchmark::State& state) {
-  std::size_t const n = state.range();
-  std::vector<T> vec;
-  std::generate_n(std::back_inserter(vec), n, [] { return Generate<T>::cheap(); });
-  for (auto _ : state) {
-    std::reverse(vec.begin(), vec.end());
-    benchmark::DoNotOptimize(vec);
-  }
-}
-BENCHMARK(bm_reverse<int>)->Name("std::reverse(vector<int>)")->DenseRange(1, 8)->Range(16, 1 << 20);
-BENCHMARK(bm_reverse<std::string>)->Name("std::reverse(vector<string>)")->DenseRange(1, 8)->Range(16, 1 << 20);
-
-template <class T>
-static void bm_ranges_reverse(benchmark::State& state) {
-  std::size_t const n = state.range();
-  std::vector<T> vec;
-  std::generate_n(std::back_inserter(vec), n, [] { return Generate<T>::cheap(); });
-  for (auto _ : state) {
-    std::ranges::reverse(vec.begin(), vec.end());
-    benchmark::DoNotOptimize(vec);
-  }
-}
-BENCHMARK(bm_ranges_reverse<int>)->Name("ranges::reverse(vector<int>)")->DenseRange(1, 8)->Range(16, 1 << 20);
-BENCHMARK(bm_ranges_reverse<std::string>)
-    ->Name("ranges::reverse(vector<string>)")
-    ->DenseRange(1, 8)
-    ->Range(16, 1 << 20);
-
-BENCHMARK_MAIN();



More information about the libcxx-commits mailing list