[libcxx-commits] [libcxx] 8c43588 - [libc++] Refactor and add benchmark coverage for [alg.sort] (#128236)

via libcxx-commits libcxx-commits at lists.llvm.org
Wed May 14 11:52:21 PDT 2025


Author: Louis Dionne
Date: 2025-05-14T14:52:17-04:00
New Revision: 8c435886aaee7619314edac9b142ef6fb2dd935f

URL: https://github.com/llvm/llvm-project/commit/8c435886aaee7619314edac9b142ef6fb2dd935f
DIFF: https://github.com/llvm/llvm-project/commit/8c435886aaee7619314edac9b142ef6fb2dd935f.diff

LOG: [libc++] Refactor and add benchmark coverage for [alg.sort] (#128236)

This patch adds missing benchmark coverage for partial_sort,
partial_sort_copy, is_sorted and is_sorted_until.

It also refactors the existing benchmarks for sort and stable_sort to
follow the consistent style of the new algorithm benchmarks. However,
these benchmarks were notoriously slow to run since they tested multiple
data patterns on multiple data types. To try to alleviate this, I
reduced the benchmarks to only run on integral types and on a single
non-integral type, which should faithfully show how the algorithm
behaves for anything non-integral. However, this is technically a
reduction in coverage.

Added: 
    libcxx/test/benchmarks/algorithms/sorting/common.h
    libcxx/test/benchmarks/algorithms/sorting/is_sorted.bench.cpp
    libcxx/test/benchmarks/algorithms/sorting/is_sorted_until.bench.cpp
    libcxx/test/benchmarks/algorithms/sorting/partial_sort.bench.cpp
    libcxx/test/benchmarks/algorithms/sorting/partial_sort_copy.bench.cpp
    libcxx/test/benchmarks/algorithms/sorting/sort.bench.cpp
    libcxx/test/benchmarks/algorithms/sorting/stable_sort.bench.cpp

Modified: 
    

Removed: 
    libcxx/test/benchmarks/algorithms/pstl.stable_sort.bench.cpp
    libcxx/test/benchmarks/algorithms/ranges_sort.bench.cpp
    libcxx/test/benchmarks/algorithms/ranges_stable_sort.bench.cpp
    libcxx/test/benchmarks/algorithms/sort.bench.cpp
    libcxx/test/benchmarks/algorithms/stable_sort.bench.cpp


################################################################################
diff  --git a/libcxx/test/benchmarks/algorithms/pstl.stable_sort.bench.cpp b/libcxx/test/benchmarks/algorithms/pstl.stable_sort.bench.cpp
deleted file mode 100644
index a385185ec7fe5..0000000000000
--- a/libcxx/test/benchmarks/algorithms/pstl.stable_sort.bench.cpp
+++ /dev/null
@@ -1,42 +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
-// UNSUPPORTED: libcpp-has-no-incomplete-pstl
-
-#include <algorithm>
-#include <execution>
-
-#include "common.h"
-
-namespace {
-template <class ValueType, class Order>
-struct StableSort {
-  size_t Quantity;
-
-  void run(benchmark::State& state) const {
-    runOpOnCopies<ValueType>(state, Quantity, Order(), BatchSize::CountBatch, [](auto& Copy) {
-      std::stable_sort(std::execution::par, Copy.begin(), Copy.end());
-    });
-  }
-
-  bool skip() const { return Order() == ::Order::Heap; }
-
-  std::string name() const {
-    return "BM_pstl_stable_sort" + ValueType::name() + Order::name() + "/" + std::to_string(Quantity);
-  }
-};
-} // namespace
-
-int main(int argc, char** argv) {
-  benchmark::Initialize(&argc, argv);
-  if (benchmark::ReportUnrecognizedArguments(argc, argv))
-    return 1;
-  makeCartesianProductBenchmark<StableSort, AllValueTypes, AllOrders>(Quantities);
-  benchmark::RunSpecifiedBenchmarks();
-}

diff  --git a/libcxx/test/benchmarks/algorithms/ranges_sort.bench.cpp b/libcxx/test/benchmarks/algorithms/ranges_sort.bench.cpp
deleted file mode 100644
index d145a159a21fd..0000000000000
--- a/libcxx/test/benchmarks/algorithms/ranges_sort.bench.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-// UNSUPPORTED: c++03, c++11, c++14, c++17
-
-#include <algorithm>
-
-#include "common.h"
-
-namespace {
-template <class ValueType, class Order>
-struct Sort {
-  size_t Quantity;
-
-  void run(benchmark::State& state) const {
-    runOpOnCopies<ValueType>(state, Quantity, Order(), BatchSize::CountElements, [](auto& Copy) {
-      std::ranges::sort(Copy);
-    });
-  }
-
-  bool skip() const { return Order() == ::Order::Heap; }
-
-  std::string name() const {
-    return "BM_RangesSort" + ValueType::name() + Order::name() + "_" + std::to_string(Quantity);
-  }
-};
-} // namespace
-
-int main(int argc, char** argv) {
-  benchmark::Initialize(&argc, argv);
-  if (benchmark::ReportUnrecognizedArguments(argc, argv))
-    return 1;
-  makeCartesianProductBenchmark<Sort, AllValueTypes, AllOrders>(Quantities);
-  benchmark::RunSpecifiedBenchmarks();
-}

diff  --git a/libcxx/test/benchmarks/algorithms/ranges_stable_sort.bench.cpp b/libcxx/test/benchmarks/algorithms/ranges_stable_sort.bench.cpp
deleted file mode 100644
index acc2f3f755fb8..0000000000000
--- a/libcxx/test/benchmarks/algorithms/ranges_stable_sort.bench.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-// UNSUPPORTED: c++03, c++11, c++14, c++17
-
-#include <algorithm>
-
-#include "common.h"
-
-namespace {
-template <class ValueType, class Order>
-struct StableSort {
-  size_t Quantity;
-
-  void run(benchmark::State& state) const {
-    runOpOnCopies<ValueType>(state, Quantity, Order(), BatchSize::CountElements, [](auto& Copy) {
-      std::ranges::stable_sort(Copy);
-    });
-  }
-
-  bool skip() const { return Order() == ::Order::Heap; }
-
-  std::string name() const {
-    return "BM_RangesStableSort" + ValueType::name() + Order::name() + "_" + std::to_string(Quantity);
-  }
-};
-} // namespace
-
-int main(int argc, char** argv) {
-  benchmark::Initialize(&argc, argv);
-  if (benchmark::ReportUnrecognizedArguments(argc, argv))
-    return 1;
-  makeCartesianProductBenchmark<StableSort, AllValueTypes, AllOrders>(Quantities);
-  benchmark::RunSpecifiedBenchmarks();
-}

diff  --git a/libcxx/test/benchmarks/algorithms/sort.bench.cpp b/libcxx/test/benchmarks/algorithms/sort.bench.cpp
deleted file mode 100644
index 7f3ce6ff7a07e..0000000000000
--- a/libcxx/test/benchmarks/algorithms/sort.bench.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-// UNSUPPORTED: c++03, c++11, c++14, c++17
-
-#include <algorithm>
-
-#include "common.h"
-
-namespace {
-template <class ValueType, class Order>
-struct Sort {
-  size_t Quantity;
-
-  void run(benchmark::State& state) const {
-    runOpOnCopies<ValueType>(state, Quantity, Order(), BatchSize::CountElements, [](auto& Copy) {
-      std::sort(Copy.begin(), Copy.end());
-    });
-  }
-
-  bool skip() const { return Order() == ::Order::Heap; }
-
-  std::string name() const { return "BM_Sort" + ValueType::name() + Order::name() + "_" + std::to_string(Quantity); };
-};
-} // namespace
-
-int main(int argc, char** argv) {
-  benchmark::Initialize(&argc, argv);
-  if (benchmark::ReportUnrecognizedArguments(argc, argv))
-    return 1;
-  makeCartesianProductBenchmark<Sort, AllValueTypes, AllOrders>(Quantities);
-  benchmark::RunSpecifiedBenchmarks();
-}

diff  --git a/libcxx/test/benchmarks/algorithms/sorting/common.h b/libcxx/test/benchmarks/algorithms/sorting/common.h
new file mode 100644
index 0000000000000..8195e9a2dc8d0
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/sorting/common.h
@@ -0,0 +1,141 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LIBCXX_TEST_BENCHMARKS_ALGORITHMS_SORTING_COMMON_H
+#define LIBCXX_TEST_BENCHMARKS_ALGORITHMS_SORTING_COMMON_H
+
+#include <algorithm>
+#include <cstddef>
+#include <numeric>
+#include <random>
+#include <type_traits>
+#include <vector>
+
+namespace support {
+
+// This function creates a vector with N int-like values.
+//
+// These values are arranged in such a way that they would invoke O(N^2)
+// behavior on any quick sort implementation that satisifies certain conditions.
+// Details are available in the following paper:
+//
+//  "A Killer Adversary for Quicksort", M. D. McIlroy, Software-Practice &
+//  Experience Volume 29 Issue 4 April 10, 1999 pp 341-344.
+//  https://dl.acm.org/doi/10.5555/311868.311871.
+template <class T>
+std::vector<T> quicksort_adversarial_data(std::size_t n) {
+  static_assert(std::is_integral_v<T>);
+  assert(n > 0);
+
+  // If an element is equal to gas, it indicates that the value of the element
+  // is still to be decided and may change over the course of time.
+  T gas = n - 1;
+
+  std::vector<T> v;
+  v.resize(n);
+  for (unsigned int i = 0; i < n; ++i) {
+    v[i] = gas;
+  }
+  // Candidate for the pivot position.
+  int candidate = 0;
+  int nsolid    = 0;
+  // Populate all positions in the generated input to gas.
+  std::vector<int> ascending_values(v.size());
+
+  // Fill up with ascending values from 0 to v.size()-1.  These will act as
+  // indices into v.
+  std::iota(ascending_values.begin(), ascending_values.end(), 0);
+  std::sort(ascending_values.begin(), ascending_values.end(), [&](int x, int y) {
+    if (v[x] == gas && v[y] == gas) {
+      // We are comparing two inputs whose value is still to be decided.
+      if (x == candidate) {
+        v[x] = nsolid++;
+      } else {
+        v[y] = nsolid++;
+      }
+    }
+    if (v[x] == gas) {
+      candidate = x;
+    } else if (v[y] == gas) {
+      candidate = y;
+    }
+    return v[x] < v[y];
+  });
+  return v;
+}
+
+// ascending sorted values
+template <class T>
+std::vector<T> ascending_sorted_data(std::size_t n) {
+  std::vector<T> v(n);
+  std::iota(v.begin(), v.end(), 0);
+  return v;
+}
+
+// descending sorted values
+template <class T>
+std::vector<T> descending_sorted_data(std::size_t n) {
+  std::vector<T> v(n);
+  std::iota(v.begin(), v.end(), 0);
+  std::reverse(v.begin(), v.end());
+  return v;
+}
+
+// pipe-organ pattern
+template <class T>
+std::vector<T> pipe_organ_data(std::size_t n) {
+  std::vector<T> v(n);
+  std::iota(v.begin(), v.end(), 0);
+  auto half = v.begin() + v.size() / 2;
+  std::reverse(half, v.end());
+  return v;
+}
+
+// heap pattern
+template <class T>
+std::vector<T> heap_data(std::size_t n) {
+  std::vector<T> v(n);
+  std::iota(v.begin(), v.end(), 0);
+  std::make_heap(v.begin(), v.end());
+  return v;
+}
+
+// shuffled randomly
+template <class T>
+std::vector<T> shuffled_data(std::size_t n) {
+  std::vector<T> v(n);
+  std::iota(v.begin(), v.end(), 0);
+  std::mt19937 rng;
+  std::shuffle(v.begin(), v.end(), rng);
+  return v;
+}
+
+// single element in the whole sequence
+template <class T>
+std::vector<T> single_element_data(std::size_t n) {
+  std::vector<T> v(n);
+  return v;
+}
+
+struct NonIntegral {
+  NonIntegral() : value_(0) {}
+  NonIntegral(int i) : value_(i) {}
+  friend auto operator<(NonIntegral const& a, NonIntegral const& b) { return a.value_ < b.value_; }
+  friend auto operator>(NonIntegral const& a, NonIntegral const& b) { return a.value_ > b.value_; }
+  friend auto operator<=(NonIntegral const& a, NonIntegral const& b) { return a.value_ <= b.value_; }
+  friend auto operator>=(NonIntegral const& a, NonIntegral const& b) { return a.value_ >= b.value_; }
+  friend auto operator==(NonIntegral const& a, NonIntegral const& b) { return a.value_ == b.value_; }
+  friend auto operator!=(NonIntegral const& a, NonIntegral const& b) { return a.value_ != b.value_; }
+
+private:
+  int value_;
+};
+
+} // namespace support
+
+#endif // LIBCXX_TEST_BENCHMARKS_ALGORITHMS_SORTING_COMMON_H

diff  --git a/libcxx/test/benchmarks/algorithms/sorting/is_sorted.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/is_sorted.bench.cpp
new file mode 100644
index 0000000000000..6e553e93d017c
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/sorting/is_sorted.bench.cpp
@@ -0,0 +1,82 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+int main(int argc, char** argv) {
+  auto std_is_sorted      = [](auto first, auto last) { return std::is_sorted(first, last); };
+  auto std_is_sorted_pred = [](auto first, auto last) {
+    return std::is_sorted(first, last, [](auto x, auto y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x < y;
+    });
+  };
+  auto ranges_is_sorted_pred = [](auto first, auto last) {
+    return std::ranges::is_sorted(first, last, [](auto x, auto y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x < y;
+    });
+  };
+
+  // Benchmark {std,ranges}::is_sorted on a sorted sequence (the worst case).
+  {
+    auto bm = []<class Container>(std::string name, auto is_sorted) {
+      benchmark::RegisterBenchmark(
+          name,
+          [is_sorted](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            std::vector<ValueType> data;
+            std::generate_n(std::back_inserter(data), size, [] { return Generate<ValueType>::random(); });
+            std::sort(data.begin(), data.end());
+
+            Container c(data.begin(), data.end());
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              auto result = is_sorted(c.begin(), c.end());
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(1024)
+          ->Arg(8192);
+    };
+    bm.operator()<std::vector<int>>("std::is_sorted(vector<int>)", std_is_sorted);
+    bm.operator()<std::deque<int>>("std::is_sorted(deque<int>)", std_is_sorted);
+    bm.operator()<std::list<int>>("std::is_sorted(list<int>)", std_is_sorted);
+    bm.operator()<std::vector<int>>("rng::is_sorted(vector<int>)", std::ranges::is_sorted);
+    bm.operator()<std::deque<int>>("rng::is_sorted(deque<int>)", std::ranges::is_sorted);
+    bm.operator()<std::list<int>>("rng::is_sorted(list<int>)", std::ranges::is_sorted);
+
+    bm.operator()<std::vector<int>>("std::is_sorted(vector<int>, pred)", std_is_sorted_pred);
+    bm.operator()<std::deque<int>>("std::is_sorted(deque<int>, pred)", std_is_sorted_pred);
+    bm.operator()<std::list<int>>("std::is_sorted(list<int>, pred)", std_is_sorted_pred);
+    bm.operator()<std::vector<int>>("rng::is_sorted(vector<int>, pred)", ranges_is_sorted_pred);
+    bm.operator()<std::deque<int>>("rng::is_sorted(deque<int>, pred)", ranges_is_sorted_pred);
+    bm.operator()<std::list<int>>("rng::is_sorted(list<int>, pred)", ranges_is_sorted_pred);
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}

diff  --git a/libcxx/test/benchmarks/algorithms/sorting/is_sorted_until.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/is_sorted_until.bench.cpp
new file mode 100644
index 0000000000000..ab11ee35327c7
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/sorting/is_sorted_until.bench.cpp
@@ -0,0 +1,82 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <iterator>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+int main(int argc, char** argv) {
+  auto std_is_sorted_until      = [](auto first, auto last) { return std::is_sorted_until(first, last); };
+  auto std_is_sorted_until_pred = [](auto first, auto last) {
+    return std::is_sorted_until(first, last, [](auto x, auto y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x < y;
+    });
+  };
+  auto ranges_is_sorted_until_pred = [](auto first, auto last) {
+    return std::ranges::is_sorted_until(first, last, [](auto x, auto y) {
+      benchmark::DoNotOptimize(x);
+      benchmark::DoNotOptimize(y);
+      return x < y;
+    });
+  };
+
+  // Benchmark {std,ranges}::is_sorted_until on a sorted sequence (the worst case).
+  {
+    auto bm = []<class Container>(std::string name, auto is_sorted_until) {
+      benchmark::RegisterBenchmark(
+          name,
+          [is_sorted_until](auto& st) {
+            std::size_t const size = st.range(0);
+            using ValueType        = typename Container::value_type;
+            std::vector<ValueType> data;
+            std::generate_n(std::back_inserter(data), size, [] { return Generate<ValueType>::random(); });
+            std::sort(data.begin(), data.end());
+
+            Container c(data.begin(), data.end());
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              auto result = is_sorted_until(c.begin(), c.end());
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(1024)
+          ->Arg(8192);
+    };
+    bm.operator()<std::vector<int>>("std::is_sorted_until(vector<int>)", std_is_sorted_until);
+    bm.operator()<std::deque<int>>("std::is_sorted_until(deque<int>)", std_is_sorted_until);
+    bm.operator()<std::list<int>>("std::is_sorted_until(list<int>)", std_is_sorted_until);
+    bm.operator()<std::vector<int>>("rng::is_sorted_until(vector<int>)", std::ranges::is_sorted_until);
+    bm.operator()<std::deque<int>>("rng::is_sorted_until(deque<int>)", std::ranges::is_sorted_until);
+    bm.operator()<std::list<int>>("rng::is_sorted_until(list<int>)", std::ranges::is_sorted_until);
+
+    bm.operator()<std::vector<int>>("std::is_sorted_until(vector<int>, pred)", std_is_sorted_until_pred);
+    bm.operator()<std::deque<int>>("std::is_sorted_until(deque<int>, pred)", std_is_sorted_until_pred);
+    bm.operator()<std::list<int>>("std::is_sorted_until(list<int>, pred)", std_is_sorted_until_pred);
+    bm.operator()<std::vector<int>>("rng::is_sorted_until(vector<int>, pred)", ranges_is_sorted_until_pred);
+    bm.operator()<std::deque<int>>("rng::is_sorted_until(deque<int>, pred)", ranges_is_sorted_until_pred);
+    bm.operator()<std::list<int>>("rng::is_sorted_until(list<int>, pred)", ranges_is_sorted_until_pred);
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}

diff  --git a/libcxx/test/benchmarks/algorithms/sorting/partial_sort.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/partial_sort.bench.cpp
new file mode 100644
index 0000000000000..7000be66920d0
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/sorting/partial_sort.bench.cpp
@@ -0,0 +1,95 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <array>
+#include <cstddef>
+#include <deque>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "common.h"
+
+int main(int argc, char** argv) {
+  auto std_partial_sort = [](auto first, auto mid, auto last) { return std::partial_sort(first, mid, last); };
+
+  // Benchmark {std,ranges}::partial_sort on various types of data. We always partially sort only
+  // half of the full range.
+  //
+  // We perform this benchmark in a batch because we need to restore the
+  // state of the container after the operation.
+  //
+  // Also note that we intentionally don't benchmark the predicated version of the algorithm
+  // because that makes the benchmark run too slowly.
+  {
+    auto bm = []<class Container>(std::string name, auto partial_sort, auto generate_data) {
+      benchmark::RegisterBenchmark(
+          name,
+          [partial_sort, generate_data](auto& st) {
+            std::size_t const size          = st.range(0);
+            constexpr std::size_t BatchSize = 32;
+            using ValueType                 = typename Container::value_type;
+            std::vector<ValueType> data     = generate_data(size);
+            std::array<Container, BatchSize> c;
+            std::fill_n(c.begin(), BatchSize, Container(data.begin(), data.end()));
+
+            std::size_t const half = size / 2;
+            while (st.KeepRunningBatch(BatchSize)) {
+              for (std::size_t i = 0; i != BatchSize; ++i) {
+                benchmark::DoNotOptimize(c[i]);
+                partial_sort(c[i].begin(), c[i].begin() + half, c[i].end());
+                benchmark::DoNotOptimize(c[i]);
+              }
+
+              st.PauseTiming();
+              for (std::size_t i = 0; i != BatchSize; ++i) {
+                std::copy(data.begin(), data.end(), c[i].begin());
+              }
+              st.ResumeTiming();
+            }
+          })
+          ->Arg(8)
+          ->Arg(1024)
+          ->Arg(8192);
+    };
+
+    auto register_bm = [&](auto generate, std::string variant) {
+      auto gen2 = [generate](auto size) {
+        std::vector<int> data = generate(size);
+        std::vector<support::NonIntegral> real_data(data.begin(), data.end());
+        return real_data;
+      };
+      auto name = [variant](std::string op) { return op + " (" + variant + ")"; };
+      bm.operator()<std::vector<int>>(name("std::partial_sort(vector<int>"), std_partial_sort, generate);
+      bm.operator()<std::vector<support::NonIntegral>>(
+          name("std::partial_sort(vector<NonIntegral>"), std_partial_sort, gen2);
+      bm.operator()<std::deque<int>>(name("std::partial_sort(deque<int>"), std_partial_sort, generate);
+
+      bm.operator()<std::vector<int>>(name("rng::partial_sort(vector<int>"), std::ranges::partial_sort, generate);
+      bm.operator()<std::vector<support::NonIntegral>>(
+          name("rng::partial_sort(vector<NonIntegral>"), std::ranges::partial_sort, gen2);
+      bm.operator()<std::deque<int>>(name("rng::partial_sort(deque<int>"), std::ranges::partial_sort, generate);
+    };
+
+    register_bm(support::quicksort_adversarial_data<int>, "qsort adversarial");
+    register_bm(support::ascending_sorted_data<int>, "ascending");
+    register_bm(support::descending_sorted_data<int>, "descending");
+    register_bm(support::pipe_organ_data<int>, "pipe-organ");
+    register_bm(support::heap_data<int>, "heap");
+    register_bm(support::shuffled_data<int>, "shuffled");
+    register_bm(support::single_element_data<int>, "repeated");
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}

diff  --git a/libcxx/test/benchmarks/algorithms/sorting/partial_sort_copy.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/partial_sort_copy.bench.cpp
new file mode 100644
index 0000000000000..2ebc286b1c03b
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/sorting/partial_sort_copy.bench.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "common.h"
+
+int main(int argc, char** argv) {
+  auto std_partial_sort_copy = [](auto first, auto last, auto dfirst, auto dlast) {
+    return std::partial_sort_copy(first, last, dfirst, dlast);
+  };
+
+  // Benchmark {std,ranges}::partial_sort_copy on various types of data. We always partially
+  // sort only half of the full range.
+  //
+  // Also note that we intentionally don't benchmark the predicated version of the algorithm
+  // because that makes the benchmark run too slowly.
+  {
+    auto bm = []<class Container>(std::string name, auto partial_sort_copy, auto generate_data) {
+      benchmark::RegisterBenchmark(
+          name,
+          [partial_sort_copy, generate_data](auto& st) {
+            std::size_t const size      = st.range(0);
+            using ValueType             = typename Container::value_type;
+            std::vector<ValueType> data = generate_data(size);
+            Container c(data.begin(), data.end());
+            std::vector<ValueType> out(size / 2);
+
+            for ([[maybe_unused]] auto _ : st) {
+              benchmark::DoNotOptimize(c);
+              benchmark::DoNotOptimize(out);
+              auto result = partial_sort_copy(c.begin(), c.end(), out.begin(), out.end());
+              benchmark::DoNotOptimize(result);
+            }
+          })
+          ->Arg(8)
+          ->Arg(1024)
+          ->Arg(8192);
+    };
+
+    auto register_bm = [&](auto generate, std::string variant) {
+      auto gen2 = [generate](auto size) {
+        std::vector<int> data = generate(size);
+        std::vector<support::NonIntegral> real_data(data.begin(), data.end());
+        return real_data;
+      };
+      auto name = [variant](std::string op) { return op + " (" + variant + ")"; };
+      bm.operator()<std::vector<int>>(name("std::partial_sort_copy(vector<int>)"), std_partial_sort_copy, generate);
+      bm.operator()<std::vector<support::NonIntegral>>(
+          name("std::partial_sort_copy(vector<NonIntegral>)"), std_partial_sort_copy, gen2);
+      bm.operator()<std::deque<int>>(name("std::partial_sort_copy(deque<int>)"), std_partial_sort_copy, generate);
+      bm.operator()<std::list<int>>(name("std::partial_sort_copy(list<int>)"), std_partial_sort_copy, generate);
+
+      bm.operator()<std::vector<int>>(
+          name("rng::partial_sort_copy(vector<int>)"), std::ranges::partial_sort_copy, generate);
+      bm.operator()<std::vector<support::NonIntegral>>(
+          name("rng::partial_sort_copy(vector<NonIntegral>)"), std::ranges::partial_sort_copy, gen2);
+      bm.operator()<std::deque<int>>(
+          name("rng::partial_sort_copy(deque<int>)"), std::ranges::partial_sort_copy, generate);
+      bm.operator()<std::list<int>>(
+          name("rng::partial_sort_copy(list<int>)"), std::ranges::partial_sort_copy, generate);
+    };
+
+    register_bm(support::quicksort_adversarial_data<int>, "qsort adversarial");
+    register_bm(support::ascending_sorted_data<int>, "ascending");
+    register_bm(support::descending_sorted_data<int>, "descending");
+    register_bm(support::pipe_organ_data<int>, "pipe-organ");
+    register_bm(support::heap_data<int>, "heap");
+    register_bm(support::shuffled_data<int>, "shuffled");
+    register_bm(support::single_element_data<int>, "repeated");
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}

diff  --git a/libcxx/test/benchmarks/algorithms/sorting/sort.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/sort.bench.cpp
new file mode 100644
index 0000000000000..d12aa108fe123
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/sorting/sort.bench.cpp
@@ -0,0 +1,91 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <array>
+#include <cstddef>
+#include <deque>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "common.h"
+
+int main(int argc, char** argv) {
+  auto std_sort = [](auto first, auto last) { return std::sort(first, last); };
+
+  // Benchmark {std,ranges}::sort on various types of data
+  //
+  // We perform this benchmark in a batch because we need to restore the
+  // state of the container after the operation.
+  //
+  // Also note that we intentionally don't benchmark the predicated version of the algorithm
+  // because that makes the benchmark run too slowly.
+  {
+    auto bm = []<class Container>(std::string name, auto sort, auto generate_data) {
+      benchmark::RegisterBenchmark(
+          name,
+          [sort, generate_data](auto& st) {
+            std::size_t const size          = st.range(0);
+            constexpr std::size_t BatchSize = 32;
+            using ValueType                 = typename Container::value_type;
+            std::vector<ValueType> data     = generate_data(size);
+            std::array<Container, BatchSize> c;
+            std::fill_n(c.begin(), BatchSize, Container(data.begin(), data.end()));
+
+            while (st.KeepRunningBatch(BatchSize)) {
+              for (std::size_t i = 0; i != BatchSize; ++i) {
+                benchmark::DoNotOptimize(c[i]);
+                sort(c[i].begin(), c[i].end());
+                benchmark::DoNotOptimize(c[i]);
+              }
+
+              st.PauseTiming();
+              for (std::size_t i = 0; i != BatchSize; ++i) {
+                std::copy(data.begin(), data.end(), c[i].begin());
+              }
+              st.ResumeTiming();
+            }
+          })
+          ->Arg(8)
+          ->Arg(1024)
+          ->Arg(8192);
+    };
+
+    auto register_bm = [&](auto generate, std::string variant) {
+      auto gen2 = [generate](auto size) {
+        std::vector<int> data = generate(size);
+        std::vector<support::NonIntegral> real_data(data.begin(), data.end());
+        return real_data;
+      };
+      auto name = [variant](std::string op) { return op + " (" + variant + ")"; };
+      bm.operator()<std::vector<int>>(name("std::sort(vector<int>)"), std_sort, generate);
+      bm.operator()<std::vector<support::NonIntegral>>(name("std::sort(vector<NonIntegral>)"), std_sort, gen2);
+      bm.operator()<std::deque<int>>(name("std::sort(deque<int>)"), std_sort, generate);
+
+      bm.operator()<std::vector<int>>(name("rng::sort(vector<int>)"), std::ranges::sort, generate);
+      bm.operator()<std::vector<support::NonIntegral>>(name("rng::sort(vector<NonIntegral>)"), std::ranges::sort, gen2);
+      bm.operator()<std::deque<int>>(name("rng::sort(deque<int>)"), std::ranges::sort, generate);
+    };
+
+    register_bm(support::quicksort_adversarial_data<int>, "qsort adversarial");
+    register_bm(support::ascending_sorted_data<int>, "ascending");
+    register_bm(support::descending_sorted_data<int>, "descending");
+    register_bm(support::pipe_organ_data<int>, "pipe-organ");
+    register_bm(support::heap_data<int>, "heap");
+    register_bm(support::shuffled_data<int>, "shuffled");
+    register_bm(support::single_element_data<int>, "repeated");
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}

diff  --git a/libcxx/test/benchmarks/algorithms/sorting/stable_sort.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/stable_sort.bench.cpp
new file mode 100644
index 0000000000000..8040f5c12a46a
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/sorting/stable_sort.bench.cpp
@@ -0,0 +1,159 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <array>
+#include <cstddef>
+#include <deque>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "common.h"
+#include "count_new.h"
+
+int main(int argc, char** argv) {
+  auto std_stable_sort = [](auto first, auto last) { return std::stable_sort(first, last); };
+
+  // Benchmark {std,ranges}::stable_sort on various types of data
+  //
+  // We perform this benchmark in a batch because we need to restore the
+  // state of the container after the operation.
+  //
+  // Also note that we intentionally don't benchmark the predicated version of the algorithm
+  // because that makes the benchmark run too slowly.
+  {
+    auto bm = []<class Container>(std::string name, auto stable_sort, auto generate_data) {
+      benchmark::RegisterBenchmark(
+          name,
+          [stable_sort, generate_data](auto& st) {
+            std::size_t const size          = st.range(0);
+            constexpr std::size_t BatchSize = 32;
+            using ValueType                 = typename Container::value_type;
+            std::vector<ValueType> data     = generate_data(size);
+            std::array<Container, BatchSize> c;
+            std::fill_n(c.begin(), BatchSize, Container(data.begin(), data.end()));
+
+            while (st.KeepRunningBatch(BatchSize)) {
+              for (std::size_t i = 0; i != BatchSize; ++i) {
+                benchmark::DoNotOptimize(c[i]);
+                stable_sort(c[i].begin(), c[i].end());
+                benchmark::DoNotOptimize(c[i]);
+              }
+
+              st.PauseTiming();
+              for (std::size_t i = 0; i != BatchSize; ++i) {
+                std::copy(data.begin(), data.end(), c[i].begin());
+              }
+              st.ResumeTiming();
+            }
+          })
+          ->Arg(8)
+          ->Arg(1024)
+          ->Arg(8192);
+    };
+
+    auto register_bm = [&](auto generate, std::string variant) {
+      auto gen2 = [generate](auto size) {
+        std::vector<int> data = generate(size);
+        std::vector<support::NonIntegral> real_data(data.begin(), data.end());
+        return real_data;
+      };
+      auto name = [variant](std::string op) { return op + " (" + variant + ")"; };
+      bm.operator()<std::vector<int>>(name("std::stable_sort(vector<int>)"), std_stable_sort, generate);
+      bm.operator()<std::vector<support::NonIntegral>>(
+          name("std::stable_sort(vector<NonIntegral>)"), std_stable_sort, gen2);
+      bm.operator()<std::deque<int>>(name("std::stable_sort(deque<int>)"), std_stable_sort, generate);
+
+      bm.operator()<std::vector<int>>(name("rng::stable_sort(vector<int>)"), std::ranges::stable_sort, generate);
+      bm.operator()<std::vector<support::NonIntegral>>(
+          name("rng::stable_sort(vector<NonIntegral>)"), std::ranges::stable_sort, gen2);
+      bm.operator()<std::deque<int>>(name("rng::stable_sort(deque<int>)"), std::ranges::stable_sort, generate);
+    };
+
+    register_bm(support::quicksort_adversarial_data<int>, "qsort adversarial");
+    register_bm(support::ascending_sorted_data<int>, "ascending");
+    register_bm(support::descending_sorted_data<int>, "descending");
+    register_bm(support::pipe_organ_data<int>, "pipe-organ");
+    register_bm(support::heap_data<int>, "heap");
+    register_bm(support::shuffled_data<int>, "shuffled");
+    register_bm(support::single_element_data<int>, "repeated");
+  }
+
+  // Benchmark {std,ranges}::stable_sort when memory allocation fails. The algorithm must fall back to
+  // a 
diff erent algorithm that has 
diff erent complexity guarantees.
+  {
+    auto bm = []<class Container>(std::string name, auto stable_sort, auto generate_data) {
+      benchmark::RegisterBenchmark(
+          name,
+          [stable_sort, generate_data](auto& st) {
+            std::size_t const size          = st.range(0);
+            constexpr std::size_t BatchSize = 32;
+            using ValueType                 = typename Container::value_type;
+            std::vector<ValueType> data     = generate_data(size);
+            std::array<Container, BatchSize> c;
+            std::fill_n(c.begin(), BatchSize, Container(data.begin(), data.end()));
+
+            while (st.KeepRunningBatch(BatchSize)) {
+              for (std::size_t i = 0; i != BatchSize; ++i) {
+                benchmark::DoNotOptimize(c[i]);
+                // Disable the ability to allocate memory inside this block
+                globalMemCounter.throw_after = 0;
+
+                stable_sort(c[i].begin(), c[i].end());
+                benchmark::DoNotOptimize(c[i]);
+
+                globalMemCounter.reset();
+              }
+
+              st.PauseTiming();
+              for (std::size_t i = 0; i != BatchSize; ++i) {
+                std::copy(data.begin(), data.end(), c[i].begin());
+              }
+              st.ResumeTiming();
+            }
+          })
+          ->Arg(8)
+          ->Arg(1024)
+          ->Arg(8192);
+    };
+
+    auto register_bm = [&](auto generate, std::string variant) {
+      auto gen2 = [generate](auto size) {
+        std::vector<int> data = generate(size);
+        std::vector<support::NonIntegral> real_data(data.begin(), data.end());
+        return real_data;
+      };
+      auto name = [variant](std::string op) { return op + " (alloc fails, " + variant + ")"; };
+      bm.operator()<std::vector<int>>(name("std::stable_sort(vector<int>)"), std_stable_sort, generate);
+      bm.operator()<std::vector<support::NonIntegral>>(
+          name("std::stable_sort(vector<NonIntegral>)"), std_stable_sort, gen2);
+      bm.operator()<std::deque<int>>(name("std::stable_sort(deque<int>)"), std_stable_sort, generate);
+
+      bm.operator()<std::vector<int>>(name("rng::stable_sort(vector<int>)"), std::ranges::stable_sort, generate);
+      bm.operator()<std::vector<support::NonIntegral>>(
+          name("rng::stable_sort(vector<NonIntegral>)"), std::ranges::stable_sort, gen2);
+      bm.operator()<std::deque<int>>(name("rng::stable_sort(deque<int>)"), std::ranges::stable_sort, generate);
+    };
+
+    register_bm(support::quicksort_adversarial_data<int>, "qsort adversarial");
+    register_bm(support::ascending_sorted_data<int>, "ascending");
+    register_bm(support::descending_sorted_data<int>, "descending");
+    register_bm(support::pipe_organ_data<int>, "pipe-organ");
+    register_bm(support::heap_data<int>, "heap");
+    register_bm(support::shuffled_data<int>, "shuffled");
+    register_bm(support::single_element_data<int>, "repeated");
+  }
+
+  benchmark::Initialize(&argc, argv);
+  benchmark::RunSpecifiedBenchmarks();
+  benchmark::Shutdown();
+  return 0;
+}

diff  --git a/libcxx/test/benchmarks/algorithms/stable_sort.bench.cpp b/libcxx/test/benchmarks/algorithms/stable_sort.bench.cpp
deleted file mode 100644
index 26e8de935f5c5..0000000000000
--- a/libcxx/test/benchmarks/algorithms/stable_sort.bench.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-// UNSUPPORTED: c++03, c++11, c++14, c++17
-
-#include <algorithm>
-
-#include "common.h"
-
-namespace {
-template <class ValueType, class Order>
-struct StableSort {
-  size_t Quantity;
-
-  void run(benchmark::State& state) const {
-    runOpOnCopies<ValueType>(state, Quantity, Order(), BatchSize::CountBatch, [](auto& Copy) {
-      std::stable_sort(Copy.begin(), Copy.end());
-    });
-  }
-
-  bool skip() const { return Order() == ::Order::Heap; }
-
-  std::string name() const {
-    return "BM_StableSort" + ValueType::name() + Order::name() + "_" + std::to_string(Quantity);
-  };
-};
-} // namespace
-
-int main(int argc, char** argv) {
-  benchmark::Initialize(&argc, argv);
-  if (benchmark::ReportUnrecognizedArguments(argc, argv))
-    return 1;
-  makeCartesianProductBenchmark<StableSort, AllValueTypes, AllOrders>(Quantities);
-  benchmark::RunSpecifiedBenchmarks();
-}


        


More information about the libcxx-commits mailing list