[libcxx-commits] [libcxx] [libc++] Refactor and add benchmark coverage for [alg.sort] (PR #128236)
Louis Dionne via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Feb 21 19:00:31 PST 2025
https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/128236
>From 03cd33d6bf51a7181f7b6a4d7de1bab0a3969830 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 21 Feb 2025 12:51:50 -0500
Subject: [PATCH 1/2] [libc++] Refactor and add benchmark coverage for
[alg.sort]
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.
---
.../algorithms/pstl.stable_sort.bench.cpp | 42 ------
.../algorithms/ranges_sort.bench.cpp | 40 -----
.../algorithms/ranges_stable_sort.bench.cpp | 40 -----
.../test/benchmarks/algorithms/sort.bench.cpp | 38 -----
.../benchmarks/algorithms/sorting/common.h | 141 ++++++++++++++++++
.../algorithms/sorting/is_sorted.bench.cpp | 81 ++++++++++
.../sorting/is_sorted_until.bench.cpp | 81 ++++++++++
.../algorithms/sorting/partial_sort.bench.cpp | 95 ++++++++++++
.../sorting/partial_sort_copy.bench.cpp | 90 +++++++++++
.../algorithms/sorting/sort.bench.cpp | 92 ++++++++++++
.../algorithms/sorting/stable_sort.bench.cpp | 93 ++++++++++++
.../algorithms/stable_sort.bench.cpp | 40 -----
12 files changed, 673 insertions(+), 200 deletions(-)
delete mode 100644 libcxx/test/benchmarks/algorithms/pstl.stable_sort.bench.cpp
delete mode 100644 libcxx/test/benchmarks/algorithms/ranges_sort.bench.cpp
delete mode 100644 libcxx/test/benchmarks/algorithms/ranges_stable_sort.bench.cpp
delete mode 100644 libcxx/test/benchmarks/algorithms/sort.bench.cpp
create mode 100644 libcxx/test/benchmarks/algorithms/sorting/common.h
create mode 100644 libcxx/test/benchmarks/algorithms/sorting/is_sorted.bench.cpp
create mode 100644 libcxx/test/benchmarks/algorithms/sorting/is_sorted_until.bench.cpp
create mode 100644 libcxx/test/benchmarks/algorithms/sorting/partial_sort.bench.cpp
create mode 100644 libcxx/test/benchmarks/algorithms/sorting/partial_sort_copy.bench.cpp
create mode 100644 libcxx/test/benchmarks/algorithms/sorting/sort.bench.cpp
create mode 100644 libcxx/test/benchmarks/algorithms/sorting/stable_sort.bench.cpp
delete mode 100644 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..cb07d87cbd2c8
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/sorting/is_sorted.bench.cpp
@@ -0,0 +1,81 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+int main(int argc, char** argv) {
+ auto std_is_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..1b96c36c66a1a
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/sorting/is_sorted_until.bench.cpp
@@ -0,0 +1,81 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <algorithm>
+#include <cstddef>
+#include <deque>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+int main(int argc, char** argv) {
+ auto std_is_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..aa25ad25043ac
--- /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);
+ };
+#define BENCH(generate_data, name) \
+ do { \
+ auto gen1 = [](auto size) { return generate_data<int>(size); }; \
+ auto gen2 = [](auto size) { \
+ auto data = generate_data<int>(size); \
+ std::vector<support::NonIntegral> real_data(data.begin(), data.end()); \
+ return real_data; \
+ }; \
+ bm.operator()<std::vector<int>>("std::partial_sort(vector<int>) (" #name ")", std_partial_sort, gen1); \
+ bm.operator()<std::vector<support::NonIntegral>>( \
+ "std::partial_sort(vector<NonIntegral>) (" #name ")", std_partial_sort, gen2); \
+ bm.operator()<std::deque<int>>("std::partial_sort(deque<int>) (" #name ")", std_partial_sort, gen1); \
+ \
+ bm.operator()<std::vector<int>>("rng::partial_sort(vector<int>) (" #name ")", std::ranges::partial_sort, gen1); \
+ bm.operator()<std::vector<support::NonIntegral>>( \
+ "rng::partial_sort(vector<NonIntegral>) (" #name ")", std::ranges::partial_sort, gen2); \
+ bm.operator()<std::deque<int>>("rng::partial_sort(deque<int>) (" #name ")", std::ranges::partial_sort, gen1); \
+ } while (false)
+
+ BENCH(support::quicksort_adversarial_data, "qsort adversarial");
+ BENCH(support::ascending_sorted_data, "ascending");
+ BENCH(support::descending_sorted_data, "descending");
+ BENCH(support::pipe_organ_data, "pipe-organ");
+ BENCH(support::heap_data, "heap");
+ BENCH(support::shuffled_data, "shuffled");
+ BENCH(support::single_element_data, "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..9ce5e814b8472
--- /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);
+ };
+#define BENCH(generate_data, name) \
+ do { \
+ auto gen1 = [](auto size) { return generate_data<int>(size); }; \
+ auto gen2 = [](auto size) { \
+ auto data = generate_data<int>(size); \
+ std::vector<support::NonIntegral> real_data(data.begin(), data.end()); \
+ return real_data; \
+ }; \
+ bm.operator()<std::vector<int>>("std::partial_sort_copy(vector<int>) (" #name ")", std_partial_sort_copy, gen1); \
+ bm.operator()<std::vector<support::NonIntegral>>( \
+ "std::partial_sort_copy(vector<NonIntegral>) (" #name ")", std_partial_sort_copy, gen2); \
+ bm.operator()<std::deque<int>>("std::partial_sort_copy(deque<int>) (" #name ")", std_partial_sort_copy, gen1); \
+ bm.operator()<std::list<int>>("std::partial_sort_copy(list<int>) (" #name ")", std_partial_sort_copy, gen1); \
+ \
+ bm.operator()<std::vector<int>>( \
+ "rng::partial_sort_copy(vector<int>) (" #name ")", std::ranges::partial_sort_copy, gen1); \
+ bm.operator()<std::vector<support::NonIntegral>>( \
+ "rng::partial_sort_copy(vector<NonIntegral>) (" #name ")", std::ranges::partial_sort_copy, gen2); \
+ bm.operator()<std::deque<int>>( \
+ "rng::partial_sort_copy(deque<int>) (" #name ")", std::ranges::partial_sort_copy, gen1); \
+ bm.operator()<std::list<int>>( \
+ "rng::partial_sort_copy(list<int>) (" #name ")", std::ranges::partial_sort_copy, gen1); \
+ } while (false)
+
+ BENCH(support::quicksort_adversarial_data, "qsort adversarial");
+ BENCH(support::ascending_sorted_data, "ascending");
+ BENCH(support::descending_sorted_data, "descending");
+ BENCH(support::pipe_organ_data, "pipe-organ");
+ BENCH(support::heap_data, "heap");
+ BENCH(support::shuffled_data, "shuffled");
+ BENCH(support::single_element_data, "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..0e6c3551daa25
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/sorting/sort.bench.cpp
@@ -0,0 +1,92 @@
+//===----------------------------------------------------------------------===//
+//
+// 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);
+ };
+#define BENCH(generate_data, name) \
+ do { \
+ auto gen1 = [](auto size) { return generate_data<int>(size); }; \
+ auto gen2 = [](auto size) { \
+ auto data = generate_data<int>(size); \
+ std::vector<support::NonIntegral> real_data(data.begin(), data.end()); \
+ return real_data; \
+ }; \
+ bm.operator()<std::vector<int>>("std::sort(vector<int>) (" #name ")", std_sort, gen1); \
+ bm.operator()<std::vector<support::NonIntegral>>("std::sort(vector<NonIntegral>) (" #name ")", std_sort, gen2); \
+ bm.operator()<std::deque<int>>("std::sort(deque<int>) (" #name ")", std_sort, gen1); \
+ \
+ bm.operator()<std::vector<int>>("rng::sort(vector<int>) (" #name ")", std::ranges::sort, gen1); \
+ bm.operator()<std::vector<support::NonIntegral>>( \
+ "rng::sort(vector<NonIntegral>) (" #name ")", std::ranges::sort, gen2); \
+ bm.operator()<std::deque<int>>("rng::sort(deque<int>) (" #name ")", std::ranges::sort, gen1); \
+ } while (false)
+
+ BENCH(support::quicksort_adversarial_data, "qsort adversarial");
+ BENCH(support::ascending_sorted_data, "ascending");
+ BENCH(support::descending_sorted_data, "descending");
+ BENCH(support::pipe_organ_data, "pipe-organ");
+ BENCH(support::heap_data, "heap");
+ BENCH(support::shuffled_data, "shuffled");
+ BENCH(support::single_element_data, "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..ff6f09e1fa852
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/sorting/stable_sort.bench.cpp
@@ -0,0 +1,93 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_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);
+ };
+#define BENCH(generate_data, name) \
+ do { \
+ auto gen1 = [](auto size) { return generate_data<int>(size); }; \
+ auto gen2 = [](auto size) { \
+ auto data = generate_data<int>(size); \
+ std::vector<support::NonIntegral> real_data(data.begin(), data.end()); \
+ return real_data; \
+ }; \
+ bm.operator()<std::vector<int>>("std::stable_sort(vector<int>) (" #name ")", std_stable_sort, gen1); \
+ bm.operator()<std::vector<support::NonIntegral>>( \
+ "std::stable_sort(vector<NonIntegral>) (" #name ")", std_stable_sort, gen2); \
+ bm.operator()<std::deque<int>>("std::stable_sort(deque<int>) (" #name ")", std_stable_sort, gen1); \
+ \
+ bm.operator()<std::vector<int>>("rng::stable_sort(vector<int>) (" #name ")", std::ranges::stable_sort, gen1); \
+ bm.operator()<std::vector<support::NonIntegral>>( \
+ "rng::stable_sort(vector<NonIntegral>) (" #name ")", std::ranges::stable_sort, gen2); \
+ bm.operator()<std::deque<int>>("rng::stable_sort(deque<int>) (" #name ")", std::ranges::stable_sort, gen1); \
+ } while (false)
+
+ BENCH(support::quickstable_sort_adversarial_data, "qstable_sort adversarial");
+ BENCH(support::ascending_stable_sorted_data, "ascending");
+ BENCH(support::descending_stable_sorted_data, "descending");
+ BENCH(support::pipe_organ_data, "pipe-organ");
+ BENCH(support::heap_data, "heap");
+ BENCH(support::shuffled_data, "shuffled");
+ BENCH(support::single_element_data, "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();
-}
>From 86fbaa8ea346344fa12b6adccafc6b182fb23ab3 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Fri, 21 Feb 2025 22:00:22 -0500
Subject: [PATCH 2/2] CI fixes
---
.../test/benchmarks/algorithms/sorting/is_sorted.bench.cpp | 1 +
.../benchmarks/algorithms/sorting/is_sorted_until.bench.cpp | 1 +
.../benchmarks/algorithms/sorting/stable_sort.bench.cpp | 6 +++---
3 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/libcxx/test/benchmarks/algorithms/sorting/is_sorted.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/is_sorted.bench.cpp
index cb07d87cbd2c8..6e553e93d017c 100644
--- a/libcxx/test/benchmarks/algorithms/sorting/is_sorted.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/sorting/is_sorted.bench.cpp
@@ -11,6 +11,7 @@
#include <algorithm>
#include <cstddef>
#include <deque>
+#include <iterator>
#include <list>
#include <string>
#include <vector>
diff --git a/libcxx/test/benchmarks/algorithms/sorting/is_sorted_until.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/is_sorted_until.bench.cpp
index 1b96c36c66a1a..ab11ee35327c7 100644
--- a/libcxx/test/benchmarks/algorithms/sorting/is_sorted_until.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/sorting/is_sorted_until.bench.cpp
@@ -11,6 +11,7 @@
#include <algorithm>
#include <cstddef>
#include <deque>
+#include <iterator>
#include <list>
#include <string>
#include <vector>
diff --git a/libcxx/test/benchmarks/algorithms/sorting/stable_sort.bench.cpp b/libcxx/test/benchmarks/algorithms/sorting/stable_sort.bench.cpp
index ff6f09e1fa852..d398549eb2da8 100644
--- a/libcxx/test/benchmarks/algorithms/sorting/stable_sort.bench.cpp
+++ b/libcxx/test/benchmarks/algorithms/sorting/stable_sort.bench.cpp
@@ -77,9 +77,9 @@ int main(int argc, char** argv) {
bm.operator()<std::deque<int>>("rng::stable_sort(deque<int>) (" #name ")", std::ranges::stable_sort, gen1); \
} while (false)
- BENCH(support::quickstable_sort_adversarial_data, "qstable_sort adversarial");
- BENCH(support::ascending_stable_sorted_data, "ascending");
- BENCH(support::descending_stable_sorted_data, "descending");
+ BENCH(support::quicksort_adversarial_data, "qsort adversarial");
+ BENCH(support::ascending_sorted_data, "ascending");
+ BENCH(support::descending_sorted_data, "descending");
BENCH(support::pipe_organ_data, "pipe-organ");
BENCH(support::heap_data, "heap");
BENCH(support::shuffled_data, "shuffled");
More information about the libcxx-commits
mailing list