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 -#include - -#include "common.h" - -namespace { -template -struct StableSort { - size_t Quantity; - - void run(benchmark::State& state) const { - runOpOnCopies(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(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 - -#include "common.h" - -namespace { -template -struct Sort { - size_t Quantity; - - void run(benchmark::State& state) const { - runOpOnCopies(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(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 - -#include "common.h" - -namespace { -template -struct StableSort { - size_t Quantity; - - void run(benchmark::State& state) const { - runOpOnCopies(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(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 - -#include "common.h" - -namespace { -template -struct Sort { - size_t Quantity; - - void run(benchmark::State& state) const { - runOpOnCopies(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(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 +#include +#include +#include +#include +#include + +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 +std::vector quicksort_adversarial_data(std::size_t n) { + static_assert(std::is_integral_v); + 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 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 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 +std::vector ascending_sorted_data(std::size_t n) { + std::vector v(n); + std::iota(v.begin(), v.end(), 0); + return v; +} + +// descending sorted values +template +std::vector descending_sorted_data(std::size_t n) { + std::vector v(n); + std::iota(v.begin(), v.end(), 0); + std::reverse(v.begin(), v.end()); + return v; +} + +// pipe-organ pattern +template +std::vector pipe_organ_data(std::size_t n) { + std::vector 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 +std::vector heap_data(std::size_t n) { + std::vector v(n); + std::iota(v.begin(), v.end(), 0); + std::make_heap(v.begin(), v.end()); + return v; +} + +// shuffled randomly +template +std::vector shuffled_data(std::size_t n) { + std::vector 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 +std::vector single_element_data(std::size_t n) { + std::vector 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 +#include +#include +#include +#include +#include +#include + +#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 = [](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 data; + std::generate_n(std::back_inserter(data), size, [] { return Generate::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::is_sorted(vector)", std_is_sorted); + bm.operator()>("std::is_sorted(deque)", std_is_sorted); + bm.operator()>("std::is_sorted(list)", std_is_sorted); + bm.operator()>("rng::is_sorted(vector)", std::ranges::is_sorted); + bm.operator()>("rng::is_sorted(deque)", std::ranges::is_sorted); + bm.operator()>("rng::is_sorted(list)", std::ranges::is_sorted); + + bm.operator()>("std::is_sorted(vector, pred)", std_is_sorted_pred); + bm.operator()>("std::is_sorted(deque, pred)", std_is_sorted_pred); + bm.operator()>("std::is_sorted(list, pred)", std_is_sorted_pred); + bm.operator()>("rng::is_sorted(vector, pred)", ranges_is_sorted_pred); + bm.operator()>("rng::is_sorted(deque, pred)", ranges_is_sorted_pred); + bm.operator()>("rng::is_sorted(list, 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 +#include +#include +#include +#include +#include +#include + +#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 = [](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 data; + std::generate_n(std::back_inserter(data), size, [] { return Generate::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::is_sorted_until(vector)", std_is_sorted_until); + bm.operator()>("std::is_sorted_until(deque)", std_is_sorted_until); + bm.operator()>("std::is_sorted_until(list)", std_is_sorted_until); + bm.operator()>("rng::is_sorted_until(vector)", std::ranges::is_sorted_until); + bm.operator()>("rng::is_sorted_until(deque)", std::ranges::is_sorted_until); + bm.operator()>("rng::is_sorted_until(list)", std::ranges::is_sorted_until); + + bm.operator()>("std::is_sorted_until(vector, pred)", std_is_sorted_until_pred); + bm.operator()>("std::is_sorted_until(deque, pred)", std_is_sorted_until_pred); + bm.operator()>("std::is_sorted_until(list, pred)", std_is_sorted_until_pred); + bm.operator()>("rng::is_sorted_until(vector, pred)", ranges_is_sorted_until_pred); + bm.operator()>("rng::is_sorted_until(deque, pred)", ranges_is_sorted_until_pred); + bm.operator()>("rng::is_sorted_until(list, 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 +#include +#include +#include +#include +#include + +#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 = [](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 data = generate_data(size); + std::array 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 data = generate(size); + std::vector real_data(data.begin(), data.end()); + return real_data; + }; + auto name = [variant](std::string op) { return op + " (" + variant + ")"; }; + bm.operator()>(name("std::partial_sort(vector"), std_partial_sort, generate); + bm.operator()>( + name("std::partial_sort(vector"), std_partial_sort, gen2); + bm.operator()>(name("std::partial_sort(deque"), std_partial_sort, generate); + + bm.operator()>(name("rng::partial_sort(vector"), std::ranges::partial_sort, generate); + bm.operator()>( + name("rng::partial_sort(vector"), std::ranges::partial_sort, gen2); + bm.operator()>(name("rng::partial_sort(deque"), std::ranges::partial_sort, generate); + }; + + register_bm(support::quicksort_adversarial_data, "qsort adversarial"); + register_bm(support::ascending_sorted_data, "ascending"); + register_bm(support::descending_sorted_data, "descending"); + register_bm(support::pipe_organ_data, "pipe-organ"); + register_bm(support::heap_data, "heap"); + register_bm(support::shuffled_data, "shuffled"); + register_bm(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..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 +#include +#include +#include +#include +#include + +#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 = [](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 data = generate_data(size); + Container c(data.begin(), data.end()); + std::vector 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 data = generate(size); + std::vector real_data(data.begin(), data.end()); + return real_data; + }; + auto name = [variant](std::string op) { return op + " (" + variant + ")"; }; + bm.operator()>(name("std::partial_sort_copy(vector)"), std_partial_sort_copy, generate); + bm.operator()>( + name("std::partial_sort_copy(vector)"), std_partial_sort_copy, gen2); + bm.operator()>(name("std::partial_sort_copy(deque)"), std_partial_sort_copy, generate); + bm.operator()>(name("std::partial_sort_copy(list)"), std_partial_sort_copy, generate); + + bm.operator()>( + name("rng::partial_sort_copy(vector)"), std::ranges::partial_sort_copy, generate); + bm.operator()>( + name("rng::partial_sort_copy(vector)"), std::ranges::partial_sort_copy, gen2); + bm.operator()>( + name("rng::partial_sort_copy(deque)"), std::ranges::partial_sort_copy, generate); + bm.operator()>( + name("rng::partial_sort_copy(list)"), std::ranges::partial_sort_copy, generate); + }; + + register_bm(support::quicksort_adversarial_data, "qsort adversarial"); + register_bm(support::ascending_sorted_data, "ascending"); + register_bm(support::descending_sorted_data, "descending"); + register_bm(support::pipe_organ_data, "pipe-organ"); + register_bm(support::heap_data, "heap"); + register_bm(support::shuffled_data, "shuffled"); + register_bm(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..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 +#include +#include +#include +#include +#include + +#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 = [](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 data = generate_data(size); + std::array 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 data = generate(size); + std::vector real_data(data.begin(), data.end()); + return real_data; + }; + auto name = [variant](std::string op) { return op + " (" + variant + ")"; }; + bm.operator()>(name("std::sort(vector)"), std_sort, generate); + bm.operator()>(name("std::sort(vector)"), std_sort, gen2); + bm.operator()>(name("std::sort(deque)"), std_sort, generate); + + bm.operator()>(name("rng::sort(vector)"), std::ranges::sort, generate); + bm.operator()>(name("rng::sort(vector)"), std::ranges::sort, gen2); + bm.operator()>(name("rng::sort(deque)"), std::ranges::sort, generate); + }; + + register_bm(support::quicksort_adversarial_data, "qsort adversarial"); + register_bm(support::ascending_sorted_data, "ascending"); + register_bm(support::descending_sorted_data, "descending"); + register_bm(support::pipe_organ_data, "pipe-organ"); + register_bm(support::heap_data, "heap"); + register_bm(support::shuffled_data, "shuffled"); + register_bm(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..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 +#include +#include +#include +#include +#include + +#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 = [](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 data = generate_data(size); + std::array 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 data = generate(size); + std::vector real_data(data.begin(), data.end()); + return real_data; + }; + auto name = [variant](std::string op) { return op + " (" + variant + ")"; }; + bm.operator()>(name("std::stable_sort(vector)"), std_stable_sort, generate); + bm.operator()>( + name("std::stable_sort(vector)"), std_stable_sort, gen2); + bm.operator()>(name("std::stable_sort(deque)"), std_stable_sort, generate); + + bm.operator()>(name("rng::stable_sort(vector)"), std::ranges::stable_sort, generate); + bm.operator()>( + name("rng::stable_sort(vector)"), std::ranges::stable_sort, gen2); + bm.operator()>(name("rng::stable_sort(deque)"), std::ranges::stable_sort, generate); + }; + + register_bm(support::quicksort_adversarial_data, "qsort adversarial"); + register_bm(support::ascending_sorted_data, "ascending"); + register_bm(support::descending_sorted_data, "descending"); + register_bm(support::pipe_organ_data, "pipe-organ"); + register_bm(support::heap_data, "heap"); + register_bm(support::shuffled_data, "shuffled"); + register_bm(support::single_element_data, "repeated"); + } + + // Benchmark {std,ranges}::stable_sort when memory allocation fails. The algorithm must fall back to + // a different algorithm that has different complexity guarantees. + { + auto bm = [](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 data = generate_data(size); + std::array 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 data = generate(size); + std::vector real_data(data.begin(), data.end()); + return real_data; + }; + auto name = [variant](std::string op) { return op + " (alloc fails, " + variant + ")"; }; + bm.operator()>(name("std::stable_sort(vector)"), std_stable_sort, generate); + bm.operator()>( + name("std::stable_sort(vector)"), std_stable_sort, gen2); + bm.operator()>(name("std::stable_sort(deque)"), std_stable_sort, generate); + + bm.operator()>(name("rng::stable_sort(vector)"), std::ranges::stable_sort, generate); + bm.operator()>( + name("rng::stable_sort(vector)"), std::ranges::stable_sort, gen2); + bm.operator()>(name("rng::stable_sort(deque)"), std::ranges::stable_sort, generate); + }; + + register_bm(support::quicksort_adversarial_data, "qsort adversarial"); + register_bm(support::ascending_sorted_data, "ascending"); + register_bm(support::descending_sorted_data, "descending"); + register_bm(support::pipe_organ_data, "pipe-organ"); + register_bm(support::heap_data, "heap"); + register_bm(support::shuffled_data, "shuffled"); + register_bm(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 - -#include "common.h" - -namespace { -template -struct StableSort { - size_t Quantity; - - void run(benchmark::State& state) const { - runOpOnCopies(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(Quantities); - benchmark::RunSpecifiedBenchmarks(); -}