diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap index 406b26f86a4c5..ca3f5575d476f 100644 --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -543,11 +543,17 @@ module std [system] { } module ranges_fill_n { header "__algorithm/ranges_fill_n.h" } module ranges_fill { header "__algorithm/ranges_fill.h" } - module ranges_find_end { header "__algorithm/ranges_find_end.h" } + module ranges_find_end { + header "__algorithm/ranges_find_end.h" + export std.ranges.subrange // return type + } module ranges_find_first_of { header "__algorithm/ranges_find_first_of.h" } module ranges_find_if_not { header "__algorithm/ranges_find_if_not.h" } module ranges_find_if { header "__algorithm/ranges_find_if.h" } - module ranges_find_last { header "__algorithm/ranges_find_last.h" } + module ranges_find_last { + header "__algorithm/ranges_find_last.h" + export std.ranges.subrange // return type + } module ranges_find { header "__algorithm/ranges_find.h" } module ranges_fold { header "__algorithm/ranges_fold.h" } module ranges_for_each_n { @@ -737,8 +743,14 @@ module std [system] { } module ranges_rotate { header "__algorithm/ranges_rotate.h" } module ranges_sample { header "__algorithm/ranges_sample.h" } - module ranges_search_n { header "__algorithm/ranges_search_n.h" } - module ranges_search { header "__algorithm/ranges_search.h" } + module ranges_search_n { + header "__algorithm/ranges_search_n.h" + export std.ranges.subrange // return type + } + module ranges_search { + header "__algorithm/ranges_search.h" + export std.ranges.subrange // return type + } module ranges_set_difference { header "__algorithm/ranges_set_difference.h" export std.functional.ranges_operations diff --git a/libcxx/test/benchmarks/GenerateInput.h b/libcxx/test/benchmarks/GenerateInput.h index 9be76f55c2774..06387852f76a6 100644 --- a/libcxx/test/benchmarks/GenerateInput.h +++ b/libcxx/test/benchmarks/GenerateInput.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/libcxx/test/benchmarks/algorithms/count.bench.cpp b/libcxx/test/benchmarks/algorithms/count.bench.cpp deleted file mode 100644 index 46b85e909efa5..0000000000000 --- a/libcxx/test/benchmarks/algorithms/count.bench.cpp +++ /dev/null @@ -1,37 +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 - -static void bm_vector_bool_count(benchmark::State& state) { - std::vector vec1(state.range(), false); - - for (auto _ : state) { - benchmark::DoNotOptimize(vec1); - benchmark::DoNotOptimize(std::count(vec1.begin(), vec1.end(), true)); - } -} -BENCHMARK(bm_vector_bool_count)->DenseRange(1, 8)->Range(16, 1 << 20); - -static void bm_vector_bool_ranges_count(benchmark::State& state) { - std::vector vec1(state.range(), false); - - for (auto _ : state) { - benchmark::DoNotOptimize(vec1); - benchmark::DoNotOptimize(std::ranges::count(vec1.begin(), vec1.end(), true)); - } -} -BENCHMARK(bm_vector_bool_ranges_count)->DenseRange(1, 8)->Range(16, 1 << 20); - -BENCHMARK_MAIN(); diff --git a/libcxx/test/benchmarks/algorithms/equal.bench.cpp b/libcxx/test/benchmarks/algorithms/equal.bench.cpp deleted file mode 100644 index 328b39608607e..0000000000000 --- a/libcxx/test/benchmarks/algorithms/equal.bench.cpp +++ /dev/null @@ -1,99 +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 - -static void bm_equal_iter(benchmark::State& state) { - std::vector vec1(state.range(), '1'); - std::vector vec2(state.range(), '1'); - for (auto _ : state) { - benchmark::DoNotOptimize(vec1); - benchmark::DoNotOptimize(vec2); - benchmark::DoNotOptimize(std::equal(vec1.begin(), vec1.end(), vec2.begin())); - } -} -BENCHMARK(bm_equal_iter)->DenseRange(1, 8)->Range(16, 1 << 20); - -static void bm_equal(benchmark::State& state) { - std::vector vec1(state.range(), '1'); - std::vector vec2(state.range(), '1'); - for (auto _ : state) { - benchmark::DoNotOptimize(vec1); - benchmark::DoNotOptimize(vec2); - benchmark::DoNotOptimize(std::equal(vec1.begin(), vec1.end(), vec2.begin(), vec2.end())); - } -} -BENCHMARK(bm_equal)->DenseRange(1, 8)->Range(16, 1 << 20); - -static void bm_ranges_equal(benchmark::State& state) { - std::vector vec1(state.range(), '1'); - std::vector vec2(state.range(), '1'); - for (auto _ : state) { - benchmark::DoNotOptimize(vec1); - benchmark::DoNotOptimize(vec2); - benchmark::DoNotOptimize(std::ranges::equal(vec1, vec2)); - } -} -BENCHMARK(bm_ranges_equal)->DenseRange(1, 8)->Range(16, 1 << 20); - -static void bm_ranges_equal_vb_aligned(benchmark::State& state) { - auto n = state.range(); - std::vector vec1(n, true); - std::vector vec2(n, true); - for (auto _ : state) { - benchmark::DoNotOptimize(std::ranges::equal(vec1, vec2)); - benchmark::DoNotOptimize(&vec1); - benchmark::DoNotOptimize(&vec2); - } -} - -static void bm_ranges_equal_vb_unaligned(benchmark::State& state) { - auto n = state.range(); - std::vector vec1(n, true); - std::vector vec2(n + 8, true); - auto beg1 = std::ranges::begin(vec1); - auto end1 = std::ranges::end(vec1); - auto beg2 = std::ranges::begin(vec2) + 4; - auto end2 = std::ranges::end(vec2) - 4; - for (auto _ : state) { - benchmark::DoNotOptimize(std::ranges::equal(beg1, end1, beg2, end2)); - benchmark::DoNotOptimize(&vec1); - benchmark::DoNotOptimize(&vec2); - } -} - -// Test std::ranges::equal for vector::iterator -BENCHMARK(bm_ranges_equal_vb_aligned)->RangeMultiplier(4)->Range(8, 1 << 20); -BENCHMARK(bm_ranges_equal_vb_unaligned)->Range(8, 1 << 20); - -static void bm_equal_vb(benchmark::State& state, bool aligned) { - auto n = state.range(); - std::vector vec1(n, true); - std::vector vec2(aligned ? n : n + 8, true); - auto beg1 = vec1.begin(); - auto end1 = vec1.end(); - auto beg2 = aligned ? vec2.begin() : vec2.begin() + 4; - for (auto _ : state) { - benchmark::DoNotOptimize(std::equal(beg1, end1, beg2)); - benchmark::DoNotOptimize(&vec1); - benchmark::DoNotOptimize(&vec2); - } -} - -static void bm_equal_vb_aligned(benchmark::State& state) { bm_equal_vb(state, true); } -static void bm_equal_vb_unaligned(benchmark::State& state) { bm_equal_vb(state, false); } - -// Test std::equal for vector::iterator -BENCHMARK(bm_equal_vb_aligned)->Range(8, 1 << 20); -BENCHMARK(bm_equal_vb_unaligned)->Range(8, 1 << 20); - -BENCHMARK_MAIN(); diff --git a/libcxx/test/benchmarks/algorithms/find.bench.cpp b/libcxx/test/benchmarks/algorithms/find.bench.cpp deleted file mode 100644 index 43d103474ebdf..0000000000000 --- a/libcxx/test/benchmarks/algorithms/find.bench.cpp +++ /dev/null @@ -1,90 +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 - -template -static void bm_find(benchmark::State& state) { - using T = Container::value_type; - - Container vec1(state.range(), '1'); - std::mt19937_64 rng(std::random_device{}()); - - for (auto _ : state) { - auto idx = rng() % vec1.size(); - vec1[idx] = '2'; - benchmark::DoNotOptimize(vec1); - benchmark::DoNotOptimize(std::find(vec1.begin(), vec1.end(), T('2'))); - vec1[idx] = '1'; - } -} -BENCHMARK(bm_find>)->DenseRange(1, 8)->Range(16, 1 << 20); -BENCHMARK(bm_find>)->DenseRange(1, 8)->Range(16, 1 << 20); -BENCHMARK(bm_find>)->DenseRange(1, 8)->Range(16, 1 << 20); -BENCHMARK(bm_find>)->DenseRange(1, 8)->Range(16, 1 << 20); -BENCHMARK(bm_find>)->DenseRange(1, 8)->Range(16, 1 << 20); -BENCHMARK(bm_find>)->DenseRange(1, 8)->Range(16, 1 << 20); - -template -static void bm_ranges_find(benchmark::State& state) { - using T = Container::value_type; - - Container vec1(state.range(), '1'); - std::mt19937_64 rng(std::random_device{}()); - - for (auto _ : state) { - auto idx = rng() % vec1.size(); - vec1[idx] = '2'; - benchmark::DoNotOptimize(vec1); - benchmark::DoNotOptimize(std::ranges::find(vec1, T('2'))); - vec1[idx] = '1'; - } -} -BENCHMARK(bm_ranges_find>)->DenseRange(1, 8)->Range(16, 1 << 20); -BENCHMARK(bm_ranges_find>)->DenseRange(1, 8)->Range(16, 1 << 20); -BENCHMARK(bm_ranges_find>)->DenseRange(1, 8)->Range(16, 1 << 20); -BENCHMARK(bm_ranges_find>)->DenseRange(1, 8)->Range(16, 1 << 20); -BENCHMARK(bm_ranges_find>)->DenseRange(1, 8)->Range(16, 1 << 20); -BENCHMARK(bm_ranges_find>)->DenseRange(1, 8)->Range(16, 1 << 20); - -static void bm_vector_bool_find(benchmark::State& state) { - std::vector vec1(state.range(), false); - std::mt19937_64 rng(std::random_device{}()); - - for (auto _ : state) { - auto idx = rng() % vec1.size(); - vec1[idx] = true; - benchmark::DoNotOptimize(vec1); - benchmark::DoNotOptimize(std::find(vec1.begin(), vec1.end(), true)); - vec1[idx] = false; - } -} -BENCHMARK(bm_vector_bool_find)->DenseRange(1, 8)->Range(16, 1 << 20); - -static void bm_vector_bool_ranges_find(benchmark::State& state) { - std::vector vec1(state.range(), false); - std::mt19937_64 rng(std::random_device{}()); - - for (auto _ : state) { - auto idx = rng() % vec1.size(); - vec1[idx] = true; - benchmark::DoNotOptimize(vec1); - benchmark::DoNotOptimize(std::ranges::find(vec1, true)); - vec1[idx] = false; - } -} -BENCHMARK(bm_vector_bool_ranges_find)->DenseRange(1, 8)->Range(16, 1 << 20); - -BENCHMARK_MAIN(); diff --git a/libcxx/test/benchmarks/algorithms/for_each.bench.cpp b/libcxx/test/benchmarks/algorithms/for_each.bench.cpp deleted file mode 100644 index 554c9ec993043..0000000000000 --- a/libcxx/test/benchmarks/algorithms/for_each.bench.cpp +++ /dev/null @@ -1,25 +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 - -#include -#include -#include - -static void bm_deque_for_each(benchmark::State& state) { - std::deque vec1(state.range(), '1'); - for (auto _ : state) { - benchmark::DoNotOptimize(vec1); - benchmark::DoNotOptimize( - std::for_each(vec1.begin(), vec1.end(), [](char& v) { v = std::clamp(v, (char)10, (char)100); })); - } -} -BENCHMARK(bm_deque_for_each)->DenseRange(1, 8)->Range(16, 1 << 20); - -BENCHMARK_MAIN(); diff --git a/libcxx/test/benchmarks/algorithms/mismatch.bench.cpp b/libcxx/test/benchmarks/algorithms/mismatch.bench.cpp deleted file mode 100644 index 348009a230d6e..0000000000000 --- a/libcxx/test/benchmarks/algorithms/mismatch.bench.cpp +++ /dev/null @@ -1,58 +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 - -#include -#include -#include - -void BenchmarkSizes(benchmark::internal::Benchmark* Benchmark) { - Benchmark->DenseRange(1, 8); - for (size_t i = 16; i != 1 << 20; i *= 2) { - Benchmark->Arg(i - 1); - Benchmark->Arg(i); - Benchmark->Arg(i + 1); - } -} - -// TODO: Look into benchmarking aligned and unaligned memory explicitly -// (currently things happen to be aligned because they are malloced that way) -template -static void bm_mismatch(benchmark::State& state) { - std::vector vec1(state.range(), '1'); - std::vector vec2(state.range(), '1'); - std::mt19937_64 rng(std::random_device{}()); - - vec1.back() = '2'; - for (auto _ : state) { - benchmark::DoNotOptimize(vec1); - benchmark::DoNotOptimize(std::mismatch(vec1.begin(), vec1.end(), vec2.begin())); - } -} -BENCHMARK(bm_mismatch)->Apply(BenchmarkSizes); -BENCHMARK(bm_mismatch)->Apply(BenchmarkSizes); -BENCHMARK(bm_mismatch)->Apply(BenchmarkSizes); - -template -static void bm_mismatch_two_range_overload(benchmark::State& state) { - std::vector vec1(state.range(), '1'); - std::vector vec2(state.range(), '1'); - std::mt19937_64 rng(std::random_device{}()); - - vec1.back() = '2'; - for (auto _ : state) { - benchmark::DoNotOptimize(vec1); - benchmark::DoNotOptimize(std::mismatch(vec1.begin(), vec1.end(), vec2.begin(), vec2.end())); - } -} -BENCHMARK(bm_mismatch_two_range_overload)->DenseRange(1, 8)->Range(16, 1 << 20); -BENCHMARK(bm_mismatch_two_range_overload)->DenseRange(1, 8)->Range(16, 1 << 20); -BENCHMARK(bm_mismatch_two_range_overload)->DenseRange(1, 8)->Range(16, 1 << 20); - -BENCHMARK_MAIN(); diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/adjacent_find.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/adjacent_find.bench.cpp new file mode 100644 index 0000000000000..2bfdffdabdf02 --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/adjacent_find.bench.cpp @@ -0,0 +1,88 @@ +//===----------------------------------------------------------------------===// +// +// 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 "../../GenerateInput.h" + +int main(int argc, char** argv) { + auto std_adjacent_find = [](auto first, auto last) { return std::adjacent_find(first, last); }; + auto std_adjacent_find_pred = [](auto first, auto last) { + return std::adjacent_find(first, last, [](auto x, auto y) { + benchmark::DoNotOptimize(x); + benchmark::DoNotOptimize(y); + return x == y; + }); + }; + auto ranges_adjacent_find_pred = [](auto first, auto last) { + return std::ranges::adjacent_find(first, last, [](auto x, auto y) { + benchmark::DoNotOptimize(x); + benchmark::DoNotOptimize(y); + return x == y; + }); + }; + + // Benchmark {std,ranges}::adjacent_find on a sequence of the form xyxyxyxyxyxyxyxyxyxy, + // which means we never find adjacent equal elements (the worst case of the algorithm). + { + auto bm = [](std::string name, auto adjacent_find) { + benchmark::RegisterBenchmark( + name, + [adjacent_find](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + ValueType x = Generate::random(); + ValueType y = random_different_from({x}); + Container c; + for (std::size_t i = 0; i != size; ++i) { + c.push_back(i % 2 == 0 ? x : y); + } + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(c); + auto result = adjacent_find(c.begin(), c.end()); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(8) + ->Arg(50) // non power-of-two + ->Arg(1024) + ->Arg(8192) + ->Arg(1 << 20); + }; + + // {std,ranges}::adjacent_find + bm.operator()>("std::adjacent_find(vector)", std_adjacent_find); + bm.operator()>("std::adjacent_find(deque)", std_adjacent_find); + bm.operator()>("std::adjacent_find(list)", std_adjacent_find); + bm.operator()>("rng::adjacent_find(vector)", std::ranges::adjacent_find); + bm.operator()>("rng::adjacent_find(deque)", std::ranges::adjacent_find); + bm.operator()>("rng::adjacent_find(list)", std::ranges::adjacent_find); + + // {std,ranges}::adjacent_find(pred) + bm.operator()>("std::adjacent_find(vector, pred)", std_adjacent_find_pred); + bm.operator()>("std::adjacent_find(deque, pred)", std_adjacent_find_pred); + bm.operator()>("std::adjacent_find(list, pred)", std_adjacent_find_pred); + bm.operator()>("rng::adjacent_find(vector, pred)", ranges_adjacent_find_pred); + bm.operator()>("rng::adjacent_find(deque, pred)", ranges_adjacent_find_pred); + bm.operator()>("rng::adjacent_find(list, pred)", ranges_adjacent_find_pred); + } + + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); + return 0; +} diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/any_all_none_of.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/any_all_none_of.bench.cpp new file mode 100644 index 0000000000000..fb17ef12f1fce --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/any_all_none_of.bench.cpp @@ -0,0 +1,100 @@ +//===----------------------------------------------------------------------===// +// +// 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 "../../GenerateInput.h" + +int main(int argc, char** argv) { + auto std_any_of = [](auto first, auto last, auto pred) { return std::any_of(first, last, pred); }; + auto std_all_of = [](auto first, auto last, auto pred) { + // match semantics of any_of + return !std::all_of(first, last, [pred](auto x) { return !pred(x); }); + }; + auto std_none_of = [](auto first, auto last, auto pred) { + // match semantics of any_of + return !std::none_of(first, last, pred); + }; + + auto ranges_all_of = [](auto first, auto last, auto pred) { + // match semantics of any_of + return !std::ranges::all_of(first, last, [pred](auto x) { return !pred(x); }); + }; + auto ranges_none_of = [](auto first, auto last, auto pred) { + // match semantics of any_of + return !std::ranges::none_of(first, last, pred); + }; + + // Benchmark {std,ranges}::{any_of,all_of,none_of} where we process the whole sequence, + // which is the worst case. + { + auto bm = [](std::string name, auto any_of) { + benchmark::RegisterBenchmark( + name, + [any_of](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + ValueType x = Generate::random(); + ValueType y = random_different_from({x}); + Container c(size, x); + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(c); + auto result = any_of(c.begin(), c.end(), [&](auto element) { + benchmark::DoNotOptimize(element); + return element == y; + }); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(8) + ->Arg(32) + ->Arg(50) // non power-of-two + ->Arg(8192) + ->Arg(32768); + }; + + // any_of + bm.operator()>("std::any_of(vector) (process all)", std_any_of); + bm.operator()>("std::any_of(deque) (process all)", std_any_of); + bm.operator()>("std::any_of(list) (process all)", std_any_of); + bm.operator()>("rng::any_of(vector) (process all)", std::ranges::any_of); + bm.operator()>("rng::any_of(deque) (process all)", std::ranges::any_of); + bm.operator()>("rng::any_of(list) (process all)", std::ranges::any_of); + + // all_of + bm.operator()>("std::all_of(vector) (process all)", std_all_of); + bm.operator()>("std::all_of(deque) (process all)", std_all_of); + bm.operator()>("std::all_of(list) (process all)", std_all_of); + bm.operator()>("rng::all_of(vector) (process all)", ranges_all_of); + bm.operator()>("rng::all_of(deque) (process all)", ranges_all_of); + bm.operator()>("rng::all_of(list) (process all)", ranges_all_of); + + // none_of + bm.operator()>("std::none_of(vector) (process all)", std_none_of); + bm.operator()>("std::none_of(deque) (process all)", std_none_of); + bm.operator()>("std::none_of(list) (process all)", std_none_of); + bm.operator()>("rng::none_of(vector) (process all)", ranges_none_of); + bm.operator()>("rng::none_of(deque) (process all)", ranges_none_of); + bm.operator()>("rng::none_of(list) (process all)", ranges_none_of); + } + + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); + return 0; +} diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp new file mode 100644 index 0000000000000..1bb9400113ef9 --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp @@ -0,0 +1,59 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +#include +#include +#include +#include +#include +#include + +#include +#include "../../GenerateInput.h" + +int main(int argc, char** argv) { + // Benchmark ranges::contains where we process the whole sequence, which is the + // worst case. + { + auto bm = [](std::string name) { + benchmark::RegisterBenchmark( + name, + [](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + ValueType x = Generate::random(); + ValueType y = random_different_from({x}); + Container c(size, x); + auto first = c.begin(); + auto last = c.end(); + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(c); + benchmark::DoNotOptimize(y); + auto result = std::ranges::contains(first, last, y); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(8) + ->Arg(32) + ->Arg(50) // non power-of-two + ->Arg(8192) + ->Arg(1 << 20); + }; + bm.operator()>("rng::contains(vector) (process all)"); + bm.operator()>("rng::contains(deque) (process all)"); + bm.operator()>("rng::contains(list) (process all)"); + } + + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); + return 0; +} diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp new file mode 100644 index 0000000000000..cce2e42142ab0 --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp @@ -0,0 +1,162 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "../../GenerateInput.h" + +int main(int argc, char** argv) { + // Benchmark ranges::contains_subrange where we never find the needle, which is the + // worst case. + { + auto bm = [](std::string name) { + benchmark::RegisterBenchmark( + name, + [](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + ValueType x = Generate::random(); + ValueType y = random_different_from({x}); + Container haystack(size, x); + std::size_t n = size / 10; // needle size is 10% of the haystack, but we'll never find it + assert(n > 0); + Container needle(n, y); + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(haystack); + benchmark::DoNotOptimize(needle); + auto result = std::ranges::contains_subrange(haystack, needle); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(16) + ->Arg(32) + ->Arg(50) // non power-of-two + ->Arg(8192) + ->Arg(1 << 20); + }; + bm.operator()>("rng::contains_subrange(vector) (process all)"); + bm.operator()>("rng::contains_subrange(deque) (process all)"); + bm.operator()>("rng::contains_subrange(list) (process all)"); + } + + // Benchmark ranges::contains_subrange where we intersperse "near matches" inside the haystack. + { + auto bm = [](std::string name) { + benchmark::RegisterBenchmark( + name, + [](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + ValueType x = Generate::random(); + ValueType y = random_different_from({x}); + Container haystack(size, x); + std::size_t n = size / 10; // needle size is 10% of the haystack, but we'll never find it + assert(n > 0); + Container needle(n, y); + + // intersperse near-matches inside the haystack + { + auto first = haystack.begin(); + for (int i = 0; i != 10; ++i) { + first = std::copy_n(needle.begin(), n - 1, first); + ++first; // this causes the subsequence not to match because it has length n-1 + } + } + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(haystack); + benchmark::DoNotOptimize(needle); + auto result = std::ranges::contains_subrange(haystack, needle); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(1000) // non power-of-two + ->Arg(1024) + ->Arg(8192); + }; + bm.operator()>("rng::contains_subrange(vector) (near matches)"); + bm.operator()>("rng::contains_subrange(deque) (near matches)"); + bm.operator()>("rng::contains_subrange(list) (near matches)"); + } + + // Special case: the two ranges are the same length (and they are equal, which is the worst case). + { + auto bm = [](std::string name) { + benchmark::RegisterBenchmark( + name, + [](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + ValueType x = Generate::random(); + Container haystack(size, x); + Container needle(size, x); + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(haystack); + benchmark::DoNotOptimize(needle); + auto result = std::ranges::contains_subrange(haystack, needle); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(16) + ->Arg(32) + ->Arg(50) // non power-of-two + ->Arg(8192) + ->Arg(1 << 20); + }; + bm.operator()>("rng::contains_subrange(vector) (same length)"); + bm.operator()>("rng::contains_subrange(deque) (same length)"); + bm.operator()>("rng::contains_subrange(list) (same length)"); + } + + // Special case: the needle contains a single element (which we never find, i.e. the worst case). + { + auto bm = [](std::string name) { + benchmark::RegisterBenchmark( + name, + [](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + ValueType x = Generate::random(); + ValueType y = random_different_from({x}); + Container haystack(size, x); + Container needle(1, y); + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(haystack); + benchmark::DoNotOptimize(needle); + auto result = std::ranges::contains_subrange(haystack, needle); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(16) + ->Arg(32) + ->Arg(50) // non power-of-two + ->Arg(8192) + ->Arg(1 << 20); + }; + bm.operator()>("rng::contains_subrange(vector) (single element)"); + bm.operator()>("rng::contains_subrange(deque) (single element)"); + bm.operator()>("rng::contains_subrange(list) (single element)"); + } + + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); + return 0; +} diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/count.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/count.bench.cpp new file mode 100644 index 0000000000000..c90b23eda8672 --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/count.bench.cpp @@ -0,0 +1,111 @@ +//===----------------------------------------------------------------------===// +// +// 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 "../../GenerateInput.h" + +int main(int argc, char** argv) { + auto std_count = [](auto first, auto last, auto const& value) { return std::count(first, last, value); }; + auto std_count_if = [](auto first, auto last, auto const& value) { + return std::count_if(first, last, [&](auto element) { + benchmark::DoNotOptimize(element); + return element == value; + }); + }; + + auto ranges_count = [](auto first, auto last, auto const& value) { return std::ranges::count(first, last, value); }; + auto ranges_count_if = [](auto first, auto last, auto const& value) { + return std::ranges::count_if(first, last, [&](auto element) { + benchmark::DoNotOptimize(element); + return element == value; + }); + }; + + // Benchmark {std,ranges}::{count,count_if} on a sequence where every other element is counted. + { + auto bm = [](std::string name, auto count) { + benchmark::RegisterBenchmark( + name, + [count](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + ValueType x = Generate::random(); + ValueType y = random_different_from({x}); + Container c; + for (std::size_t i = 0; i != size; ++i) { + c.push_back(i % 2 == 0 ? x : y); + } + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(c); + benchmark::DoNotOptimize(x); + auto result = count(c.begin(), c.end(), x); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(8) + ->Arg(1024) + ->Arg(8192) + ->Arg(1 << 20); + }; + + // count + bm.operator()>("std::count(vector) (every other)", std_count); + bm.operator()>("std::count(deque) (every other)", std_count); + bm.operator()>("std::count(list) (every other)", std_count); + bm.operator()>("rng::count(vector) (every other)", ranges_count); + bm.operator()>("rng::count(deque) (every other)", ranges_count); + bm.operator()>("rng::count(list) (every other)", ranges_count); + + // count_if + bm.operator()>("std::count_if(vector) (every other)", std_count_if); + bm.operator()>("std::count_if(deque) (every other)", std_count_if); + bm.operator()>("std::count_if(list) (every other)", std_count_if); + bm.operator()>("rng::count_if(vector) (every other)", ranges_count_if); + bm.operator()>("rng::count_if(deque) (every other)", ranges_count_if); + bm.operator()>("rng::count_if(list) (every other)", ranges_count_if); + } + + // Benchmark {std,ranges}::count(vector) + { + auto bm = [](std::string name, auto count) { + benchmark::RegisterBenchmark( + name, + [count](auto& st) { + std::size_t const size = st.range(0); + std::vector c(size, false); + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(c); + auto result = count(c.begin(), c.end(), true); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(1000) // non power-of-two + ->Arg(1024) + ->Arg(8192) + ->Arg(1 << 20); + }; + bm.operator()("std::count(vector)", std_count); + bm.operator()("rng::count(vector)", ranges_count); + } + + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); + return 0; +} diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp new file mode 100644 index 0000000000000..0c01833453156 --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp @@ -0,0 +1,118 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "../../GenerateInput.h" + +int main(int argc, char** argv) { + auto ranges_ends_with_pred = [](auto first1, auto last1, auto first2, auto last2) { + return std::ranges::ends_with(first1, last1, first2, last2, [](auto x, auto y) { + benchmark::DoNotOptimize(x); + benchmark::DoNotOptimize(y); + return x == y; + }); + }; + + // Benchmark ranges::ends_with where we find the mismatching element at the very end. + { + auto bm = [](std::string name, auto ends_with) { + benchmark::RegisterBenchmark( + name, + [ends_with](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + ValueType x = Generate::random(); + ValueType y = random_different_from({x}); + Container c1(size, x); + Container c2(size, x); + assert(size != 0); + *std::next(c2.begin(), size - 1) = y; // set last element to y + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(c1); + benchmark::DoNotOptimize(c2); + auto result = ends_with(c1.begin(), c1.end(), c2.begin(), c2.end()); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(8) + ->Arg(50) // non power-of-two + ->Arg(1024) + ->Arg(8192) + ->Arg(1 << 20); + }; + bm.operator()>("rng::ends_with(vector) (mismatch at end)", std::ranges::ends_with); + bm.operator()>("rng::ends_with(deque) (mismatch at end)", std::ranges::ends_with); + bm.operator()>("rng::ends_with(list) (mismatch at end)", std::ranges::ends_with); + bm.operator()>( + "rng::ends_with(forward_list) (mismatch at end)", std::ranges::ends_with); + + bm.operator()>("rng::ends_with(vector, pred) (mismatch at end)", ranges_ends_with_pred); + bm.operator()>("rng::ends_with(deque, pred) (mismatch at end)", ranges_ends_with_pred); + bm.operator()>("rng::ends_with(list, pred) (mismatch at end)", ranges_ends_with_pred); + bm.operator()>( + "rng::ends_with(forward_list, pred) (mismatch at end)", ranges_ends_with_pred); + } + + // Benchmark ranges::ends_with where we find the mismatching element at the very beginning. + { + auto bm = [](std::string name, auto ends_with) { + benchmark::RegisterBenchmark( + name, + [ends_with](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + ValueType x = Generate::random(); + ValueType y = random_different_from({x}); + Container c1(size, x); + Container c2(size, x); + assert(size != 0); + c2.front() = y; + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(c1); + benchmark::DoNotOptimize(c2); + auto result = ends_with(c1.begin(), c1.end(), c2.begin(), c2.end()); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(8) + ->Arg(50) // non power-of-two + ->Arg(1024) + ->Arg(8192) + ->Arg(1 << 20); + }; + bm.operator()>("rng::ends_with(vector) (mismatch at start)", std::ranges::ends_with); + bm.operator()>("rng::ends_with(deque) (mismatch at start)", std::ranges::ends_with); + bm.operator()>("rng::ends_with(list) (mismatch at start)", std::ranges::ends_with); + bm.operator()>( + "rng::ends_with(forward_list) (mismatch at start)", std::ranges::ends_with); + + bm.operator()>("rng::ends_with(vector, pred) (mismatch at start)", ranges_ends_with_pred); + bm.operator()>("rng::ends_with(deque, pred) (mismatch at start)", ranges_ends_with_pred); + bm.operator()>("rng::ends_with(list, pred) (mismatch at start)", ranges_ends_with_pred); + bm.operator()>( + "rng::ends_with(forward_list, pred) (mismatch at start)", ranges_ends_with_pred); + } + + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); + return 0; +} diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/equal.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/equal.bench.cpp new file mode 100644 index 0000000000000..4da164d5f0f94 --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/equal.bench.cpp @@ -0,0 +1,143 @@ +//===----------------------------------------------------------------------===// +// +// 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 "../../GenerateInput.h" + +int main(int argc, char** argv) { + auto std_equal_3leg = [](auto first1, auto last1, auto first2, auto) { return std::equal(first1, last1, first2); }; + auto std_equal_4leg = [](auto first1, auto last1, auto first2, auto last2) { + return std::equal(first1, last1, first2, last2); + }; + auto std_equal_3leg_pred = [](auto first1, auto last1, auto first2, auto) { + return std::equal(first1, last1, first2, [](auto x, auto y) { + benchmark::DoNotOptimize(x); + benchmark::DoNotOptimize(y); + return x == y; + }); + }; + auto std_equal_4leg_pred = [](auto first1, auto last1, auto first2, auto last2) { + return std::equal(first1, last1, first2, last2, [](auto x, auto y) { + benchmark::DoNotOptimize(x); + benchmark::DoNotOptimize(y); + return x == y; + }); + }; + auto ranges_equal_4leg_pred = [](auto first1, auto last1, auto first2, auto last2) { + return std::ranges::equal(first1, last1, first2, last2, [](auto x, auto y) { + benchmark::DoNotOptimize(x); + benchmark::DoNotOptimize(y); + return x == y; + }); + }; + + // Benchmark {std,ranges}::equal where we determine inequality at the very end (worst case). + { + auto bm = [](std::string name, auto equal) { + benchmark::RegisterBenchmark( + name, + [equal](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + ValueType x = Generate::random(); + ValueType y = random_different_from({x}); + Container c1(size, x); + Container c2(size, x); + c2.back() = y; + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(c1); + benchmark::DoNotOptimize(c2); + auto result = equal(c1.begin(), c1.end(), c2.begin(), c2.end()); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(8) + ->Arg(50) // non power-of-two + ->Arg(1024) + ->Arg(8192) + ->Arg(1 << 20); + }; + + // std::equal(it, it, it) + bm.operator()>("std::equal(vector) (it, it, it)", std_equal_3leg); + bm.operator()>("std::equal(deque) (it, it, it)", std_equal_3leg); + bm.operator()>("std::equal(list) (it, it, it)", std_equal_3leg); + + // std::equal(it, it, it, pred) + bm.operator()>("std::equal(vector) (it, it, it, pred)", std_equal_3leg_pred); + bm.operator()>("std::equal(deque) (it, it, it, pred)", std_equal_3leg_pred); + bm.operator()>("std::equal(list) (it, it, it, pred)", std_equal_3leg_pred); + + // {std,ranges}::equal(it, it, it, it) + bm.operator()>("std::equal(vector) (it, it, it, it)", std_equal_4leg); + bm.operator()>("std::equal(deque) (it, it, it, it)", std_equal_4leg); + bm.operator()>("std::equal(list) (it, it, it, it)", std_equal_4leg); + bm.operator()>("rng::equal(vector) (it, it, it, it)", std::ranges::equal); + bm.operator()>("rng::equal(deque) (it, it, it, it)", std::ranges::equal); + bm.operator()>("rng::equal(list) (it, it, it, it)", std::ranges::equal); + + // {std,ranges}::equal(it, it, it, it, pred) + bm.operator()>("std::equal(vector) (it, it, it, it, pred)", std_equal_4leg_pred); + bm.operator()>("std::equal(deque) (it, it, it, it, pred)", std_equal_4leg_pred); + bm.operator()>("std::equal(list) (it, it, it, it, pred)", std_equal_4leg_pred); + bm.operator()>("rng::equal(vector) (it, it, it, it, pred)", ranges_equal_4leg_pred); + bm.operator()>("rng::equal(deque) (it, it, it, it, pred)", ranges_equal_4leg_pred); + bm.operator()>("rng::equal(list) (it, it, it, it, pred)", ranges_equal_4leg_pred); + } + + // Benchmark {std,ranges}::equal on vector. + { + auto bm = [](std::string name, auto equal, bool aligned) { + benchmark::RegisterBenchmark( + name, + [=](auto& st) { + std::size_t const size = st.range(); + std::vector c1(size, true); + std::vector c2(size + 8, true); + auto first1 = c1.begin(); + auto last1 = c1.end(); + auto first2 = aligned ? c2.begin() : c2.begin() + 4; + auto last2 = aligned ? c2.end() : c2.end() - 4; + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(c1); + benchmark::DoNotOptimize(c2); + auto result = equal(first1, last1, first2, last2); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(8) + ->Arg(50) // non power-of-two + ->Arg(1024) + ->Arg(8192) + ->Arg(1 << 20); + }; + + // {std,ranges}::equal(vector) (aligned) + bm("std::equal(vector) (aligned)", std_equal_4leg, true); + bm("rng::equal(vector) (aligned)", std::ranges::equal, true); + + // {std,ranges}::equal(vector) (unaligned) + bm("std::equal(vector) (unaligned)", std_equal_4leg, false); + bm("rng::equal(vector) (unaligned)", std::ranges::equal, false); + } + + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); + return 0; +} diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find.bench.cpp new file mode 100644 index 0000000000000..b2ead1cc75585 --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find.bench.cpp @@ -0,0 +1,181 @@ +//===----------------------------------------------------------------------===// +// +// 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 "../../GenerateInput.h" + +int main(int argc, char** argv) { + auto std_find = [](auto first, auto last, auto const& value) { return std::find(first, last, value); }; + auto std_find_if = [](auto first, auto last, auto const& value) { + return std::find_if(first, last, [&](auto element) { + benchmark::DoNotOptimize(element); + return element == value; + }); + }; + auto std_find_if_not = [](auto first, auto last, auto const& value) { + return std::find_if_not(first, last, [&](auto element) { + benchmark::DoNotOptimize(element); + return element != value; + }); + }; + + auto ranges_find = [](auto first, auto last, auto const& value) { return std::ranges::find(first, last, value); }; + auto ranges_find_if = [](auto first, auto last, auto const& value) { + return std::ranges::find_if(first, last, [&](auto element) { + benchmark::DoNotOptimize(element); + return element == value; + }); + }; + auto ranges_find_if_not = [](auto first, auto last, auto const& value) { + return std::ranges::find_if_not(first, last, [&](auto element) { + benchmark::DoNotOptimize(element); + return element != value; + }); + }; + + auto register_benchmarks = [&](auto bm, std::string comment) { + // find + bm.template operator()>("std::find(vector) (" + comment + ")", std_find); + bm.template operator()>("std::find(vector) (" + comment + ")", std_find); + bm.template operator()>("std::find(deque) (" + comment + ")", std_find); + bm.template operator()>("std::find(list) (" + comment + ")", std_find); + + bm.template operator()>("rng::find(vector) (" + comment + ")", ranges_find); + bm.template operator()>("rng::find(vector) (" + comment + ")", ranges_find); + bm.template operator()>("rng::find(deque) (" + comment + ")", ranges_find); + bm.template operator()>("rng::find(list) (" + comment + ")", ranges_find); + + // find_if + bm.template operator()>("std::find_if(vector) (" + comment + ")", std_find_if); + bm.template operator()>("std::find_if(vector) (" + comment + ")", std_find_if); + bm.template operator()>("std::find_if(deque) (" + comment + ")", std_find_if); + bm.template operator()>("std::find_if(list) (" + comment + ")", std_find_if); + + bm.template operator()>("rng::find_if(vector) (" + comment + ")", ranges_find_if); + bm.template operator()>("rng::find_if(vector) (" + comment + ")", ranges_find_if); + bm.template operator()>("rng::find_if(deque) (" + comment + ")", ranges_find_if); + bm.template operator()>("rng::find_if(list) (" + comment + ")", ranges_find_if); + + // find_if_not + bm.template operator()>("std::find_if_not(vector) (" + comment + ")", std_find_if_not); + bm.template operator()>("std::find_if_not(vector) (" + comment + ")", std_find_if_not); + bm.template operator()>("std::find_if_not(deque) (" + comment + ")", std_find_if_not); + bm.template operator()>("std::find_if_not(list) (" + comment + ")", std_find_if_not); + + bm.template operator()>("rng::find_if_not(vector) (" + comment + ")", ranges_find_if_not); + bm.template operator()>("rng::find_if_not(vector) (" + comment + ")", ranges_find_if_not); + bm.template operator()>("rng::find_if_not(deque) (" + comment + ")", ranges_find_if_not); + bm.template operator()>("rng::find_if_not(list) (" + comment + ")", ranges_find_if_not); + }; + + // Benchmark {std,ranges}::{find,find_if,find_if_not}(normal container) where we + // bail out after 25% of elements + { + auto bm = [](std::string name, auto find) { + benchmark::RegisterBenchmark( + name, + [find](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + ValueType x = Generate::random(); + ValueType y = random_different_from({x}); + Container c(size, x); + + // put the element we're searching for at 25% of the sequence + *std::next(c.begin(), size / 4) = y; + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(c); + benchmark::DoNotOptimize(y); + auto result = find(c.begin(), c.end(), y); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(8) + ->Arg(1024) + ->Arg(8192) + ->Arg(1 << 15); + }; + register_benchmarks(bm, "bail 25%"); + } + + // Benchmark {std,ranges}::{find,find_if,find_if_not}(normal container) where we process the whole sequence + { + auto bm = [](std::string name, auto find) { + benchmark::RegisterBenchmark( + name, + [find](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + ValueType x = Generate::random(); + ValueType y = random_different_from({x}); + Container c(size, x); + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(c); + benchmark::DoNotOptimize(y); + auto result = find(c.begin(), c.end(), y); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(8) + ->Arg(50) // non power-of-two + ->Arg(1024) + ->Arg(8192) + ->Arg(1 << 15); + }; + register_benchmarks(bm, "process all"); + } + + // Benchmark {std,ranges}::{find,find_if,find_if_not}(vector) where we process the whole sequence + { + auto bm = [](std::string name, auto find) { + benchmark::RegisterBenchmark( + name, + [find](auto& st) { + std::size_t const size = st.range(0); + std::vector c(size, true); + bool y = false; + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(c); + benchmark::DoNotOptimize(y); + auto result = find(c.begin(), c.end(), y); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(8) + ->Arg(50) // non power-of-two + ->Arg(1024) + ->Arg(8192) + ->Arg(1 << 20); + }; + bm("std::find(vector) (process all)", std_find); + bm("rng::find(vector) (process all)", ranges_find); + + bm("std::find_if(vector) (process all)", std_find_if); + bm("rng::find_if(vector) (process all)", ranges_find_if); + + bm("std::find_if_not(vector) (process all)", std_find_if_not); + bm("rng::find_if_not(vector) (process all)", ranges_find_if_not); + } + + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); + return 0; +} diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp new file mode 100644 index 0000000000000..f1ad8a65c3235 --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp @@ -0,0 +1,226 @@ +//===----------------------------------------------------------------------===// +// +// 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 +#include "../../GenerateInput.h" + +int main(int argc, char** argv) { + auto std_find_end = [](auto first1, auto last1, auto first2, auto last2) { + return std::find_end(first1, last1, first2, last2); + }; + auto std_find_end_pred = [](auto first1, auto last1, auto first2, auto last2) { + return std::find_end(first1, last1, first2, last2, [](auto x, auto y) { + benchmark::DoNotOptimize(x); + benchmark::DoNotOptimize(y); + return x == y; + }); + }; + auto ranges_find_end_pred = [](auto first1, auto last1, auto first2, auto last2) { + return std::ranges::find_end(first1, last1, first2, last2, [](auto x, auto y) { + benchmark::DoNotOptimize(x); + benchmark::DoNotOptimize(y); + return x == y; + }); + }; + + auto register_benchmarks = [&](auto bm, std::string comment) { + // {std,ranges}::find_end(it1, it1, it2, it2) + bm.template operator()>("std::find_end(vector) (" + comment + ")", std_find_end); + bm.template operator()>("std::find_end(deque) (" + comment + ")", std_find_end); + bm.template operator()>("std::find_end(list) (" + comment + ")", std_find_end); + bm.template operator()>("std::find_end(forward_list) (" + comment + ")", std_find_end); + bm.template operator()>("rng::find_end(vector) (" + comment + ")", std::ranges::find_end); + bm.template operator()>("rng::find_end(deque) (" + comment + ")", std::ranges::find_end); + bm.template operator()>("rng::find_end(list) (" + comment + ")", std::ranges::find_end); + bm.template operator()>( + "rng::find_end(forward_list) (" + comment + ")", std::ranges::find_end); + + // {std,ranges}::find_end(it1, it1, it2, it2, pred) + bm.template operator()>("std::find_end(vector, pred) (" + comment + ")", std_find_end_pred); + bm.template operator()>("std::find_end(deque, pred) (" + comment + ")", std_find_end_pred); + bm.template operator()>("std::find_end(list, pred) (" + comment + ")", std_find_end_pred); + bm.template operator()>( + "std::find_end(forward_list, pred) (" + comment + ")", std_find_end_pred); + bm.template operator()>( + "rng::find_end(vector, pred) (" + comment + ")", ranges_find_end_pred); + bm.template operator()>("rng::find_end(deque, pred) (" + comment + ")", ranges_find_end_pred); + bm.template operator()>("rng::find_end(list, pred) (" + comment + ")", ranges_find_end_pred); + bm.template operator()>( + "rng::find_end(forward_list, pred) (" + comment + ")", ranges_find_end_pred); + }; + + // Benchmark {std,ranges}::find_end where we never find the needle, which is the + // worst case. + { + auto bm = [](std::string name, auto find_end) { + benchmark::RegisterBenchmark( + name, + [find_end](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + ValueType x = Generate::random(); + ValueType y = random_different_from({x}); + Container haystack(size, x); + std::size_t n = size / 10; // needle size is 10% of the haystack, but we'll never find it + assert(n > 0); + Container needle(n, y); + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(haystack); + benchmark::DoNotOptimize(needle); + auto result = find_end(haystack.begin(), haystack.end(), needle.begin(), needle.end()); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(1000) // non power-of-two + ->Arg(1024) + ->Arg(8192) + ->Arg(1 << 20); + }; + register_benchmarks(bm, "process all"); + } + + // Benchmark {std,ranges}::find_end where we intersperse "near matches" inside the haystack. + { + auto bm = [](std::string name, auto find_end) { + benchmark::RegisterBenchmark( + name, + [find_end](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + ValueType x = Generate::random(); + ValueType y = random_different_from({x}); + Container haystack(size, x); + std::size_t n = size / 10; // needle size is 10% of the haystack + assert(n > 0); + Container needle(n, y); + + // intersperse near-matches inside the haystack + { + auto first = haystack.begin(); + for (int i = 0; i != 10; ++i) { + first = std::copy_n(needle.begin(), n - 1, first); + ++first; // this causes the subsequence not to match because it has length n-1 + } + } + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(haystack); + benchmark::DoNotOptimize(needle); + auto result = find_end(haystack.begin(), haystack.end(), needle.begin(), needle.end()); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(1000) // non power-of-two + ->Arg(1024) + ->Arg(8192); + }; + register_benchmarks(bm, "near matches"); + } + + // Special case: the two ranges are the same length (and they are equal, which is the worst case). + { + auto bm = [](std::string name, auto find_end) { + benchmark::RegisterBenchmark( + name, + [find_end](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + ValueType x = Generate::random(); + Container haystack(size, x); + Container needle(size, x); + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(haystack); + benchmark::DoNotOptimize(needle); + auto result = find_end(haystack.begin(), haystack.end(), needle.begin(), needle.end()); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(1000) // non power-of-two + ->Arg(1024) + ->Arg(8192); + }; + register_benchmarks(bm, "same length"); + } + + // Special case: the needle contains a single element (which we never find, i.e. the worst case). + { + auto bm = [](std::string name, auto find_end) { + benchmark::RegisterBenchmark( + name, + [find_end](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + ValueType x = Generate::random(); + ValueType y = random_different_from({x}); + Container haystack(size, x); + Container needle(1, y); + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(haystack); + benchmark::DoNotOptimize(needle); + auto result = find_end(haystack.begin(), haystack.end(), needle.begin(), needle.end()); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(1000) // non power-of-two + ->Arg(1024) + ->Arg(8192); + }; + register_benchmarks(bm, "single element"); + } + + // Special case: we have a match close to the end of the haystack (ideal case if we start searching from the end). + { + auto bm = [](std::string name, auto find_end) { + benchmark::RegisterBenchmark( + name, + [find_end](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + ValueType x = Generate::random(); + ValueType y = random_different_from({x}); + Container haystack(size, x); + std::size_t n = size / 10; // needle size is 10% of the haystack + assert(n > 0); + Container needle(n, y); + + // put the needle at 90% of the haystack + std::ranges::copy(needle, std::next(haystack.begin(), (9 * size) / 10)); + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(haystack); + benchmark::DoNotOptimize(needle); + auto result = find_end(haystack.begin(), haystack.end(), needle.begin(), needle.end()); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(1000) // non power-of-two + ->Arg(1024) + ->Arg(8192); + }; + register_benchmarks(bm, "match near end"); + } + + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); + return 0; +} diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp new file mode 100644 index 0000000000000..fca1aaf233988 --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.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 "../../GenerateInput.h" + +int main(int argc, char** argv) { + auto std_find_first_of = [](auto first1, auto last1, auto first2, auto last2) { + return std::find_first_of(first1, last1, first2, last2); + }; + auto std_find_first_of_pred = [](auto first1, auto last1, auto first2, auto last2) { + return std::find_first_of(first1, last1, first2, last2, [](auto x, auto y) { + benchmark::DoNotOptimize(x); + benchmark::DoNotOptimize(y); + return x == y; + }); + }; + auto ranges_find_first_of_pred = [](auto first1, auto last1, auto first2, auto last2) { + return std::ranges::find_first_of(first1, last1, first2, last2, [](auto x, auto y) { + benchmark::DoNotOptimize(x); + benchmark::DoNotOptimize(y); + return x == y; + }); + }; + + // Benchmark {std,ranges}::find_first_of where we never find a match in the needle, and the needle is small. + // This is the worst case of the most common case (a small needle). + { + auto bm = [](std::string name, auto find_first_of) { + benchmark::RegisterBenchmark( + name, + [find_first_of](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + ValueType x = Generate::random(); + ValueType y = random_different_from({x}); + Container haystack(size, x); + Container needle(10, y); + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(haystack); + benchmark::DoNotOptimize(needle); + auto result = find_first_of(haystack.begin(), haystack.end(), needle.begin(), needle.end()); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(32) + ->Arg(50) // non power-of-two + ->Arg(1024) + ->Arg(8192); + }; + // {std,ranges}::find_first_of(it1, it1, it2, it2) + bm.operator()>("std::find_first_of(vector) (small needle)", std_find_first_of); + bm.operator()>("std::find_first_of(deque) (small needle)", std_find_first_of); + bm.operator()>("std::find_first_of(list) (small needle)", std_find_first_of); + bm.operator()>("rng::find_first_of(vector) (small needle)", std::ranges::find_first_of); + bm.operator()>("rng::find_first_of(deque) (small needle)", std::ranges::find_first_of); + bm.operator()>("rng::find_first_of(list) (small needle)", std::ranges::find_first_of); + + // {std,ranges}::find_first_of(it1, it1, it2, it2, pred) + bm.operator()>("std::find_first_of(vector, pred) (small needle)", std_find_first_of_pred); + bm.operator()>("std::find_first_of(deque, pred) (small needle)", std_find_first_of_pred); + bm.operator()>("std::find_first_of(list, pred) (small needle)", std_find_first_of_pred); + bm.operator()>("rng::find_first_of(vector, pred) (small needle)", ranges_find_first_of_pred); + bm.operator()>("rng::find_first_of(deque, pred) (small needle)", ranges_find_first_of_pred); + bm.operator()>("rng::find_first_of(list, pred) (small needle)", ranges_find_first_of_pred); + } + + // Special case: the needle is large compared to the haystack, and we find a match early in the haystack. + { + auto bm = [](std::string name, auto find_first_of) { + benchmark::RegisterBenchmark( + name, + [find_first_of](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + ValueType x = Generate::random(); + ValueType y = random_different_from({x}); + Container haystack(size, x); + Container needle(size * 10, y); + + // put a match at 10% of the haystack + *std::next(haystack.begin(), haystack.size() / 10) = y; + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(haystack); + benchmark::DoNotOptimize(needle); + auto result = find_first_of(haystack.begin(), haystack.end(), needle.begin(), needle.end()); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(32) + ->Arg(50) // non power-of-two + ->Arg(1024) + ->Arg(8192); + }; + // {std,ranges}::find_first_of(it1, it1, it2, it2) + bm.operator()>("std::find_first_of(vector) (large needle)", std_find_first_of); + bm.operator()>("std::find_first_of(deque) (large needle)", std_find_first_of); + bm.operator()>("std::find_first_of(list) (large needle)", std_find_first_of); + bm.operator()>("rng::find_first_of(vector) (large needle)", std::ranges::find_first_of); + bm.operator()>("rng::find_first_of(deque) (large needle)", std::ranges::find_first_of); + bm.operator()>("rng::find_first_of(list) (large needle)", std::ranges::find_first_of); + + // {std,ranges}::find_first_of(it1, it1, it2, it2, pred) + bm.operator()>("std::find_first_of(vector, pred) (large needle)", std_find_first_of_pred); + bm.operator()>("std::find_first_of(deque, pred) (large needle)", std_find_first_of_pred); + bm.operator()>("std::find_first_of(list, pred) (large needle)", std_find_first_of_pred); + bm.operator()>("rng::find_first_of(vector, pred) (large needle)", ranges_find_first_of_pred); + bm.operator()>("rng::find_first_of(deque, pred) (large needle)", ranges_find_first_of_pred); + bm.operator()>("rng::find_first_of(list, pred) (large needle)", ranges_find_first_of_pred); + } + + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); + return 0; +} diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp new file mode 100644 index 0000000000000..17074250c6c72 --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp @@ -0,0 +1,145 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "../../GenerateInput.h" + +int main(int argc, char** argv) { + auto ranges_find_last_if = [](auto first, auto last, auto const& value) { + return std::ranges::find_last_if(first, last, [&](auto element) { + benchmark::DoNotOptimize(element); + return element == value; + }); + }; + auto ranges_find_last_if_not = [](auto first, auto last, auto const& value) { + return std::ranges::find_last_if_not(first, last, [&](auto element) { + benchmark::DoNotOptimize(element); + return element != value; + }); + }; + + // Benchmark ranges::{find_last,find_last_if,find_last_if_not} where the last element + // is found 10% into the sequence + { + auto bm = [](std::string name, auto find_last) { + benchmark::RegisterBenchmark( + name, + [find_last](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + ValueType x = Generate::random(); + ValueType y = random_different_from({x}); + Container c(size, x); + + // put the element we're searching for at 10% of the sequence + *std::next(c.begin(), size / 10) = y; + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(c); + benchmark::DoNotOptimize(y); + auto result = find_last(c.begin(), c.end(), y); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(8) + ->Arg(50) // non power-of-two + ->Arg(1024) + ->Arg(8192) + ->Arg(1 << 20); + }; + + // find_last + bm.operator()>("rng::find_last(vector) (bail 10%)", std::ranges::find_last); + bm.operator()>("rng::find_last(vector) (bail 10%)", std::ranges::find_last); + bm.operator()>("rng::find_last(deque) (bail 10%)", std::ranges::find_last); + bm.operator()>("rng::find_last(list) (bail 10%)", std::ranges::find_last); + bm.operator()>("rng::find_last(forward_list) (bail 10%)", std::ranges::find_last); + + // find_last_if + bm.operator()>("rng::find_last_if(vector) (bail 10%)", ranges_find_last_if); + bm.operator()>("rng::find_last_if(vector) (bail 10%)", ranges_find_last_if); + bm.operator()>("rng::find_last_if(deque) (bail 10%)", ranges_find_last_if); + bm.operator()>("rng::find_last_if(list) (bail 10%)", ranges_find_last_if); + bm.operator()>("rng::find_last_if(forward_list) (bail 10%)", ranges_find_last_if); + + // find_last_if_not + bm.operator()>("rng::find_last_if_not(vector) (bail 10%)", ranges_find_last_if_not); + bm.operator()>("rng::find_last_if_not(vector) (bail 10%)", ranges_find_last_if_not); + bm.operator()>("rng::find_last_if_not(deque) (bail 10%)", ranges_find_last_if_not); + bm.operator()>("rng::find_last_if_not(list) (bail 10%)", ranges_find_last_if_not); + bm.operator()>( + "rng::find_last_if_not(forward_list) (bail 10%)", ranges_find_last_if_not); + } + + // Benchmark ranges::{find_last,find_last_if,find_last_if_not} where the last element + // is found 90% into the sequence (i.e. near the end) + { + auto bm = [](std::string name, auto find_last) { + benchmark::RegisterBenchmark( + name, + [find_last](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + ValueType x = Generate::random(); + ValueType y = random_different_from({x}); + Container c(size, x); + + // put the element we're searching for at 90% of the sequence + *std::next(c.begin(), (9 * size) / 10) = y; + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(c); + benchmark::DoNotOptimize(y); + auto result = find_last(c.begin(), c.end(), y); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(8) + ->Arg(50) // non power-of-two + ->Arg(1024) + ->Arg(8192) + ->Arg(1 << 20); + }; + // find_last + bm.operator()>("rng::find_last(vector) (bail 90%)", std::ranges::find_last); + bm.operator()>("rng::find_last(vector) (bail 90%)", std::ranges::find_last); + bm.operator()>("rng::find_last(deque) (bail 90%)", std::ranges::find_last); + bm.operator()>("rng::find_last(list) (bail 90%)", std::ranges::find_last); + bm.operator()>("rng::find_last(forward_list) (bail 90%)", std::ranges::find_last); + + // find_last_if + bm.operator()>("rng::find_last_if(vector) (bail 90%)", ranges_find_last_if); + bm.operator()>("rng::find_last_if(vector) (bail 90%)", ranges_find_last_if); + bm.operator()>("rng::find_last_if(deque) (bail 90%)", ranges_find_last_if); + bm.operator()>("rng::find_last_if(list) (bail 90%)", ranges_find_last_if); + bm.operator()>("rng::find_last_if(forward_list) (bail 90%)", ranges_find_last_if); + + // find_last_if_not + bm.operator()>("rng::find_last_if_not(vector) (bail 90%)", ranges_find_last_if_not); + bm.operator()>("rng::find_last_if_not(vector) (bail 90%)", ranges_find_last_if_not); + bm.operator()>("rng::find_last_if_not(deque) (bail 90%)", ranges_find_last_if_not); + bm.operator()>("rng::find_last_if_not(list) (bail 90%)", ranges_find_last_if_not); + bm.operator()>( + "rng::find_last_if_not(forward_list) (bail 90%)", ranges_find_last_if_not); + } + + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); + return 0; +} diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp new file mode 100644 index 0000000000000..a795b5df7f7f4 --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp @@ -0,0 +1,73 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "../../GenerateInput.h" + +int main(int argc, char** argv) { + // ranges::{fold_left,fold_right} + { + auto bm = [](std::string name, auto fold) { + benchmark::RegisterBenchmark( + name, + [fold](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + static_assert(std::is_unsigned_v, + "We could encounter UB if signed arithmetic overflows in this benchmark"); + + Container c; + std::generate_n(std::back_inserter(c), size, [&] { return Generate::random(); }); + ValueType init = c.back(); + c.pop_back(); + + auto f = [](auto x, auto y) { + benchmark::DoNotOptimize(x); + benchmark::DoNotOptimize(y); + return x + y; + }; + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(c); + benchmark::DoNotOptimize(init); + auto result = fold(c.begin(), c.end(), init, f); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(8) + ->Arg(32) + ->Arg(50) // non power-of-two + ->Arg(8192) + ->Arg(1 << 20); + }; + bm.operator()>("rng::fold_left(vector)", std::ranges::fold_left); + bm.operator()>("rng::fold_left(deque)", std::ranges::fold_left); + bm.operator()>("rng::fold_left(list)", std::ranges::fold_left); + + // TODO: fold_right not implemented yet + // bm.operator()>("rng::fold_right(vector)", std::ranges::fold_right); + // bm.operator()>("rng::fold_right(deque)", std::ranges::fold_right); + // bm.operator()>("rng::fold_right(list)", std::ranges::fold_right); + } + + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); + return 0; +} diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/for_each.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/for_each.bench.cpp new file mode 100644 index 0000000000000..760accbe4d929 --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/for_each.bench.cpp @@ -0,0 +1,58 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +int main(int argc, char** argv) { + auto std_for_each = [](auto first, auto last, auto f) { return std::for_each(first, last, f); }; + + // {std,ranges}::for_each + { + auto bm = [](std::string name, auto for_each) { + benchmark::RegisterBenchmark( + name, + [for_each](auto& st) { + std::size_t const size = st.range(0); + Container c(size, 1); + auto first = c.begin(); + auto last = c.end(); + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(c); + auto result = for_each(first, last, [](int& x) { x = std::clamp(x, 10, 100); }); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(8) + ->Arg(32) + ->Arg(50) // non power-of-two + ->Arg(8192) + ->Arg(1 << 20); + }; + bm.operator()>("std::for_each(vector)", std_for_each); + bm.operator()>("std::for_each(deque)", std_for_each); + bm.operator()>("std::for_each(list)", std_for_each); + bm.operator()>("rng::for_each(vector)", std::ranges::for_each); + bm.operator()>("rng::for_each(deque)", std::ranges::for_each); + bm.operator()>("rng::for_each(list)", std::ranges::for_each); + } + + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); + return 0; +} diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/is_permutation.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/is_permutation.bench.cpp new file mode 100644 index 0000000000000..3e4d21945bf06 --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/is_permutation.bench.cpp @@ -0,0 +1,158 @@ +//===----------------------------------------------------------------------===// +// +// 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 "../../GenerateInput.h" + +int main(int argc, char** argv) { + auto std_is_permutation_3leg = [](auto first1, auto last1, auto first2, auto) { + return std::is_permutation(first1, last1, first2); + }; + auto std_is_permutation_4leg = [](auto first1, auto last1, auto first2, auto last2) { + return std::is_permutation(first1, last1, first2, last2); + }; + auto std_is_permutation_3leg_pred = [](auto first1, auto last1, auto first2, auto) { + return std::is_permutation(first1, last1, first2, [](auto x, auto y) { + benchmark::DoNotOptimize(x); + benchmark::DoNotOptimize(y); + return x == y; + }); + }; + auto std_is_permutation_4leg_pred = [](auto first1, auto last1, auto first2, auto last2) { + return std::is_permutation(first1, last1, first2, last2, [](auto x, auto y) { + benchmark::DoNotOptimize(x); + benchmark::DoNotOptimize(y); + return x == y; + }); + }; + auto ranges_is_permutation_4leg_pred = [](auto first1, auto last1, auto first2, auto last2) { + return std::ranges::is_permutation(first1, last1, first2, last2, [](auto x, auto y) { + benchmark::DoNotOptimize(x); + benchmark::DoNotOptimize(y); + return x == y; + }); + }; + + auto register_benchmarks = [&](auto bm, std::string comment) { + // std::is_permutation(it, it, it) + bm.template operator()>( + "std::is_permutation(vector) (3leg) (" + comment + ")", std_is_permutation_3leg); + bm.template operator()>( + "std::is_permutation(deque) (3leg) (" + comment + ")", std_is_permutation_3leg); + bm.template operator()>( + "std::is_permutation(list) (3leg) (" + comment + ")", std_is_permutation_3leg); + + // std::is_permutation(it, it, it, pred) + bm.template operator()>( + "std::is_permutation(vector) (3leg, pred) (" + comment + ")", std_is_permutation_3leg_pred); + bm.template operator()>( + "std::is_permutation(deque) (3leg, pred) (" + comment + ")", std_is_permutation_3leg_pred); + bm.template operator()>( + "std::is_permutation(list) (3leg, pred) (" + comment + ")", std_is_permutation_3leg_pred); + + // {std,ranges}::is_permutation(it, it, it, it) + bm.template operator()>( + "std::is_permutation(vector) (4leg) (" + comment + ")", std_is_permutation_4leg); + bm.template operator()>( + "std::is_permutation(deque) (4leg) (" + comment + ")", std_is_permutation_4leg); + bm.template operator()>( + "std::is_permutation(list) (4leg) (" + comment + ")", std_is_permutation_4leg); + bm.template operator()>( + "rng::is_permutation(vector) (4leg) (" + comment + ")", std::ranges::is_permutation); + bm.template operator()>( + "rng::is_permutation(deque) (4leg) (" + comment + ")", std::ranges::is_permutation); + bm.template operator()>( + "rng::is_permutation(list) (4leg) (" + comment + ")", std::ranges::is_permutation); + + // {std,ranges}::is_permutation(it, it, it, it, pred) + bm.template operator()>( + "std::is_permutation(vector) (4leg, pred) (" + comment + ")", std_is_permutation_4leg_pred); + bm.template operator()>( + "std::is_permutation(deque) (4leg, pred) (" + comment + ")", std_is_permutation_4leg_pred); + bm.template operator()>( + "std::is_permutation(list) (4leg, pred) (" + comment + ")", std_is_permutation_4leg_pred); + bm.template operator()>( + "rng::is_permutation(vector) (4leg, pred) (" + comment + ")", ranges_is_permutation_4leg_pred); + bm.template operator()>( + "rng::is_permutation(deque) (4leg, pred) (" + comment + ")", ranges_is_permutation_4leg_pred); + bm.template operator()>( + "rng::is_permutation(list) (4leg, pred) (" + comment + ")", ranges_is_permutation_4leg_pred); + }; + + // Benchmark {std,ranges}::is_permutation where both sequences share a common prefix (this can be optimized). + { + auto bm = [](std::string name, auto is_permutation) { + benchmark::RegisterBenchmark( + name, + [is_permutation](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + ValueType x = Generate::random(); + ValueType y = random_different_from({x}); + Container c1(size, x); + Container c2(size, x); + c2.back() = y; + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(c1); + benchmark::DoNotOptimize(c2); + auto result = is_permutation(c1.begin(), c1.end(), c2.begin(), c2.end()); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(8) + ->Arg(1024) + ->Arg(8192); + }; + register_benchmarks(bm, "common prefix"); + } + + // Benchmark {std,ranges}::is_permutation on fully shuffled sequences. + { + auto bm = [](std::string name, auto is_permutation) { + benchmark::RegisterBenchmark( + name, + [is_permutation](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(); }); + Container c1(data.begin(), data.end()); + + std::mt19937 rng; + std::shuffle(data.begin(), data.end(), rng); + Container c2(data.begin(), data.end()); + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(c1); + benchmark::DoNotOptimize(c2); + auto result = is_permutation(c1.begin(), c1.end(), c2.begin(), c2.end()); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(8) + ->Arg(1024); // this one is very slow, no need for large sequences + }; + register_benchmarks(bm, "shuffled"); + } + + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); + return 0; +} diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/mismatch.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/mismatch.bench.cpp new file mode 100644 index 0000000000000..5c5961adcd05d --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/mismatch.bench.cpp @@ -0,0 +1,112 @@ +//===----------------------------------------------------------------------===// +// +// 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 "../../GenerateInput.h" + +int main(int argc, char** argv) { + auto std_mismatch_3leg = [](auto first1, auto last1, auto first2, auto) { + return std::mismatch(first1, last1, first2); + }; + auto std_mismatch_4leg = [](auto first1, auto last1, auto first2, auto last2) { + return std::mismatch(first1, last1, first2, last2); + }; + auto std_mismatch_3leg_pred = [](auto first1, auto last1, auto first2, auto) { + return std::mismatch(first1, last1, first2, [](auto x, auto y) { + benchmark::DoNotOptimize(x); + benchmark::DoNotOptimize(y); + return x == y; + }); + }; + auto std_mismatch_4leg_pred = [](auto first1, auto last1, auto first2, auto last2) { + return std::mismatch(first1, last1, first2, last2, [](auto x, auto y) { + benchmark::DoNotOptimize(x); + benchmark::DoNotOptimize(y); + return x == y; + }); + }; + auto ranges_mismatch_4leg_pred = [](auto first1, auto last1, auto first2, auto last2) { + return std::ranges::mismatch(first1, last1, first2, last2, [](auto x, auto y) { + benchmark::DoNotOptimize(x); + benchmark::DoNotOptimize(y); + return x == y; + }); + }; + + // Benchmark {std,ranges}::mismatch where we find the mismatching element at the very end (worst case). + // + // TODO: Look into benchmarking aligned and unaligned memory explicitly + // (currently things happen to be aligned because they are malloced that way) + { + auto bm = [](std::string name, auto mismatch) { + benchmark::RegisterBenchmark( + name, + [mismatch](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + ValueType x = Generate::random(); + ValueType y = random_different_from({x}); + Container c1(size, x); + Container c2(size, x); + c2.back() = y; + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(c1); + benchmark::DoNotOptimize(c2); + auto result = mismatch(c1.begin(), c1.end(), c2.begin(), c2.end()); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(8) + ->Arg(1000) // non power-of-two + ->Arg(1024) + ->Arg(8192) + ->Arg(1 << 20); + }; + + // std::mismatch(it, it, it) + bm.operator()>("std::mismatch(vector) (it, it, it)", std_mismatch_3leg); + bm.operator()>("std::mismatch(deque) (it, it, it)", std_mismatch_3leg); + bm.operator()>("std::mismatch(list) (it, it, it)", std_mismatch_3leg); + + // std::mismatch(it, it, it, pred) + bm.operator()>("std::mismatch(vector) (it, it, it, pred)", std_mismatch_3leg_pred); + bm.operator()>("std::mismatch(deque) (it, it, it, pred)", std_mismatch_3leg_pred); + bm.operator()>("std::mismatch(list) (it, it, it, pred)", std_mismatch_3leg_pred); + + // {std,ranges}::mismatch(it, it, it, it) + bm.operator()>("std::mismatch(vector) (it, it, it, it)", std_mismatch_4leg); + bm.operator()>("std::mismatch(deque) (it, it, it, it)", std_mismatch_4leg); + bm.operator()>("std::mismatch(list) (it, it, it, it)", std_mismatch_4leg); + bm.operator()>("rng::mismatch(vector) (it, it, it, it)", std::ranges::mismatch); + bm.operator()>("rng::mismatch(deque) (it, it, it, it)", std::ranges::mismatch); + bm.operator()>("rng::mismatch(list) (it, it, it, it)", std::ranges::mismatch); + + // {std,ranges}::mismatch(it, it, it, it, pred) + bm.operator()>("std::mismatch(vector) (it, it, it, it, pred)", std_mismatch_4leg_pred); + bm.operator()>("std::mismatch(deque) (it, it, it, it, pred)", std_mismatch_4leg_pred); + bm.operator()>("std::mismatch(list) (it, it, it, it, pred)", std_mismatch_4leg_pred); + bm.operator()>("rng::mismatch(vector) (it, it, it, it, pred)", ranges_mismatch_4leg_pred); + bm.operator()>("rng::mismatch(deque) (it, it, it, it, pred)", ranges_mismatch_4leg_pred); + bm.operator()>("rng::mismatch(list) (it, it, it, it, pred)", ranges_mismatch_4leg_pred); + } + + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); + return 0; +} diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp new file mode 100644 index 0000000000000..a9d4a788d1b03 --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp @@ -0,0 +1,220 @@ +//===----------------------------------------------------------------------===// +// +// 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 "../../GenerateInput.h" + +int main(int argc, char** argv) { + auto std_search = [](auto first1, auto last1, auto first2, auto last2) { + return std::search(first1, last1, first2, last2); + }; + auto std_search_pred = [](auto first1, auto last1, auto first2, auto last2) { + return std::search(first1, last1, first2, last2, [](auto x, auto y) { + benchmark::DoNotOptimize(x); + benchmark::DoNotOptimize(y); + return x == y; + }); + }; + auto ranges_search_pred = [](auto first1, auto last1, auto first2, auto last2) { + return std::ranges::search(first1, last1, first2, last2, [](auto x, auto y) { + benchmark::DoNotOptimize(x); + benchmark::DoNotOptimize(y); + return x == y; + }); + }; + + // Benchmark {std,ranges}::search where the needle is never found (worst case). + { + auto bm = [](std::string name, auto search) { + benchmark::RegisterBenchmark( + name, + [search](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + ValueType x = Generate::random(); + ValueType y = random_different_from({x}); + Container haystack(size, x); + Container needle(size / 10, y); // needle size is 10% of the haystack + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(haystack); + benchmark::DoNotOptimize(needle); + auto result = search(haystack.begin(), haystack.end(), needle.begin(), needle.end()); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(1000) // non power-of-two + ->Arg(1024) + ->Arg(8192) + ->Arg(1 << 20); + }; + // {std,ranges}::search + bm.operator()>("std::search(vector) (no match)", std_search); + bm.operator()>("std::search(deque) (no match)", std_search); + bm.operator()>("std::search(list) (no match)", std_search); + bm.operator()>("rng::search(vector) (no match)", std::ranges::search); + bm.operator()>("rng::search(deque) (no match)", std::ranges::search); + bm.operator()>("rng::search(list) (no match)", std::ranges::search); + + // {std,ranges}::search(pred) + bm.operator()>("std::search(vector, pred) (no match)", std_search_pred); + bm.operator()>("std::search(deque, pred) (no match)", std_search_pred); + bm.operator()>("std::search(list, pred) (no match)", std_search_pred); + bm.operator()>("rng::search(vector, pred) (no match)", ranges_search_pred); + bm.operator()>("rng::search(deque, pred) (no match)", ranges_search_pred); + bm.operator()>("rng::search(list, pred) (no match)", ranges_search_pred); + } + + // Benchmark {std,ranges}::search where we intersperse "near matches" inside the haystack. + { + auto bm = [](std::string name, auto search) { + benchmark::RegisterBenchmark( + name, + [search](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + ValueType x = Generate::random(); + ValueType y = random_different_from({x}); + Container haystack(size, x); + std::size_t n = size / 10; // needle size is 10% of the haystack + assert(n > 0); + Container needle(n, y); + + // intersperse near-matches inside the haystack + { + auto first = haystack.begin(); + for (int i = 0; i != 10; ++i) { + first = std::copy_n(needle.begin(), n - 1, first); + ++first; // this causes the subsequence not to match because it has length n-1 + } + } + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(haystack); + benchmark::DoNotOptimize(needle); + auto result = search(haystack.begin(), haystack.end(), needle.begin(), needle.end()); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(1000) // non power-of-two + ->Arg(1024) + ->Arg(8192); + }; + // {std,ranges}::search + bm.operator()>("std::search(vector) (near matches)", std_search); + bm.operator()>("std::search(deque) (near matches)", std_search); + bm.operator()>("std::search(list) (near matches)", std_search); + bm.operator()>("rng::search(vector) (near matches)", std::ranges::search); + bm.operator()>("rng::search(deque) (near matches)", std::ranges::search); + bm.operator()>("rng::search(list) (near matches)", std::ranges::search); + + // {std,ranges}::search(pred) + bm.operator()>("std::search(vector, pred) (near matches)", std_search_pred); + bm.operator()>("std::search(deque, pred) (near matches)", std_search_pred); + bm.operator()>("std::search(list, pred) (near matches)", std_search_pred); + bm.operator()>("rng::search(vector, pred) (near matches)", ranges_search_pred); + bm.operator()>("rng::search(deque, pred) (near matches)", ranges_search_pred); + bm.operator()>("rng::search(list, pred) (near matches)", ranges_search_pred); + } + + // Special case: the two ranges are the same length (and they are equal, which is the worst case). + { + auto bm = [](std::string name, auto search) { + benchmark::RegisterBenchmark( + name, + [search](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + ValueType x = Generate::random(); + Container haystack(size, x); + Container needle(size, x); + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(haystack); + benchmark::DoNotOptimize(needle); + auto result = search(haystack.begin(), haystack.end(), needle.begin(), needle.end()); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(1000) // non power-of-two + ->Arg(1024) + ->Arg(8192); + }; + // {std,ranges}::search + bm.operator()>("std::search(vector) (same length)", std_search); + bm.operator()>("std::search(deque) (same length)", std_search); + bm.operator()>("std::search(list) (same length)", std_search); + bm.operator()>("rng::search(vector) (same length)", std::ranges::search); + bm.operator()>("rng::search(deque) (same length)", std::ranges::search); + bm.operator()>("rng::search(list) (same length)", std::ranges::search); + + // {std,ranges}::search(pred) + bm.operator()>("std::search(vector, pred) (same length)", std_search_pred); + bm.operator()>("std::search(deque, pred) (same length)", std_search_pred); + bm.operator()>("std::search(list, pred) (same length)", std_search_pred); + bm.operator()>("rng::search(vector, pred) (same length)", ranges_search_pred); + bm.operator()>("rng::search(deque, pred) (same length)", ranges_search_pred); + bm.operator()>("rng::search(list, pred) (same length)", ranges_search_pred); + } + + // Special case: the needle contains a single element (which we never find, i.e. the worst case). + { + auto bm = [](std::string name, auto search) { + benchmark::RegisterBenchmark( + name, + [search](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + ValueType x = Generate::random(); + ValueType y = random_different_from({x}); + Container haystack(size, x); + Container needle(1, y); + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(haystack); + benchmark::DoNotOptimize(needle); + auto result = search(haystack.begin(), haystack.end(), needle.begin(), needle.end()); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(1000) // non power-of-two + ->Arg(1024) + ->Arg(8192); + }; + // {std,ranges}::search + bm.operator()>("std::search(vector) (single element)", std_search); + bm.operator()>("std::search(deque) (single element)", std_search); + bm.operator()>("std::search(list) (single element)", std_search); + bm.operator()>("rng::search(vector) (single element)", std::ranges::search); + bm.operator()>("rng::search(deque) (single element)", std::ranges::search); + bm.operator()>("rng::search(list) (single element)", std::ranges::search); + + // {std,ranges}::search(pred) + bm.operator()>("std::search(vector, pred) (single element)", std_search_pred); + bm.operator()>("std::search(deque, pred) (single element)", std_search_pred); + bm.operator()>("std::search(list, pred) (single element)", std_search_pred); + bm.operator()>("rng::search(vector, pred) (single element)", ranges_search_pred); + bm.operator()>("rng::search(deque, pred) (single element)", ranges_search_pred); + bm.operator()>("rng::search(list, pred) (single element)", ranges_search_pred); + } + + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); + return 0; +} diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp new file mode 100644 index 0000000000000..de404fedaed3a --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp @@ -0,0 +1,88 @@ +//===----------------------------------------------------------------------===// +// +// 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 "../../GenerateInput.h" + +int main(int argc, char** argv) { + auto std_search_n = [](auto first, auto last, auto n, auto const& value) { + return std::search_n(first, last, n, value); + }; + auto std_search_n_pred = [](auto first, auto last, auto n, auto const& value) { + return std::search_n(first, last, n, value, [](auto x, auto y) { + benchmark::DoNotOptimize(x); + benchmark::DoNotOptimize(y); + return x == y; + }); + }; + auto ranges_search_n_pred = [](auto first, auto last, auto n, auto const& value) { + return std::ranges::search_n(first, last, n, value, [](auto x, auto y) { + benchmark::DoNotOptimize(x); + benchmark::DoNotOptimize(y); + return x == y; + }); + }; + + // Benchmark {std,ranges}::search_n where the needle is never found (worst case). + { + auto bm = [](std::string name, auto search_n) { + benchmark::RegisterBenchmark( + name, + [search_n](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + ValueType x = Generate::random(); + ValueType y = random_different_from({x}); + Container haystack(size, x); + std::size_t n = size / 10; // needle size is 10% of the haystack + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(haystack); + benchmark::DoNotOptimize(n); + benchmark::DoNotOptimize(y); + auto result = search_n(haystack.begin(), haystack.end(), n, y); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(1000) // non power-of-two + ->Arg(1024) + ->Arg(8192) + ->Arg(1 << 20); + }; + // {std,ranges}::search_n + bm.operator()>("std::search_n(vector) (no match)", std_search_n); + bm.operator()>("std::search_n(deque) (no match)", std_search_n); + bm.operator()>("std::search_n(list) (no match)", std_search_n); + bm.operator()>("rng::search_n(vector) (no match)", std::ranges::search_n); + bm.operator()>("rng::search_n(deque) (no match)", std::ranges::search_n); + bm.operator()>("rng::search_n(list) (no match)", std::ranges::search_n); + + // {std,ranges}::search_n(pred) + bm.operator()>("std::search_n(vector, pred) (no match)", std_search_n_pred); + bm.operator()>("std::search_n(deque, pred) (no match)", std_search_n_pred); + bm.operator()>("std::search_n(list, pred) (no match)", std_search_n_pred); + bm.operator()>("rng::search_n(vector, pred) (no match)", ranges_search_n_pred); + bm.operator()>("rng::search_n(deque, pred) (no match)", ranges_search_n_pred); + bm.operator()>("rng::search_n(list, pred) (no match)", ranges_search_n_pred); + } + + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); + return 0; +} diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/starts_with.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/starts_with.bench.cpp new file mode 100644 index 0000000000000..2950ad5322e84 --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/starts_with.bench.cpp @@ -0,0 +1,70 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +#include +#include +#include +#include +#include +#include + +#include +#include "../../GenerateInput.h" + +int main(int argc, char** argv) { + auto ranges_starts_with_pred = [](auto first1, auto last1, auto first2, auto last2) { + return std::ranges::starts_with(first1, last1, first2, last2, [](auto x, auto y) { + benchmark::DoNotOptimize(x); + benchmark::DoNotOptimize(y); + return x == y; + }); + }; + + // Benchmark ranges::starts_with where we find the mismatching element at the very end (worst case). + { + auto bm = [](std::string name, auto starts_with) { + benchmark::RegisterBenchmark( + name, + [starts_with](auto& st) { + std::size_t const size = st.range(0); + using ValueType = typename Container::value_type; + ValueType x = Generate::random(); + ValueType y = random_different_from({x}); + Container c1(size, x); + Container c2(size, x); + c2.back() = y; + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(c1); + benchmark::DoNotOptimize(c2); + auto result = starts_with(c1.begin(), c1.end(), c2.begin(), c2.end()); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(8) + ->Arg(1000) // non power-of-two + ->Arg(1024) + ->Arg(8192) + ->Arg(1 << 20); + }; + bm.operator()>("rng::starts_with(vector)", std::ranges::starts_with); + bm.operator()>("rng::starts_with(deque)", std::ranges::starts_with); + bm.operator()>("rng::starts_with(list)", std::ranges::starts_with); + + bm.operator()>("rng::starts_with(vector, pred)", ranges_starts_with_pred); + bm.operator()>("rng::starts_with(deque, pred)", ranges_starts_with_pred); + bm.operator()>("rng::starts_with(list, pred)", ranges_starts_with_pred); + } + + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); + return 0; +} diff --git a/libcxx/test/benchmarks/algorithms/ranges_contains.bench.cpp b/libcxx/test/benchmarks/algorithms/ranges_contains.bench.cpp deleted file mode 100644 index c9a10202c8cfc..0000000000000 --- a/libcxx/test/benchmarks/algorithms/ranges_contains.bench.cpp +++ /dev/null @@ -1,51 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 - -#include -#include -#include -#include - -#include "test_iterators.h" - -static void bm_contains_vector_char(benchmark::State& state) { - std::vector a(state.range(), 'a'); - - for (auto _ : state) { - benchmark::DoNotOptimize(a); - - benchmark::DoNotOptimize(std::ranges::contains(a.begin(), a.end(), 'B')); - } -} -BENCHMARK(bm_contains_vector_char)->RangeMultiplier(16)->Range(16, 16 << 20); - -static void bm_contains_vector_int(benchmark::State& state) { - std::vector a(state.range(), 1); - - for (auto _ : state) { - benchmark::DoNotOptimize(a); - - benchmark::DoNotOptimize(std::ranges::contains(a.begin(), a.end(), 2)); - } -} -BENCHMARK(bm_contains_vector_int)->RangeMultiplier(16)->Range(16, 16 << 20); - -static void bm_contains_vector_bool(benchmark::State& state) { - std::vector a(state.range(), true); - - for (auto _ : state) { - benchmark::DoNotOptimize(a); - - benchmark::DoNotOptimize(std::ranges::contains(a.begin(), a.end(), false)); - } -} -BENCHMARK(bm_contains_vector_bool)->RangeMultiplier(16)->Range(16, 16 << 20); - -BENCHMARK_MAIN(); diff --git a/libcxx/test/benchmarks/algorithms/ranges_ends_with.bench.cpp b/libcxx/test/benchmarks/algorithms/ranges_ends_with.bench.cpp deleted file mode 100644 index c975d164c16d4..0000000000000 --- a/libcxx/test/benchmarks/algorithms/ranges_ends_with.bench.cpp +++ /dev/null @@ -1,109 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 - -#include -#include -#include - -#include "test_iterators.h" -#include - -static void bm_ends_with_contiguous_iter(benchmark::State& state) { - std::vector a(state.range(), 1); - std::vector p(state.range(), 1); - - for (auto _ : state) { - benchmark::DoNotOptimize(a); - benchmark::DoNotOptimize(p); - - auto begin1 = contiguous_iterator(a.data()); - auto end1 = contiguous_iterator(a.data() + a.size()); - auto begin2 = contiguous_iterator(p.data()); - auto end2 = contiguous_iterator(p.data() + p.size()); - - benchmark::DoNotOptimize(std::ranges::ends_with(begin1, end1, begin2, end2)); - } -} -BENCHMARK(bm_ends_with_contiguous_iter)->RangeMultiplier(16)->Range(16, 16 << 20); - -static void bm_ends_with_random_iter(benchmark::State& state) { - std::vector a(state.range(), 1); - std::vector p(state.range(), 1); - - for (auto _ : state) { - benchmark::DoNotOptimize(a); - benchmark::DoNotOptimize(p); - - auto begin1 = random_access_iterator(a.begin()); - auto end1 = random_access_iterator(a.end()); - auto begin2 = random_access_iterator(p.begin()); - auto end2 = random_access_iterator(p.end()); - - benchmark::DoNotOptimize(std::ranges::ends_with(begin1, end1, begin2, end2)); - } -} -BENCHMARK(bm_ends_with_random_iter)->RangeMultiplier(16)->Range(16, 16 << 20); - -static void bm_ends_with_bidirectional_iter(benchmark::State& state) { - std::vector a(state.range(), 1); - std::vector p(state.range(), 1); - - for (auto _ : state) { - benchmark::DoNotOptimize(a); - benchmark::DoNotOptimize(p); - - auto begin1 = bidirectional_iterator(a.begin()); - auto end1 = bidirectional_iterator(a.end()); - auto begin2 = bidirectional_iterator(p.begin()); - auto end2 = bidirectional_iterator(p.end()); - - benchmark::DoNotOptimize(std::ranges::ends_with(begin1, end1, begin2, end2)); - } -} -BENCHMARK(bm_ends_with_bidirectional_iter)->RangeMultiplier(16)->Range(16, 16 << 20); - -static void bm_ends_with_forward_iter(benchmark::State& state) { - std::vector a(state.range(), 1); - std::vector p(state.range(), 1); - - for (auto _ : state) { - benchmark::DoNotOptimize(a); - benchmark::DoNotOptimize(p); - - auto begin1 = forward_iterator(a.begin()); - auto end1 = forward_iterator(a.end()); - auto begin2 = forward_iterator(p.begin()); - auto end2 = forward_iterator(p.end()); - - benchmark::DoNotOptimize(std::ranges::ends_with(begin1, end1, begin2, end2)); - } -} -BENCHMARK(bm_ends_with_forward_iter)->RangeMultiplier(16)->Range(16, 16 << 20); - -static void bm_ends_with_forward_iter_with_size_optimization(benchmark::State& state) { - std::vector a(state.range(), 1); - std::vector p(state.range(), 1); - p.push_back(2); - - for (auto _ : state) { - benchmark::DoNotOptimize(a); - benchmark::DoNotOptimize(p); - - auto begin1 = forward_iterator(a.begin()); - auto end1 = forward_iterator(a.end()); - auto begin2 = forward_iterator(p.begin()); - auto end2 = forward_iterator(p.end()); - - benchmark::DoNotOptimize(std::ranges::ends_with(begin1, end1, begin2, end2)); - } -} -BENCHMARK(bm_ends_with_forward_iter_with_size_optimization)->RangeMultiplier(16)->Range(16, 16 << 20); - -BENCHMARK_MAIN();