[libcxx-commits] [libcxx] [libc++] Add benchmarks for copy algorithms (PR #127328)

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Tue Feb 18 14:07:56 PST 2025


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

>From af7a4331290716e57fd90ef9e7ae8816263ab19d Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Sat, 15 Feb 2025 15:42:31 +0100
Subject: [PATCH 1/3] [libc++] Add benchmarks for copy algorithms

This patch adds benchmarks for the copy family of algorithms
(copy, copy_n, copy_if, copy_backward).
---
 .../test/benchmarks/algorithms/copy.bench.cpp |  89 ---------------
 .../algorithms/copy_backward.bench.cpp        |  55 ---------
 .../algorithms/modifying/copy.bench.cpp       |  84 ++++++++++++++
 .../modifying/copy_backward.bench.cpp         |  86 ++++++++++++++
 .../algorithms/modifying/copy_if.bench.cpp    | 105 ++++++++++++++++++
 .../algorithms/modifying/copy_n.bench.cpp     |  83 ++++++++++++++
 6 files changed, 358 insertions(+), 144 deletions(-)
 delete mode 100644 libcxx/test/benchmarks/algorithms/copy.bench.cpp
 delete mode 100644 libcxx/test/benchmarks/algorithms/copy_backward.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/modifying/copy.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/modifying/copy_backward.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/modifying/copy_if.bench.cpp
 create mode 100644 libcxx/test/benchmarks/algorithms/modifying/copy_n.bench.cpp

diff --git a/libcxx/test/benchmarks/algorithms/copy.bench.cpp b/libcxx/test/benchmarks/algorithms/copy.bench.cpp
deleted file mode 100644
index b6f0f15eb7703..0000000000000
--- a/libcxx/test/benchmarks/algorithms/copy.bench.cpp
+++ /dev/null
@@ -1,89 +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_ranges_copy_vb(benchmark::State& state, bool aligned) {
-  auto n = state.range();
-  std::vector<bool> in(n, true);
-  std::vector<bool> out(aligned ? n : n + 8);
-  benchmark::DoNotOptimize(&in);
-  auto dst = aligned ? out.begin() : out.begin() + 4;
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(std::ranges::copy(in, dst));
-    benchmark::DoNotOptimize(&out);
-  }
-}
-
-static void bm_ranges_copy_n_vb(benchmark::State& state, bool aligned) {
-  auto n = state.range();
-  std::vector<bool> in(n, true);
-  std::vector<bool> out(aligned ? n : n + 8);
-  benchmark::DoNotOptimize(&in);
-  auto src = in.begin();
-  auto dst = aligned ? out.begin() : out.begin() + 4;
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(std::ranges::copy_n(src, n, dst));
-    benchmark::DoNotOptimize(&out);
-  }
-}
-
-static void bm_copy_vb(benchmark::State& state, bool aligned) {
-  auto n = state.range();
-  std::vector<bool> in(n, true);
-  std::vector<bool> out(aligned ? n : n + 8);
-  benchmark::DoNotOptimize(&in);
-  auto beg = in.begin();
-  auto end = in.end();
-  auto dst = aligned ? out.begin() : out.begin() + 4;
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(std::copy(beg, end, dst));
-    benchmark::DoNotOptimize(&out);
-  }
-}
-
-static void bm_copy_n_vb(benchmark::State& state, bool aligned) {
-  auto n = state.range();
-  std::vector<bool> in(n, true);
-  std::vector<bool> out(aligned ? n : n + 8);
-  benchmark::DoNotOptimize(&in);
-  auto src = in.begin();
-  auto dst = aligned ? out.begin() : out.begin() + 4;
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(std::copy_n(src, n, dst));
-    benchmark::DoNotOptimize(&out);
-  }
-}
-
-static void bm_ranges_copy_vb_aligned(benchmark::State& state) { bm_ranges_copy_vb(state, true); }
-static void bm_ranges_copy_vb_unaligned(benchmark::State& state) { bm_ranges_copy_vb(state, false); }
-static void bm_ranges_copy_n_vb_aligned(benchmark::State& state) { bm_ranges_copy_n_vb(state, true); }
-static void bm_ranges_copy_n_vb_unaligned(benchmark::State& state) { bm_ranges_copy_n_vb(state, false); }
-
-static void bm_copy_vb_aligned(benchmark::State& state) { bm_copy_vb(state, true); }
-static void bm_copy_vb_unaligned(benchmark::State& state) { bm_copy_vb(state, false); }
-static void bm_copy_n_vb_aligned(benchmark::State& state) { bm_copy_n_vb(state, true); }
-static void bm_copy_n_vb_unaligned(benchmark::State& state) { bm_copy_n_vb(state, false); }
-
-// Test std::ranges::copy for vector<bool>::iterator
-BENCHMARK(bm_ranges_copy_vb_aligned)->Range(8, 1 << 16)->DenseRange(102400, 204800, 4096);
-BENCHMARK(bm_ranges_copy_n_vb_aligned)->Range(8, 1 << 20);
-BENCHMARK(bm_ranges_copy_vb_unaligned)->Range(8, 1 << 20);
-BENCHMARK(bm_ranges_copy_n_vb_unaligned)->Range(8, 1 << 20);
-
-// Test std::copy for vector<bool>::iterator
-BENCHMARK(bm_copy_vb_aligned)->Range(8, 1 << 20);
-BENCHMARK(bm_copy_n_vb_aligned)->Range(8, 1 << 20);
-BENCHMARK(bm_copy_vb_unaligned)->Range(8, 1 << 20);
-BENCHMARK(bm_copy_n_vb_unaligned)->Range(8, 1 << 20);
-
-BENCHMARK_MAIN();
diff --git a/libcxx/test/benchmarks/algorithms/copy_backward.bench.cpp b/libcxx/test/benchmarks/algorithms/copy_backward.bench.cpp
deleted file mode 100644
index c943d9a874b49..0000000000000
--- a/libcxx/test/benchmarks/algorithms/copy_backward.bench.cpp
+++ /dev/null
@@ -1,55 +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_ranges_copy_backward_vb(benchmark::State& state, bool aligned) {
-  auto n = state.range();
-  std::vector<bool> in(n, true);
-  std::vector<bool> out(aligned ? n : n + 8);
-  benchmark::DoNotOptimize(&in);
-  auto dst = aligned ? out.end() : out.end() - 4;
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(std::ranges::copy_backward(in, dst));
-    benchmark::DoNotOptimize(&out);
-  }
-}
-
-static void bm_copy_backward_vb(benchmark::State& state, bool aligned) {
-  auto n = state.range();
-  std::vector<bool> in(n, true);
-  std::vector<bool> out(aligned ? n : n + 8);
-  benchmark::DoNotOptimize(&in);
-  auto beg = in.begin();
-  auto end = in.end();
-  auto dst = aligned ? out.end() : out.end() - 4;
-  for (auto _ : state) {
-    benchmark::DoNotOptimize(std::copy_backward(beg, end, dst));
-    benchmark::DoNotOptimize(&out);
-  }
-}
-
-static void bm_ranges_copy_backward_vb_aligned(benchmark::State& state) { bm_ranges_copy_backward_vb(state, true); }
-static void bm_ranges_copy_backward_vb_unaligned(benchmark::State& state) { bm_ranges_copy_backward_vb(state, false); }
-
-static void bm_copy_backward_vb_aligned(benchmark::State& state) { bm_copy_backward_vb(state, true); }
-static void bm_copy_backward_vb_unaligned(benchmark::State& state) { bm_copy_backward_vb(state, false); }
-
-// Test std::ranges::copy_backward for vector<bool>::iterator
-BENCHMARK(bm_ranges_copy_backward_vb_aligned)->Range(8, 1 << 16)->DenseRange(102400, 204800, 4096);
-BENCHMARK(bm_ranges_copy_backward_vb_unaligned)->Range(8, 1 << 20);
-
-// Test std::copy_backward for vector<bool>::iterator
-BENCHMARK(bm_copy_backward_vb_aligned)->Range(8, 1 << 20);
-BENCHMARK(bm_copy_backward_vb_unaligned)->Range(8, 1 << 20);
-
-BENCHMARK_MAIN();
diff --git a/libcxx/test/benchmarks/algorithms/modifying/copy.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/copy.bench.cpp
new file mode 100644
index 0000000000000..dea1e9f5d7c34
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/modifying/copy.bench.cpp
@@ -0,0 +1,84 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <deque>
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+template <class Container, class Operation>
+void bm_general(std::string operation_name, Operation copy) {
+  auto bench = [copy](auto& st) {
+    auto 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) {
+      auto result = copy(c.begin(), c.end(), out.begin());
+      benchmark::DoNotOptimize(result);
+      benchmark::DoNotOptimize(out);
+      benchmark::DoNotOptimize(c);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Range(8, 1 << 20);
+}
+
+template <bool Aligned, class Operation>
+static void bm_vector_bool(std::string operation_name, Operation copy) {
+  auto bench = [copy](auto& st) {
+    auto n = st.range();
+    std::vector<bool> in(n, true);
+    std::vector<bool> out(Aligned ? n : n + 8);
+    benchmark::DoNotOptimize(&in);
+    auto first = in.begin();
+    auto last  = in.end();
+    auto dst   = Aligned ? out.begin() : out.begin() + 4;
+    for ([[maybe_unused]] auto _ : st) {
+      auto result = copy(first, last, dst);
+      benchmark::DoNotOptimize(result);
+      benchmark::DoNotOptimize(out);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Range(64, 1 << 20);
+}
+
+int main(int argc, char** argv) {
+  auto std_copy    = [](auto first, auto last, auto out) { return std::copy(first, last, out); };
+  auto ranges_copy = [](auto first, auto last, auto out) { return std::ranges::copy(first, last, out); };
+
+  // std::copy
+  bm_general<std::vector<int>>("std::copy(vector<int>)", std_copy);
+  bm_general<std::deque<int>>("std::copy(deque<int>)", std_copy);
+  bm_general<std::list<int>>("std::copy(list<int>)", std_copy);
+  bm_vector_bool<true>("std::copy(vector<bool>) (aligned)", std_copy);
+  bm_vector_bool<false>("std::copy(vector<bool>) (unaligned)", std_copy);
+
+  // ranges::copy
+  bm_general<std::vector<int>>("ranges::copy(vector<int>)", ranges_copy);
+  bm_general<std::deque<int>>("ranges::copy(deque<int>)", ranges_copy);
+  bm_general<std::list<int>>("ranges::copy(list<int>)", ranges_copy);
+  bm_vector_bool<true>("ranges::copy(vector<bool>) (aligned)", ranges_copy);
+  bm_vector_bool<false>("ranges::copy(vector<bool>) (unaligned)", ranges_copy);
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/modifying/copy_backward.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/copy_backward.bench.cpp
new file mode 100644
index 0000000000000..6f9360533db28
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/modifying/copy_backward.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 <deque>
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+template <class Container, class Operation>
+void bm_general(std::string operation_name, Operation copy_backward) {
+  auto bench = [copy_backward](auto& st) {
+    auto 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) {
+      auto result = copy_backward(c.begin(), c.end(), out.end());
+      benchmark::DoNotOptimize(result);
+      benchmark::DoNotOptimize(out);
+      benchmark::DoNotOptimize(c);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Range(8, 1 << 20);
+}
+
+template <bool Aligned, class Operation>
+static void bm_vector_bool(std::string operation_name, Operation copy_backward) {
+  auto bench = [copy_backward](auto& st) {
+    auto n = st.range();
+    std::vector<bool> in(n, true);
+    std::vector<bool> out(Aligned ? n : n + 8);
+    benchmark::DoNotOptimize(&in);
+    auto first = in.begin();
+    auto last  = in.end();
+    auto dst   = Aligned ? out.end() : out.end() - 4;
+    for ([[maybe_unused]] auto _ : st) {
+      auto result = copy_backward(first, last, dst);
+      benchmark::DoNotOptimize(result);
+      benchmark::DoNotOptimize(out);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Range(64, 1 << 20);
+}
+
+int main(int argc, char** argv) {
+  auto std_copy_backward    = [](auto first, auto last, auto out) { return std::copy_backward(first, last, out); };
+  auto ranges_copy_backward = [](auto first, auto last, auto out) {
+    return std::ranges::copy_backward(first, last, out);
+  };
+
+  // std::copy
+  bm_general<std::vector<int>>("std::copy_backward(vector<int>)", std_copy_backward);
+  bm_general<std::deque<int>>("std::copy_backward(deque<int>)", std_copy_backward);
+  bm_general<std::list<int>>("std::copy_backward(list<int>)", std_copy_backward);
+  bm_vector_bool<true>("std::copy_backward(vector<bool>) (aligned)", std_copy_backward);
+  bm_vector_bool<false>("std::copy_backward(vector<bool>) (unaligned)", std_copy_backward);
+
+  // ranges::copy
+  bm_general<std::vector<int>>("ranges::copy_backward(vector<int>)", ranges_copy_backward);
+  bm_general<std::deque<int>>("ranges::copy_backward(deque<int>)", ranges_copy_backward);
+  bm_general<std::list<int>>("ranges::copy_backward(list<int>)", ranges_copy_backward);
+  bm_vector_bool<true>("ranges::copy_backward(vector<bool>) (aligned)", ranges_copy_backward);
+  bm_vector_bool<false>("ranges::copy_backward(vector<bool>) (unaligned)", ranges_copy_backward);
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/modifying/copy_if.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/copy_if.bench.cpp
new file mode 100644
index 0000000000000..8a298c703e4e8
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/modifying/copy_if.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 <deque>
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+// Benchmark copying one out of two element, in alternance. This is basically
+// the worst case for this algorithm, I don't think there are many optimizations
+// that can be applied in this case.
+template <class Container, class Operation>
+void bm_copy_every_other_element(std::string operation_name, Operation copy_if) {
+  auto bench = [copy_if](auto& st) {
+    auto 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) {
+      bool do_copy = false;
+      auto pred    = [&do_copy](auto& element) {
+        benchmark::DoNotOptimize(element);
+        do_copy = !do_copy;
+        return do_copy;
+      };
+      auto result = copy_if(c.begin(), c.end(), out.begin(), pred);
+      benchmark::DoNotOptimize(result);
+      benchmark::DoNotOptimize(out);
+      benchmark::DoNotOptimize(c);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Range(8, 1 << 20);
+}
+
+// Copy the full range.
+template <class Container, class Operation>
+void bm_copy_entire_range(std::string operation_name, Operation copy_if) {
+  auto bench = [copy_if](auto& st) {
+    auto 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) {
+      auto pred = [](auto& element) {
+        benchmark::DoNotOptimize(element);
+        return true;
+      };
+      auto result = copy_if(c.begin(), c.end(), out.begin(), pred);
+      benchmark::DoNotOptimize(result);
+      benchmark::DoNotOptimize(out);
+      benchmark::DoNotOptimize(c);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Range(8, 1 << 20);
+}
+
+int main(int argc, char** argv) {
+  auto std_copy_if    = [](auto first, auto last, auto out, auto pred) { return std::copy_if(first, last, out, pred); };
+  auto ranges_copy_if = [](auto first, auto last, auto out, auto pred) {
+    return std::ranges::copy_if(first, last, out, pred);
+  };
+
+  // std::copy_if
+  bm_copy_every_other_element<std::vector<int>>("std::copy_if(vector<int>) (every other)", std_copy_if);
+  bm_copy_every_other_element<std::deque<int>>("std::copy_if(deque<int>) (every other)", std_copy_if);
+  bm_copy_every_other_element<std::list<int>>("std::copy_if(list<int>) (every other)", std_copy_if);
+
+  bm_copy_entire_range<std::vector<int>>("std::copy_if(vector<int>) (entire range)", std_copy_if);
+  bm_copy_entire_range<std::deque<int>>("std::copy_if(deque<int>) (entire range)", std_copy_if);
+  bm_copy_entire_range<std::list<int>>("std::copy_if(list<int>) (entire range)", std_copy_if);
+
+  // ranges::copy
+  bm_copy_every_other_element<std::vector<int>>("ranges::copy_if(vector<int>) (every other)", ranges_copy_if);
+  bm_copy_every_other_element<std::deque<int>>("ranges::copy_if(deque<int>) (every other)", ranges_copy_if);
+  bm_copy_every_other_element<std::list<int>>("ranges::copy_if(list<int>) (every other)", ranges_copy_if);
+
+  bm_copy_entire_range<std::vector<int>>("ranges::copy_if(vector<int>) (entire range)", ranges_copy_if);
+  bm_copy_entire_range<std::deque<int>>("ranges::copy_if(deque<int>) (entire range)", ranges_copy_if);
+  bm_copy_entire_range<std::list<int>>("ranges::copy_if(list<int>) (entire range)", ranges_copy_if);
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}
diff --git a/libcxx/test/benchmarks/algorithms/modifying/copy_n.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/copy_n.bench.cpp
new file mode 100644
index 0000000000000..f4eb98f91df1c
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/modifying/copy_n.bench.cpp
@@ -0,0 +1,83 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <deque>
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+template <class Container, class Operation>
+void bm_general(std::string operation_name, Operation copy_n) {
+  auto bench = [copy_n](auto& st) {
+    auto 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) {
+      auto result = copy_n(c.begin(), size, out.begin());
+      benchmark::DoNotOptimize(result);
+      benchmark::DoNotOptimize(out);
+      benchmark::DoNotOptimize(c);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Range(8, 1 << 20);
+}
+
+template <bool Aligned, class Operation>
+static void bm_vector_bool(std::string operation_name, Operation copy_n) {
+  auto bench = [copy_n](auto& st) {
+    auto n = st.range();
+    std::vector<bool> in(n, true);
+    std::vector<bool> out(Aligned ? n : n + 8);
+    benchmark::DoNotOptimize(&in);
+    auto first = in.begin();
+    auto dst   = Aligned ? out.begin() : out.begin() + 4;
+    for ([[maybe_unused]] auto _ : st) {
+      auto result = copy_n(first, n, dst);
+      benchmark::DoNotOptimize(result);
+      benchmark::DoNotOptimize(out);
+      benchmark::ClobberMemory();
+    }
+  };
+  benchmark::RegisterBenchmark(operation_name, bench)->Range(64, 1 << 20);
+}
+
+int main(int argc, char** argv) {
+  auto std_copy_n    = [](auto first, auto n, auto out) { return std::copy_n(first, n, out); };
+  auto ranges_copy_n = [](auto first, auto n, auto out) { return std::ranges::copy_n(first, n, out); };
+
+  // std::copy_n
+  bm_general<std::vector<int>>("std::copy_n(vector<int>)", std_copy_n);
+  bm_general<std::deque<int>>("std::copy_n(deque<int>)", std_copy_n);
+  bm_general<std::list<int>>("std::copy_n(list<int>)", std_copy_n);
+  bm_vector_bool<true>("std::copy_n(vector<bool>) (aligned)", std_copy_n);
+  bm_vector_bool<false>("std::copy_n(vector<bool>) (unaligned)", std_copy_n);
+
+  // ranges::copy_n
+  bm_general<std::vector<int>>("ranges::copy_n(vector<int>)", ranges_copy_n);
+  bm_general<std::deque<int>>("ranges::copy_n(deque<int>)", ranges_copy_n);
+  bm_general<std::list<int>>("ranges::copy_n(list<int>)", ranges_copy_n);
+  bm_vector_bool<true>("ranges::copy_n(vector<bool>) (aligned)", ranges_copy_n);
+  bm_vector_bool<false>("ranges::copy_n(vector<bool>) (unaligned)", ranges_copy_n);
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}

>From d6cdeb9220ee533d1c224be708179b3a6fc27265 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Sun, 16 Feb 2025 00:49:16 +0100
Subject: [PATCH 2/3] Guard for lack of output_iterator-nesss

---
 libcxx/test/benchmarks/algorithms/modifying/copy.bench.cpp     | 3 +++
 .../benchmarks/algorithms/modifying/copy_backward.bench.cpp    | 3 +++
 libcxx/test/benchmarks/algorithms/modifying/copy_n.bench.cpp   | 3 +++
 3 files changed, 9 insertions(+)

diff --git a/libcxx/test/benchmarks/algorithms/modifying/copy.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/copy.bench.cpp
index dea1e9f5d7c34..a3db7dce1bde5 100644
--- a/libcxx/test/benchmarks/algorithms/modifying/copy.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/modifying/copy.bench.cpp
@@ -17,6 +17,7 @@
 
 #include "benchmark/benchmark.h"
 #include "../../GenerateInput.h"
+#include "test_macros.h"
 
 template <class Container, class Operation>
 void bm_general(std::string operation_name, Operation copy) {
@@ -74,8 +75,10 @@ int main(int argc, char** argv) {
   bm_general<std::vector<int>>("ranges::copy(vector<int>)", ranges_copy);
   bm_general<std::deque<int>>("ranges::copy(deque<int>)", ranges_copy);
   bm_general<std::list<int>>("ranges::copy(list<int>)", ranges_copy);
+#if TEST_STD_VER >= 23 // vector<bool>::iterator is not an output_iterator before C++23
   bm_vector_bool<true>("ranges::copy(vector<bool>) (aligned)", ranges_copy);
   bm_vector_bool<false>("ranges::copy(vector<bool>) (unaligned)", ranges_copy);
+#endif
 
   benchmark::Initialize(&argc, argv);
   benchmark::RunSpecifiedBenchmarks();
diff --git a/libcxx/test/benchmarks/algorithms/modifying/copy_backward.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/copy_backward.bench.cpp
index 6f9360533db28..b9adb24ad5964 100644
--- a/libcxx/test/benchmarks/algorithms/modifying/copy_backward.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/modifying/copy_backward.bench.cpp
@@ -17,6 +17,7 @@
 
 #include "benchmark/benchmark.h"
 #include "../../GenerateInput.h"
+#include "test_macros.h"
 
 template <class Container, class Operation>
 void bm_general(std::string operation_name, Operation copy_backward) {
@@ -76,8 +77,10 @@ int main(int argc, char** argv) {
   bm_general<std::vector<int>>("ranges::copy_backward(vector<int>)", ranges_copy_backward);
   bm_general<std::deque<int>>("ranges::copy_backward(deque<int>)", ranges_copy_backward);
   bm_general<std::list<int>>("ranges::copy_backward(list<int>)", ranges_copy_backward);
+#if TEST_STD_VER >= 23 // vector<bool>::iterator is not an output_iterator before C++23
   bm_vector_bool<true>("ranges::copy_backward(vector<bool>) (aligned)", ranges_copy_backward);
   bm_vector_bool<false>("ranges::copy_backward(vector<bool>) (unaligned)", ranges_copy_backward);
+#endif
 
   benchmark::Initialize(&argc, argv);
   benchmark::RunSpecifiedBenchmarks();
diff --git a/libcxx/test/benchmarks/algorithms/modifying/copy_n.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/copy_n.bench.cpp
index f4eb98f91df1c..c95dd327fc01c 100644
--- a/libcxx/test/benchmarks/algorithms/modifying/copy_n.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/modifying/copy_n.bench.cpp
@@ -17,6 +17,7 @@
 
 #include "benchmark/benchmark.h"
 #include "../../GenerateInput.h"
+#include "test_macros.h"
 
 template <class Container, class Operation>
 void bm_general(std::string operation_name, Operation copy_n) {
@@ -73,8 +74,10 @@ int main(int argc, char** argv) {
   bm_general<std::vector<int>>("ranges::copy_n(vector<int>)", ranges_copy_n);
   bm_general<std::deque<int>>("ranges::copy_n(deque<int>)", ranges_copy_n);
   bm_general<std::list<int>>("ranges::copy_n(list<int>)", ranges_copy_n);
+#if TEST_STD_VER >= 23 // vector<bool>::iterator is not an output_iterator before C++23
   bm_vector_bool<true>("ranges::copy_n(vector<bool>) (aligned)", ranges_copy_n);
   bm_vector_bool<false>("ranges::copy_n(vector<bool>) (unaligned)", ranges_copy_n);
+#endif
 
   benchmark::Initialize(&argc, argv);
   benchmark::RunSpecifiedBenchmarks();

>From d2600410c7048378500c6db37c346621e99a521a Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Tue, 18 Feb 2025 15:30:42 -0500
Subject: [PATCH 3/3] Use ranges CPOs directly

---
 libcxx/test/benchmarks/algorithms/modifying/copy.bench.cpp    | 2 +-
 .../benchmarks/algorithms/modifying/copy_backward.bench.cpp   | 4 +---
 libcxx/test/benchmarks/algorithms/modifying/copy_if.bench.cpp | 4 +---
 libcxx/test/benchmarks/algorithms/modifying/copy_n.bench.cpp  | 2 +-
 4 files changed, 4 insertions(+), 8 deletions(-)

diff --git a/libcxx/test/benchmarks/algorithms/modifying/copy.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/copy.bench.cpp
index a3db7dce1bde5..3daebcc0240f6 100644
--- a/libcxx/test/benchmarks/algorithms/modifying/copy.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/modifying/copy.bench.cpp
@@ -62,7 +62,7 @@ static void bm_vector_bool(std::string operation_name, Operation copy) {
 
 int main(int argc, char** argv) {
   auto std_copy    = [](auto first, auto last, auto out) { return std::copy(first, last, out); };
-  auto ranges_copy = [](auto first, auto last, auto out) { return std::ranges::copy(first, last, out); };
+  auto ranges_copy = std::ranges::copy;
 
   // std::copy
   bm_general<std::vector<int>>("std::copy(vector<int>)", std_copy);
diff --git a/libcxx/test/benchmarks/algorithms/modifying/copy_backward.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/copy_backward.bench.cpp
index b9adb24ad5964..ca5ea7fd279bd 100644
--- a/libcxx/test/benchmarks/algorithms/modifying/copy_backward.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/modifying/copy_backward.bench.cpp
@@ -62,9 +62,7 @@ static void bm_vector_bool(std::string operation_name, Operation copy_backward)
 
 int main(int argc, char** argv) {
   auto std_copy_backward    = [](auto first, auto last, auto out) { return std::copy_backward(first, last, out); };
-  auto ranges_copy_backward = [](auto first, auto last, auto out) {
-    return std::ranges::copy_backward(first, last, out);
-  };
+  auto ranges_copy_backward = std::ranges::copy_backward;
 
   // std::copy
   bm_general<std::vector<int>>("std::copy_backward(vector<int>)", std_copy_backward);
diff --git a/libcxx/test/benchmarks/algorithms/modifying/copy_if.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/copy_if.bench.cpp
index 8a298c703e4e8..e9cb9ab08ce1a 100644
--- a/libcxx/test/benchmarks/algorithms/modifying/copy_if.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/modifying/copy_if.bench.cpp
@@ -76,9 +76,7 @@ void bm_copy_entire_range(std::string operation_name, Operation copy_if) {
 
 int main(int argc, char** argv) {
   auto std_copy_if    = [](auto first, auto last, auto out, auto pred) { return std::copy_if(first, last, out, pred); };
-  auto ranges_copy_if = [](auto first, auto last, auto out, auto pred) {
-    return std::ranges::copy_if(first, last, out, pred);
-  };
+  auto ranges_copy_if = std::ranges::copy_if;
 
   // std::copy_if
   bm_copy_every_other_element<std::vector<int>>("std::copy_if(vector<int>) (every other)", std_copy_if);
diff --git a/libcxx/test/benchmarks/algorithms/modifying/copy_n.bench.cpp b/libcxx/test/benchmarks/algorithms/modifying/copy_n.bench.cpp
index c95dd327fc01c..7a047c86369ab 100644
--- a/libcxx/test/benchmarks/algorithms/modifying/copy_n.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/modifying/copy_n.bench.cpp
@@ -61,7 +61,7 @@ static void bm_vector_bool(std::string operation_name, Operation copy_n) {
 
 int main(int argc, char** argv) {
   auto std_copy_n    = [](auto first, auto n, auto out) { return std::copy_n(first, n, out); };
-  auto ranges_copy_n = [](auto first, auto n, auto out) { return std::ranges::copy_n(first, n, out); };
+  auto ranges_copy_n = std::ranges::copy_n;
 
   // std::copy_n
   bm_general<std::vector<int>>("std::copy_n(vector<int>)", std_copy_n);



More information about the libcxx-commits mailing list