diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap index 82c51ba73e41e..41ba8c57abdd0 100644 --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -687,6 +687,7 @@ module std [system] { } module ranges_partition { header "__algorithm/ranges_partition.h" + export std.ranges.subrange // return type } module ranges_pop_heap { header "__algorithm/ranges_pop_heap.h" @@ -786,6 +787,7 @@ module std [system] { } module ranges_stable_partition { header "__algorithm/ranges_stable_partition.h" + export std.ranges.subrange // return type } module ranges_stable_sort { header "__algorithm/ranges_stable_sort.h" diff --git a/libcxx/test/benchmarks/algorithms/algorithms.partition_point.bench.cpp b/libcxx/test/benchmarks/algorithms/algorithms.partition_point.bench.cpp deleted file mode 100644 index e0bd7e36f78ad..0000000000000 --- a/libcxx/test/benchmarks/algorithms/algorithms.partition_point.bench.cpp +++ /dev/null @@ -1,131 +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 -#include -#include -#include -#include - -#include "benchmark/benchmark.h" - -#include "../CartesianBenchmarks.h" -#include "../GenerateInput.h" - -namespace { - -template -std::array every_10th_percentile_N(I first, N n) { - N step = n / 10; - std::array res; - - for (size_t i = 0; i < 10; ++i) { - res[i] = first; - std::advance(first, step); - } - - return res; -} - -template -struct TestIntBase { - static std::vector generateInput(size_t size) { - std::vector Res(size); - std::generate(Res.begin(), Res.end(), [] { return getRandomInteger(0, std::numeric_limits::max()); }); - return Res; - } -}; - -struct TestInt32 : TestIntBase { - static constexpr const char* Name = "TestInt32"; -}; - -struct TestInt64 : TestIntBase { - static constexpr const char* Name = "TestInt64"; -}; - -struct TestUint32 : TestIntBase { - static constexpr const char* Name = "TestUint32"; -}; - -struct TestMediumString { - static constexpr const char* Name = "TestMediumString"; - static constexpr size_t StringSize = 32; - - static std::vector generateInput(size_t size) { - std::vector Res(size); - std::generate(Res.begin(), Res.end(), [] { return getRandomString(StringSize); }); - return Res; - } -}; - -using AllTestTypes = std::tuple; - -struct LowerBoundAlg { - template - I operator()(I first, I last, const V& value) const { - return std::lower_bound(first, last, value); - } - - static constexpr const char* Name = "LowerBoundAlg"; -}; - -struct UpperBoundAlg { - template - I operator()(I first, I last, const V& value) const { - return std::upper_bound(first, last, value); - } - - static constexpr const char* Name = "UpperBoundAlg"; -}; - -struct EqualRangeAlg { - template - std::pair operator()(I first, I last, const V& value) const { - return std::equal_range(first, last, value); - } - - static constexpr const char* Name = "EqualRangeAlg"; -}; - -using AllAlgs = std::tuple; - -template -struct PartitionPointBench { - size_t Quantity; - - std::string name() const { - return std::string("PartitionPointBench_") + Alg::Name + "_" + TestType::Name + '/' + std::to_string(Quantity); - } - - void run(benchmark::State& state) const { - auto Data = TestType::generateInput(Quantity); - std::sort(Data.begin(), Data.end()); - auto Every10Percentile = every_10th_percentile_N(Data.begin(), Data.size()); - - for (auto _ : state) { - for (auto Test : Every10Percentile) - benchmark::DoNotOptimize(Alg{}(Data.begin(), Data.end(), *Test)); - } - } -}; - -} // namespace - -int main(int argc, char** argv) { - benchmark::Initialize(&argc, argv); - if (benchmark::ReportUnrecognizedArguments(argc, argv)) - return 1; - - const std::vector Quantities = {1 << 8, 1 << 10, 1 << 20}; - makeCartesianProductBenchmark(Quantities); - benchmark::RunSpecifiedBenchmarks(); -} diff --git a/libcxx/test/benchmarks/algorithms/partitions/is_partitioned.bench.cpp b/libcxx/test/benchmarks/algorithms/partitions/is_partitioned.bench.cpp new file mode 100644 index 0000000000000..fa9873b8db1aa --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/partitions/is_partitioned.bench.cpp @@ -0,0 +1,96 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +#include "benchmark/benchmark.h" +#include "../../GenerateInput.h" + +auto compute_median(auto first, auto last) { + std::vector v(first, last); + auto middle = v.begin() + v.size() / 2; + std::nth_element(v.begin(), middle, v.end()); + return *middle; +} + +int main(int argc, char** argv) { + auto std_is_partitioned = [](auto first, auto last, auto pred) { return std::is_partitioned(first, last, pred); }; + + auto bm = [](std::string name, auto is_partitioned) { + benchmark::RegisterBenchmark( + name, + [is_partitioned](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + Container c; + std::generate_n(std::back_inserter(c), size, [] { return Generate::random(); }); + + // Partition the container in two equally-sized halves, ensuring the median + // value appears in the left half. Note that the median value isn't located + // in the middle -- this isn't std::nth_element. + ValueType median = compute_median(c.begin(), c.end()); + auto pred = [median](auto const& element) { return element <= median; }; + std::partition(c.begin(), c.end(), pred); + assert(std::is_partitioned(c.begin(), c.end(), pred)); + + if constexpr (!Partitioned) { + // De-partition the container by swapping the element containing the median + // value with the last one. + auto median_it = std::find(c.begin(), c.end(), median); + auto last_it = std::next(c.begin(), c.size() - 1); + std::iter_swap(median_it, last_it); + assert(!std::is_partitioned(c.begin(), c.end(), pred)); + } + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(c); + auto result = is_partitioned(c.begin(), c.end(), pred); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(32) + ->Arg(50) // non power-of-two + ->Arg(1024) + ->Arg(8192); + }; + + // std::is_partitioned + bm.operator(), true>("std::is_partitioned(vector) (partitioned)", std_is_partitioned); + bm.operator(), false>("std::is_partitioned(vector) (unpartitioned)", std_is_partitioned); + + bm.operator(), true>("std::is_partitioned(deque) (partitioned)", std_is_partitioned); + bm.operator(), false>("std::is_partitioned(deque) (unpartitioned)", std_is_partitioned); + + bm.operator(), true>("std::is_partitioned(list) (partitioned)", std_is_partitioned); + bm.operator(), false>("std::is_partitioned(list) (unpartitioned)", std_is_partitioned); + + // ranges::is_partitioned + bm.operator(), true>("rng::is_partitioned(vector) (partitioned)", std::ranges::is_partitioned); + bm.operator(), false>( + "rng::is_partitioned(vector) (unpartitioned)", std::ranges::is_partitioned); + + bm.operator(), true>("rng::is_partitioned(deque) (partitioned)", std::ranges::is_partitioned); + bm.operator(), false>("rng::is_partitioned(deque) (unpartitioned)", std::ranges::is_partitioned); + + bm.operator(), true>("rng::is_partitioned(list) (partitioned)", std::ranges::is_partitioned); + bm.operator(), false>("rng::is_partitioned(list) (unpartitioned)", std::ranges::is_partitioned); + + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); + return 0; +} diff --git a/libcxx/test/benchmarks/algorithms/partitions/partition.bench.cpp b/libcxx/test/benchmarks/algorithms/partitions/partition.bench.cpp new file mode 100644 index 0000000000000..2c69966a62091 --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/partitions/partition.bench.cpp @@ -0,0 +1,133 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "benchmark/benchmark.h" +#include "../../GenerateInput.h" + +auto compute_median(auto first, auto last) { + std::vector v(first, last); + auto middle = v.begin() + v.size() / 2; + std::nth_element(v.begin(), middle, v.end()); + return *middle; +} + +int main(int argc, char** argv) { + auto std_partition = [](auto first, auto last, auto pred) { return std::partition(first, last, pred); }; + + // Benchmark {std,ranges}::partition on a fully unpartitionned sequence, i.e. a lot of elements + // have to be moved around in order to partition the range. + { + auto bm = [](std::string name, auto partition) { + benchmark::RegisterBenchmark( + name, + [partition](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + Container c; + std::generate_n(std::back_inserter(c), size, [] { return Generate::random(); }); + + ValueType median = compute_median(c.begin(), c.end()); + auto pred1 = [median](auto const& element) { return element < median; }; + auto pred2 = [median](auto const& element) { return element > median; }; + bool toggle = false; + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(c); + // By toggling the predicate, we have to move almost all elements in the sequence + // to restore the partition. + if (toggle) { + auto result = partition(c.begin(), c.end(), pred1); + benchmark::DoNotOptimize(result); + } else { + auto result = partition(c.begin(), c.end(), pred2); + benchmark::DoNotOptimize(result); + } + toggle = !toggle; + } + }) + ->Arg(32) + ->Arg(50) // non power-of-two + ->Arg(1024) + ->Arg(8192); + }; + + // std::partition + bm.operator()>("std::partition(vector) (dense)", std_partition); + bm.operator()>("std::partition(deque) (dense)", std_partition); + bm.operator()>("std::partition(list) (dense)", std_partition); + + // ranges::partition + bm.operator()>("rng::partition(vector) (dense)", std::ranges::partition); + bm.operator()>("rng::partition(deque) (dense)", std::ranges::partition); + bm.operator()>("rng::partition(list) (dense)", std::ranges::partition); + } + + // Benchmark {std,ranges}::partition on a mostly partitioned sequence, i.e. only 10% of the elements + // have to be moved around in order to partition the range. + { + auto bm = [](std::string name, auto partition) { + benchmark::RegisterBenchmark( + name, + [partition](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + Container c; + std::generate_n(std::back_inserter(c), size, [] { return Generate::random(); }); + ValueType median = compute_median(c.begin(), c.end()); + auto pred = [median](auto const& element) { return element < median; }; + std::partition(c.begin(), c.end(), pred); + + // Between iterations, we swap 5% of the elements to the left of the median with 5% of the elements + // to the right of the median. This ensures that the range is slightly unpartitioned. + auto median_it = std::partition_point(c.begin(), c.end(), pred); + auto low = std::next(c.begin(), std::distance(c.begin(), median_it) - (size / 20)); + auto high = std::next(median_it, size / 20); + auto shuffle = [&] { std::swap_ranges(low, median_it, high); }; + shuffle(); + assert(!std::is_partitioned(c.begin(), c.end(), pred)); + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(c); + auto result = partition(c.begin(), c.end(), pred); + benchmark::DoNotOptimize(result); + shuffle(); + } + }) + ->Arg(32) + ->Arg(50) // non power-of-two + ->Arg(1024) + ->Arg(8192); + }; + + // std::partition + bm.operator()>("std::partition(vector) (sparse)", std_partition); + bm.operator()>("std::partition(deque) (sparse)", std_partition); + bm.operator()>("std::partition(list) (sparse)", std_partition); + + // ranges::partition + bm.operator()>("rng::partition(vector) (sparse)", std::ranges::partition); + bm.operator()>("rng::partition(deque) (sparse)", std::ranges::partition); + bm.operator()>("rng::partition(list) (sparse)", std::ranges::partition); + } + + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); + return 0; +} diff --git a/libcxx/test/benchmarks/algorithms/partitions/partition_copy.bench.cpp b/libcxx/test/benchmarks/algorithms/partitions/partition_copy.bench.cpp new file mode 100644 index 0000000000000..68cac0ffa23b4 --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/partitions/partition_copy.bench.cpp @@ -0,0 +1,76 @@ +//===----------------------------------------------------------------------===// +// +// 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" + +auto compute_median(auto first, auto last) { + std::vector v(first, last); + auto middle = v.begin() + v.size() / 2; + std::nth_element(v.begin(), middle, v.end()); + return *middle; +} + +int main(int argc, char** argv) { + auto std_partition_copy = [](auto first, auto last, auto out_yes, auto out_no, auto pred) { + return std::partition_copy(first, last, out_yes, out_no, pred); + }; + + auto bm = [](std::string name, auto partition_copy) { + benchmark::RegisterBenchmark( + name, + [partition_copy](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + Container c; + std::generate_n(std::back_inserter(c), size, [] { return Generate::random(); }); + + std::vector yes(size); + std::vector no(size); + ValueType median = compute_median(c.begin(), c.end()); + auto pred = [median](auto const& element) { return element < median; }; + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(c); + benchmark::DoNotOptimize(yes); + benchmark::DoNotOptimize(no); + auto result = partition_copy(c.begin(), c.end(), yes.begin(), no.begin(), pred); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(32) + ->Arg(50) // non power-of-two + ->Arg(1024) + ->Arg(8192); + }; + + // std::partition_copy + bm.operator()>("std::partition_copy(vector)", std_partition_copy); + bm.operator()>("std::partition_copy(deque)", std_partition_copy); + bm.operator()>("std::partition_copy(list)", std_partition_copy); + + // ranges::partition_copy + bm.operator()>("rng::partition_copy(vector)", std::ranges::partition_copy); + bm.operator()>("rng::partition_copy(deque)", std::ranges::partition_copy); + bm.operator()>("rng::partition_copy(list)", std::ranges::partition_copy); + + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); + return 0; +} diff --git a/libcxx/test/benchmarks/algorithms/partitions/partition_point.bench.cpp b/libcxx/test/benchmarks/algorithms/partitions/partition_point.bench.cpp new file mode 100644 index 0000000000000..2b98d16935cf8 --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/partitions/partition_point.bench.cpp @@ -0,0 +1,74 @@ +//===----------------------------------------------------------------------===// +// +// 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" + +auto compute_median(auto first, auto last) { + std::vector v(first, last); + auto middle = v.begin() + v.size() / 2; + std::nth_element(v.begin(), middle, v.end()); + return *middle; +} + +int main(int argc, char** argv) { + auto std_partition_point = [](auto first, auto last, auto pred) { return std::partition_point(first, last, pred); }; + + auto bm = [](std::string name, auto partition_point) { + benchmark::RegisterBenchmark( + name, + [partition_point](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + Container c; + std::generate_n(std::back_inserter(c), size, [] { return Generate::random(); }); + + // Partition the container in two equally-sized halves. Based on experimentation, the running + // time of the algorithm doesn't change much depending on the size of the halves. + ValueType median = compute_median(c.begin(), c.end()); + auto pred = [median](auto const& element) { return element < median; }; + std::partition(c.begin(), c.end(), pred); + assert(std::is_partitioned(c.begin(), c.end(), pred)); + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(c); + auto result = partition_point(c.begin(), c.end(), pred); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(32) + ->Arg(50) // non power-of-two + ->Arg(1024) + ->Arg(8192); + }; + + // std::partition_point + bm.operator()>("std::partition_point(vector)", std_partition_point); + bm.operator()>("std::partition_point(deque)", std_partition_point); + bm.operator()>("std::partition_point(list)", std_partition_point); + + // ranges::partition_point + bm.operator()>("rng::partition_point(vector)", std::ranges::partition_point); + bm.operator()>("rng::partition_point(deque)", std::ranges::partition_point); + bm.operator()>("rng::partition_point(list)", std::ranges::partition_point); + + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); + return 0; +} diff --git a/libcxx/test/benchmarks/algorithms/partitions/stable_partition.bench.cpp b/libcxx/test/benchmarks/algorithms/partitions/stable_partition.bench.cpp new file mode 100644 index 0000000000000..622a30506a46f --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/partitions/stable_partition.bench.cpp @@ -0,0 +1,183 @@ +//===----------------------------------------------------------------------===// +// +// 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 "count_new.h" +#include "benchmark/benchmark.h" +#include "../../GenerateInput.h" + +auto compute_median(auto first, auto last) { + std::vector v(first, last); + auto middle = v.begin() + v.size() / 2; + std::nth_element(v.begin(), middle, v.end()); + return *middle; +} + +int main(int argc, char** argv) { + auto std_stable_partition = [](auto first, auto last, auto pred) { return std::stable_partition(first, last, pred); }; + + // Benchmark {std,ranges}::stable_partition on a fully unpartitionned sequence, i.e. a lot of elements + // have to be moved around in order to partition the range. + { + auto bm = [](std::string name, auto stable_partition) { + benchmark::RegisterBenchmark( + name, + [stable_partition](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + Container c; + std::generate_n(std::back_inserter(c), size, [] { return Generate::random(); }); + + ValueType median = compute_median(c.begin(), c.end()); + auto pred1 = [median](auto const& element) { return element < median; }; + auto pred2 = [median](auto const& element) { return element > median; }; + bool toggle = false; + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(c); + // By toggling the predicate, we have to move almost all elements in the sequence + // to restore the partition. + if (toggle) { + auto result = stable_partition(c.begin(), c.end(), pred1); + benchmark::DoNotOptimize(result); + } else { + auto result = stable_partition(c.begin(), c.end(), pred2); + benchmark::DoNotOptimize(result); + } + toggle = !toggle; + } + }) + ->Arg(32) + ->Arg(50) // non power-of-two + ->Arg(1024) + ->Arg(8192); + }; + + // std::stable_partition + bm.operator()>("std::stable_partition(vector) (dense)", std_stable_partition); + bm.operator()>("std::stable_partition(deque) (dense)", std_stable_partition); + bm.operator()>("std::stable_partition(list) (dense)", std_stable_partition); + + // ranges::stable_partition + bm.operator()>("rng::stable_partition(vector) (dense)", std::ranges::stable_partition); + bm.operator()>("rng::stable_partition(deque) (dense)", std::ranges::stable_partition); + bm.operator()>("rng::stable_partition(list) (dense)", std::ranges::stable_partition); + } + + // Benchmark {std,ranges}::stable_partition on a mostly partitioned sequence, i.e. only 10% of the elements + // have to be moved around in order to partition the range. + { + auto bm = [](std::string name, auto stable_partition) { + benchmark::RegisterBenchmark( + name, + [stable_partition](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + Container c; + std::generate_n(std::back_inserter(c), size, [] { return Generate::random(); }); + ValueType median = compute_median(c.begin(), c.end()); + auto pred = [median](auto const& element) { return element < median; }; + std::partition(c.begin(), c.end(), pred); + + // Between iterations, we swap 5% of the elements to the left of the median with 5% of the elements + // to the right of the median. This ensures that the range is slightly unpartitioned. + auto median_it = std::partition_point(c.begin(), c.end(), pred); + auto low = std::next(c.begin(), std::distance(c.begin(), median_it) - (size / 20)); + auto high = std::next(median_it, size / 20); + auto shuffle = [&] { std::swap_ranges(low, median_it, high); }; + shuffle(); + assert(!std::is_partitioned(c.begin(), c.end(), pred)); + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(c); + auto result = stable_partition(c.begin(), c.end(), pred); + benchmark::DoNotOptimize(result); + shuffle(); + } + }) + ->Arg(32) + ->Arg(50) // non power-of-two + ->Arg(1024) + ->Arg(8192); + }; + + // std::stable_partition + bm.operator()>("std::stable_partition(vector) (sparse)", std_stable_partition); + bm.operator()>("std::stable_partition(deque) (sparse)", std_stable_partition); + bm.operator()>("std::stable_partition(list) (sparse)", std_stable_partition); + + // ranges::stable_partition + bm.operator()>("rng::stable_partition(vector) (sparse)", std::ranges::stable_partition); + bm.operator()>("rng::stable_partition(deque) (sparse)", std::ranges::stable_partition); + bm.operator()>("rng::stable_partition(list) (sparse)", std::ranges::stable_partition); + } + + // Benchmark {std,ranges}::stable_partition 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_partition) { + benchmark::RegisterBenchmark( + name, + [stable_partition](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + Container c; + std::generate_n(std::back_inserter(c), size, [] { return Generate::random(); }); + + ValueType median = compute_median(c.begin(), c.end()); + auto pred1 = [median](auto const& element) { return element < median; }; + auto pred2 = [median](auto const& element) { return element > median; }; + bool toggle = false; + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(c); + // Disable the ability to allocate memory inside this block + globalMemCounter.reset(); + globalMemCounter.throw_after = 0; + + if (toggle) { + auto result = stable_partition(c.begin(), c.end(), pred1); + benchmark::DoNotOptimize(result); + } else { + auto result = stable_partition(c.begin(), c.end(), pred2); + benchmark::DoNotOptimize(result); + } + toggle = !toggle; + } + }) + ->Arg(32) + ->Arg(50) // non power-of-two + ->Arg(1024) + ->Arg(8192); + }; + + // std::stable_partition + bm.operator()>("std::stable_partition(vector) (alloc fails)", std_stable_partition); + bm.operator()>("std::stable_partition(deque) (alloc fails)", std_stable_partition); + bm.operator()>("std::stable_partition(list) (alloc fails)", std_stable_partition); + + // ranges::stable_partition + bm.operator()>("rng::stable_partition(vector) (alloc fails)", std::ranges::stable_partition); + bm.operator()>("rng::stable_partition(deque) (alloc fails)", std::ranges::stable_partition); + bm.operator()>("rng::stable_partition(list) (alloc fails)", std::ranges::stable_partition); + } + + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); + return 0; +}