From f350b39ed472db8acfc3f19f25abb9730555601e Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Thu, 6 Feb 2025 19:13:16 -0500 Subject: [PATCH 01/21] [libc++] Refactor and add benchmarks from [alg.nonmodifying] --- libcxx/test/benchmarks/GenerateInput.h | 1 + .../benchmarks/algorithms/count.bench.cpp | 37 ---- .../benchmarks/algorithms/equal.bench.cpp | 99 ---------- .../test/benchmarks/algorithms/find.bench.cpp | 90 --------- .../benchmarks/algorithms/for_each.bench.cpp | 25 --- .../benchmarks/algorithms/mismatch.bench.cpp | 58 ------ .../nonmodifying/adjacent_find.bench.cpp | 87 +++++++++ .../nonmodifying/any_all_none_of.bench.cpp | 152 +++++++++++++++ .../nonmodifying/contains.bench.cpp | 80 ++++++++ .../nonmodifying/contains_subrange.bench.cpp | 82 ++++++++ .../algorithms/nonmodifying/count.bench.cpp | 156 +++++++++++++++ .../nonmodifying/ends_with.bench.cpp | 104 ++++++++++ .../algorithms/nonmodifying/equal.bench.cpp | 106 +++++++++++ .../algorithms/nonmodifying/find.bench.cpp | 179 ++++++++++++++++++ .../nonmodifying/find_end.bench.cpp | 102 ++++++++++ .../nonmodifying/find_first_of.bench.cpp | 115 +++++++++++ .../nonmodifying/find_last.bench.cpp | 134 +++++++++++++ .../algorithms/nonmodifying/fold.bench.cpp | 76 ++++++++ .../nonmodifying/for_each.bench.cpp | 57 ++++++ .../nonmodifying/is_permutation.bench.cpp | 157 +++++++++++++++ .../nonmodifying/mismatch.bench.cpp | 111 +++++++++++ .../algorithms/nonmodifying/search.bench.cpp | 137 ++++++++++++++ .../nonmodifying/search_n.bench.cpp | 138 ++++++++++++++ .../nonmodifying/starts_with.bench.cpp | 69 +++++++ .../algorithms/ranges_contains.bench.cpp | 51 ----- .../algorithms/ranges_ends_with.bench.cpp | 109 ----------- 26 files changed, 2043 insertions(+), 469 deletions(-) delete mode 100644 libcxx/test/benchmarks/algorithms/count.bench.cpp delete mode 100644 libcxx/test/benchmarks/algorithms/equal.bench.cpp delete mode 100644 libcxx/test/benchmarks/algorithms/find.bench.cpp delete mode 100644 libcxx/test/benchmarks/algorithms/for_each.bench.cpp delete mode 100644 libcxx/test/benchmarks/algorithms/mismatch.bench.cpp create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/adjacent_find.bench.cpp create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/any_all_none_of.bench.cpp create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/count.bench.cpp create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/equal.bench.cpp create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/find.bench.cpp create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/for_each.bench.cpp create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/is_permutation.bench.cpp create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/mismatch.bench.cpp create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp create mode 100644 libcxx/test/benchmarks/algorithms/nonmodifying/starts_with.bench.cpp delete mode 100644 libcxx/test/benchmarks/algorithms/ranges_contains.bench.cpp delete mode 100644 libcxx/test/benchmarks/algorithms/ranges_ends_with.bench.cpp 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..1f6e20dfa4527 --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/adjacent_find.bench.cpp @@ -0,0 +1,87 @@ +//===----------------------------------------------------------------------===// +// +// 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(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..363ed96f6d201 --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/any_all_none_of.bench.cpp @@ -0,0 +1,152 @@ +//===----------------------------------------------------------------------===// +// +// 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 bail out early + // (after visiting 25% of the elements). + { + 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); + *std::next(c.begin(), size / 4) = y; // bail out after the first 25% elements + + for (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(8192) + ->Arg(32768); + }; + + // any_of + bm.operator()>("std::any_of(vector) (bail 25%)", std_any_of); + bm.operator()>("std::any_of(deque) (bail 25%)", std_any_of); + bm.operator()>("std::any_of(list) (bail 25%)", std_any_of); + bm.operator()>("rng::any_of(vector) (bail 25%)", std::ranges::any_of); + bm.operator()>("rng::any_of(deque) (bail 25%)", std::ranges::any_of); + bm.operator()>("rng::any_of(list) (bail 25%)", std::ranges::any_of); + + // all_of + bm.operator()>("std::all_of(vector) (bail 25%)", std_all_of); + bm.operator()>("std::all_of(deque) (bail 25%)", std_all_of); + bm.operator()>("std::all_of(list) (bail 25%)", std_all_of); + bm.operator()>("rng::all_of(vector) (bail 25%)", ranges_all_of); + bm.operator()>("rng::all_of(deque) (bail 25%)", ranges_all_of); + bm.operator()>("rng::all_of(list) (bail 25%)", ranges_all_of); + + // none_of + bm.operator()>("std::none_of(vector) (bail 25%)", std_none_of); + bm.operator()>("std::none_of(deque) (bail 25%)", std_none_of); + bm.operator()>("std::none_of(list) (bail 25%)", std_none_of); + bm.operator()>("rng::none_of(vector) (bail 25%)", ranges_none_of); + bm.operator()>("rng::none_of(deque) (bail 25%)", ranges_none_of); + bm.operator()>("rng::none_of(list) (bail 25%)", ranges_none_of); + } + + // Benchmark {std,ranges}::{any_of,all_of,none_of} where we process the whole sequence. + { + 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 (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(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..c0fbae11a218a --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp @@ -0,0 +1,80 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +int main(int argc, char** argv) { + // Benchmark ranges::contains where we bail out early (after visiting 25% of the elements). + { + auto bm = [](std::string name) { + benchmark::RegisterBenchmark( + name, + [](auto& st) { + std::size_t const size = st.range(0); + Container c(size, 1); + *std::next(c.begin(), size / 4) = 42; // bail out after checking 25% of values + auto first = c.begin(); + auto last = c.end(); + + for (auto _ : st) { + benchmark::DoNotOptimize(c); + auto result = std::ranges::contains(first, last, 42); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(8) + ->Arg(32) + ->Arg(8192) + ->Arg(1 << 20); + }; + bm.operator()>("rng::contains(vector) (bail 25%)"); + bm.operator()>("rng::contains(deque) (bail 25%)"); + bm.operator()>("rng::contains(list) (bail 25%)"); + } + + // Benchmark ranges::contains where we process the whole sequence. + { + auto bm = [](std::string name) { + benchmark::RegisterBenchmark( + name, + [](auto& st) { + std::size_t const size = st.range(0); + Container c(size, 1); + auto first = c.begin(); + auto last = c.end(); + + for (auto _ : st) { + benchmark::DoNotOptimize(c); + auto result = std::ranges::contains(first, last, 42); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(8) + ->Arg(32) + ->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..6b585acc7c752 --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp @@ -0,0 +1,82 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +#include +#include +#include +#include +#include +#include + +#include + +int main(int argc, char** argv) { + // Benchmark ranges::contains_subrange where we find our target starting at 25% of the elements + { + auto bm = [](std::string name) { + benchmark::RegisterBenchmark( + name, + [](auto& st) { + std::size_t const size = st.range(0); + Container c(size, 1); + Container subrange(size / 10, 42); // subrange of length 10% of the full range + + // At 25% of the range, put the subrange we're going to find + std::ranges::copy(subrange, std::next(c.begin(), c.size() / 4)); + + for (auto _ : st) { + benchmark::DoNotOptimize(c); + benchmark::DoNotOptimize(subrange); + auto result = std::ranges::contains_subrange(c, subrange); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(16) + ->Arg(32) + ->Arg(8192) + ->Arg(1 << 20); + }; + bm.operator()>("rng::contains_subrange(vector) (bail 25%)"); + bm.operator()>("rng::contains_subrange(deque) (bail 25%)"); + bm.operator()>("rng::contains_subrange(list) (bail 25%)"); + } + + // Benchmark ranges::contains_subrange where we never find our target + { + auto bm = [](std::string name) { + benchmark::RegisterBenchmark( + name, + [](auto& st) { + std::size_t const size = st.range(0); + Container c(size, 1); + Container subrange(size / 10, 42); // subrange of length 10% of the full range, but we'll never find it + + for (auto _ : st) { + benchmark::DoNotOptimize(c); + benchmark::DoNotOptimize(subrange); + auto result = std::ranges::contains_subrange(c, subrange); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(16) + ->Arg(32) + ->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::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..13d8f9f2690e9 --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/count.bench.cpp @@ -0,0 +1,156 @@ +//===----------------------------------------------------------------------===// +// +// 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,count_if} on a sequence where only a few elements are counted. + // In theory, we could blaze through contiguous sequences where there are no elements to count. + { + 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 % (size / 5) == 0 ? x : y); // intersperse 5 elements to count at regular intervals + } + + 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) (sparse)", std_count); + bm.operator()>("std::count(deque) (sparse)", std_count); + bm.operator()>("std::count(list) (sparse)", std_count); + bm.operator()>("rng::count(vector) (sparse)", ranges_count); + bm.operator()>("rng::count(deque) (sparse)", ranges_count); + bm.operator()>("rng::count(list) (sparse)", ranges_count); + + // count_if + bm.operator()>("std::count_if(vector) (sparse)", std_count_if); + bm.operator()>("std::count_if(deque) (sparse)", std_count_if); + bm.operator()>("std::count_if(list) (sparse)", std_count_if); + bm.operator()>("rng::count_if(vector) (sparse)", ranges_count_if); + bm.operator()>("rng::count_if(deque) (sparse)", ranges_count_if); + bm.operator()>("rng::count_if(list) (sparse)", 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(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..08ff693916f67 --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp @@ -0,0 +1,104 @@ +//===----------------------------------------------------------------------===// +// +// 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 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); + c2.back() = 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(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(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); + } + + // 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); + 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(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(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); + } + + 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..ad73ba3610abf --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/equal.bench.cpp @@ -0,0 +1,106 @@ +//===----------------------------------------------------------------------===// +// +// 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(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::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..3be7df0ac1131 --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find.bench.cpp @@ -0,0 +1,179 @@ +//===----------------------------------------------------------------------===// +// +// 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(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(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..70576acd79792 --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp @@ -0,0 +1,102 @@ +//===----------------------------------------------------------------------===// +// +// 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_end = [](auto first1, auto last1, auto first2, auto last2) { + return std::find_end(first1, last1, first2, last2); + }; + + // Benchmark {std,ranges}::find_end where the subsequence is found + // 25% into the sequence + { + 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 c(size, x); + Container subrange(size / 10, y); // subrange of length 10% of the full range + + // put the element we're searching for at 25% of the sequence + std::ranges::copy(subrange, std::next(c.begin(), c.size() / 4)); + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(c); + benchmark::DoNotOptimize(subrange); + auto result = find_end(c.begin(), c.end(), subrange.begin(), subrange.end()); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(1024) + ->Arg(8192) + ->Arg(1 << 20); + }; + bm.operator()>("std::find_end(vector) (bail 25%)", std_find_end); + bm.operator()>("std::find_end(deque) (bail 25%)", std_find_end); + bm.operator()>("std::find_end(list) (bail 25%)", std_find_end); + bm.operator()>("rng::find_end(vector) (bail 25%)", std::ranges::find_end); + bm.operator()>("rng::find_end(deque) (bail 25%)", std::ranges::find_end); + bm.operator()>("rng::find_end(list) (bail 25%)", std::ranges::find_end); + } + + // Benchmark {std,ranges}::find_end where the subsequence is found + // 90% into the sequence (i.e. near 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 c(size, x); + Container subrange(size / 10, y); // subrange of length 10% of the full range + + // put the element we're searching for at 90% of the sequence + std::ranges::copy(subrange, std::next(c.begin(), 9 * (c.size() / 10))); + + for ([[maybe_unused]] auto _ : st) { + benchmark::DoNotOptimize(c); + benchmark::DoNotOptimize(subrange); + auto result = find_end(c.begin(), c.end(), subrange.begin(), subrange.end()); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(1024) + ->Arg(8192) + ->Arg(1 << 20); + }; + bm.operator()>("std::find_end(vector) (bail 90%)", std_find_end); + bm.operator()>("std::find_end(deque) (bail 90%)", std_find_end); + bm.operator()>("std::find_end(list) (bail 90%)", std_find_end); + bm.operator()>("rng::find_end(vector) (bail 90%)", std::ranges::find_end); + bm.operator()>("rng::find_end(deque) (bail 90%)", std::ranges::find_end); + bm.operator()>("rng::find_end(list) (bail 90%)", std::ranges::find_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..cf83c2761232c --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp @@ -0,0 +1,115 @@ +//===----------------------------------------------------------------------===// +// +// 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); + }; + + // Benchmark {std,ranges}::find_first_of where we have a hit at 25% of the haystack + // and at the end of the needle. This measures how quickly we're able to search inside + // the 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}); + ValueType z = random_different_from({x, y}); + Container haystack(size, x); + Container needle(size, y); + needle.back() = z; // hit at the very end of the needle + + // put the needle at 25% of the haystack + *std::next(haystack.begin(), haystack.size() / 4) = z; + + 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(1024) + ->Arg(8192); + }; + bm.operator()>("std::find_first_of(vector) (25% haystack, late needle)", std_find_first_of); + bm.operator()>("std::find_first_of(deque) (25% haystack, late needle)", std_find_first_of); + bm.operator()>("std::find_first_of(list) (25% haystack, late needle)", std_find_first_of); + bm.operator()>( + "rng::find_first_of(vector) (25% haystack, late needle)", std::ranges::find_first_of); + bm.operator()>( + "rng::find_first_of(deque) (25% haystack, late needle)", std::ranges::find_first_of); + bm.operator()>( + "rng::find_first_of(list) (25% haystack, late needle)", std::ranges::find_first_of); + } + + // Benchmark {std,ranges}::find_first_of where we have a hit at 90% of the haystack + // but at the beginning of the needle. This measures how quickly we're able to search + // inside 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}); + ValueType z = random_different_from({x, y}); + Container haystack(size, x); + Container needle(size, y); + *std::next(needle.begin(), needle.size() / 10) = z; // hit at 10% of the needle + + // put the needle at 90% of the haystack + *std::next(haystack.begin(), 9 * (haystack.size() / 10)) = z; + + 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(1024) + ->Arg(8192); + }; + bm.operator()>("std::find_first_of(vector) (90% haystack, early needle)", std_find_first_of); + bm.operator()>("std::find_first_of(deque) (90% haystack, early needle)", std_find_first_of); + bm.operator()>("std::find_first_of(list) (90% haystack, early needle)", std_find_first_of); + bm.operator()>( + "rng::find_first_of(vector) (90% haystack, early needle)", std::ranges::find_first_of); + bm.operator()>( + "rng::find_first_of(deque) (90% haystack, early needle)", std::ranges::find_first_of); + bm.operator()>( + "rng::find_first_of(list) (90% haystack, early needle)", std::ranges::find_first_of); + } + + 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..536d7d4f0350c --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp @@ -0,0 +1,134 @@ +//===----------------------------------------------------------------------===// +// +// 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 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 25% 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 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_last(c.begin(), c.end(), y); + benchmark::DoNotOptimize(result); + } + }) + ->Arg(8) + ->Arg(1024) + ->Arg(8192) + ->Arg(1 << 20); + }; + + // find_last + bm.operator()>("rng::find_last(vector) (bail 25%)", std::ranges::find_last); + bm.operator()>("rng::find_last(vector) (bail 25%)", std::ranges::find_last); + bm.operator()>("rng::find_last(deque) (bail 25%)", std::ranges::find_last); + bm.operator()>("rng::find_last(list) (bail 25%)", std::ranges::find_last); + + // find_last_if + bm.operator()>("rng::find_last_if(vector) (bail 25%)", ranges_find_last_if); + bm.operator()>("rng::find_last_if(vector) (bail 25%)", ranges_find_last_if); + bm.operator()>("rng::find_last_if(deque) (bail 25%)", ranges_find_last_if); + bm.operator()>("rng::find_last_if(list) (bail 25%)", ranges_find_last_if); + + // find_last_if_not + bm.operator()>("rng::find_last_if_not(vector) (bail 25%)", ranges_find_last_if_not); + bm.operator()>("rng::find_last_if_not(vector) (bail 25%)", ranges_find_last_if_not); + bm.operator()>("rng::find_last_if_not(deque) (bail 25%)", ranges_find_last_if_not); + bm.operator()>("rng::find_last_if_not(list) (bail 25%)", 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(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); + + // 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); + + // 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); + } + + 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..d9b6dbedf3186 --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp @@ -0,0 +1,76 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +#include +#include +#include +#include +#include +#include +#include +#include + +#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; + ValueType const limit = 1000; // ensure we never overflow in the addition + assert(size <= std::numeric_limits::max()); + assert(std::numeric_limits::max() > static_cast(size) * limit); + assert(std::numeric_limits::min() < static_cast(size) * limit * -1); + + Container c; + std::generate_n(std::back_inserter(c), size, [&] { + return std::clamp(Generate::random(), -1 * limit, limit); + }); + ValueType init = c.back(); + c.pop_back(); + + auto f = [](auto x, auto y) { + benchmark::DoNotOptimize(x); + benchmark::DoNotOptimize(y); + return x + y; + }; + + for (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(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); + + // 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..693403bc8f671 --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/for_each.bench.cpp @@ -0,0 +1,57 @@ +//===----------------------------------------------------------------------===// +// +// 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 (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(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..41d18ec3d9c1a --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/is_permutation.bench.cpp @@ -0,0 +1,157 @@ +//===----------------------------------------------------------------------===// +// +// 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_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..630701a0440c8 --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/mismatch.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_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(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..6c6a26509cc62 --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp @@ -0,0 +1,137 @@ +//===----------------------------------------------------------------------===// +// +// 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 + { + 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(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(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); + } + + 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..d3127e0b1b023 --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp @@ -0,0 +1,138 @@ +//===----------------------------------------------------------------------===// +// +// 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 + { + 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(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 {std,ranges}::search_n where we intersperse "near matches" inside the haystack + { + 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 + assert(n > 0); + + // intersperse near-matches inside the haystack + { + auto first = haystack.begin(); + for (int i = 0; i != 10; ++i) { + first = std::fill_n(first, n - 1, y); + ++first; // this causes the subsequence not to match because it has length n-1 + } + } + + 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(1024) + ->Arg(8192); + }; + // {std,ranges}::search_n + bm.operator()>("std::search_n(vector) (near matches)", std_search_n); + bm.operator()>("std::search_n(deque) (near matches)", std_search_n); + bm.operator()>("std::search_n(list) (near matches)", std_search_n); + bm.operator()>("rng::search_n(vector) (near matches)", std::ranges::search_n); + bm.operator()>("rng::search_n(deque) (near matches)", std::ranges::search_n); + bm.operator()>("rng::search_n(list) (near matches)", std::ranges::search_n); + + // {std,ranges}::search_n(pred) + bm.operator()>("std::search_n(vector, pred) (near matches)", std_search_n_pred); + bm.operator()>("std::search_n(deque, pred) (near matches)", std_search_n_pred); + bm.operator()>("std::search_n(list, pred) (near matches)", std_search_n_pred); + bm.operator()>("rng::search_n(vector, pred) (near matches)", ranges_search_n_pred); + bm.operator()>("rng::search_n(deque, pred) (near matches)", ranges_search_n_pred); + bm.operator()>("rng::search_n(list, pred) (near matches)", 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..cfaa2c9dec0eb --- /dev/null +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/starts_with.bench.cpp @@ -0,0 +1,69 @@ +//===----------------------------------------------------------------------===// +// +// 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 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(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(); From 63f0c77528dfa421faefd6ba3b11a4585fa3a8a8 Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Fri, 21 Feb 2025 12:38:46 -0500 Subject: [PATCH 02/21] Fix modules issues --- libcxx/include/module.modulemap | 20 +++++++++++++++---- .../algorithms/nonmodifying/fold.bench.cpp | 1 + .../nonmodifying/is_permutation.bench.cpp | 1 + 3 files changed, 18 insertions(+), 4 deletions(-) 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/algorithms/nonmodifying/fold.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp index d9b6dbedf3186..c000d01acd540 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/is_permutation.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/is_permutation.bench.cpp index 41d18ec3d9c1a..d79e666836b8b 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/is_permutation.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/is_permutation.bench.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include From 899c1179ff5f2548cebb56626f35a754689e8d98 Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Fri, 21 Feb 2025 21:57:20 -0500 Subject: [PATCH 03/21] Fix -Wsign-compare --- libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp index c000d01acd540..4e8fde69dc0cb 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp @@ -31,7 +31,7 @@ int main(int argc, char** argv) { std::size_t const size = st.range(0); using ValueType = typename Container::value_type; ValueType const limit = 1000; // ensure we never overflow in the addition - assert(size <= std::numeric_limits::max()); + assert(static_cast(size) <= std::numeric_limits::max()); assert(std::numeric_limits::max() > static_cast(size) * limit); assert(std::numeric_limits::min() < static_cast(size) * limit * -1); From c5645d028c9527c53f77180f826b617e36facf26 Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Tue, 25 Feb 2025 12:39:17 -0500 Subject: [PATCH 04/21] Add TODO --- libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp index 4e8fde69dc0cb..84a03582d4cbf 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp @@ -64,7 +64,7 @@ int main(int argc, char** argv) { bm.operator()>("rng::fold_left(deque)", std::ranges::fold_left); bm.operator()>("rng::fold_left(list)", std::ranges::fold_left); - // fold_right not implemented yet + // 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); From ed226e2ed013930b98eb8be3ea6a3310bebe94a2 Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Tue, 25 Feb 2025 12:42:41 -0500 Subject: [PATCH 05/21] Use ValueType instead of int --- .../nonmodifying/contains.bench.cpp | 19 ++++++++++++++----- .../nonmodifying/contains_subrange.bench.cpp | 15 +++++++++++---- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp index c0fbae11a218a..a6301d5598e6c 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp @@ -16,6 +16,7 @@ #include #include +#include "../../GenerateInput.h" int main(int argc, char** argv) { // Benchmark ranges::contains where we bail out early (after visiting 25% of the elements). @@ -25,14 +26,18 @@ int main(int argc, char** argv) { name, [](auto& st) { std::size_t const size = st.range(0); - Container c(size, 1); - *std::next(c.begin(), size / 4) = 42; // bail out after checking 25% of values + using ValueType = typename Container::value_type; + ValueType x = Generate::random(); + ValueType y = random_different_from({x}); + Container c(size, x); + *std::next(c.begin(), size / 4) = y; // bail out after checking 25% of values auto first = c.begin(); auto last = c.end(); for (auto _ : st) { benchmark::DoNotOptimize(c); - auto result = std::ranges::contains(first, last, 42); + benchmark::DoNotOptimize(y); + auto result = std::ranges::contains(first, last, y); benchmark::DoNotOptimize(result); } }) @@ -53,13 +58,17 @@ int main(int argc, char** argv) { name, [](auto& st) { std::size_t const size = st.range(0); - Container c(size, 1); + 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 (auto _ : st) { benchmark::DoNotOptimize(c); - auto result = std::ranges::contains(first, last, 42); + benchmark::DoNotOptimize(y); + auto result = std::ranges::contains(first, last, y); benchmark::DoNotOptimize(result); } }) diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp index 6b585acc7c752..456297afe93ba 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp @@ -16,6 +16,7 @@ #include #include +#include "../../GenerateInput.h" int main(int argc, char** argv) { // Benchmark ranges::contains_subrange where we find our target starting at 25% of the elements @@ -25,8 +26,11 @@ int main(int argc, char** argv) { name, [](auto& st) { std::size_t const size = st.range(0); - Container c(size, 1); - Container subrange(size / 10, 42); // subrange of length 10% of the full range + using ValueType = typename Container::value_type; + ValueType x = Generate::random(); + ValueType y = random_different_from({x}); + Container c(size, x); + Container subrange(size / 10, y); // subrange of length 10% of the full range // At 25% of the range, put the subrange we're going to find std::ranges::copy(subrange, std::next(c.begin(), c.size() / 4)); @@ -55,8 +59,11 @@ int main(int argc, char** argv) { name, [](auto& st) { std::size_t const size = st.range(0); - Container c(size, 1); - Container subrange(size / 10, 42); // subrange of length 10% of the full range, but we'll never find it + using ValueType = typename Container::value_type; + ValueType x = Generate::random(); + ValueType y = random_different_from({x}); + Container c(size, x); + Container subrange(size / 10, y); // subrange of length 10% of the full range, but we'll never find it for (auto _ : st) { benchmark::DoNotOptimize(c); From dd8ddd555e2fd81674adc712414299410aef58ae Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Tue, 25 Feb 2025 12:49:40 -0500 Subject: [PATCH 06/21] Add missing predicate overloads --- .../nonmodifying/find_end.bench.cpp | 32 ++++++++++++++ .../nonmodifying/find_first_of.bench.cpp | 43 +++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp index 70576acd79792..419e2dd7406cb 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp @@ -22,6 +22,20 @@ 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; + }); + }; // Benchmark {std,ranges}::find_end where the subsequence is found // 25% into the sequence @@ -51,12 +65,21 @@ int main(int argc, char** argv) { ->Arg(8192) ->Arg(1 << 20); }; + // {std,ranges}::find_end(it1, it1, it2, it2) bm.operator()>("std::find_end(vector) (bail 25%)", std_find_end); bm.operator()>("std::find_end(deque) (bail 25%)", std_find_end); bm.operator()>("std::find_end(list) (bail 25%)", std_find_end); bm.operator()>("rng::find_end(vector) (bail 25%)", std::ranges::find_end); bm.operator()>("rng::find_end(deque) (bail 25%)", std::ranges::find_end); bm.operator()>("rng::find_end(list) (bail 25%)", std::ranges::find_end); + + // {std,ranges}::find_end(it1, it1, it2, it2, pred) + bm.operator()>("std::find_end(vector, pred) (bail 25%)", std_find_end_pred); + bm.operator()>("std::find_end(deque, pred) (bail 25%)", std_find_end_pred); + bm.operator()>("std::find_end(list, pred) (bail 25%)", std_find_end_pred); + bm.operator()>("rng::find_end(vector, pred) (bail 25%)", ranges_find_end_pred); + bm.operator()>("rng::find_end(deque, pred) (bail 25%)", ranges_find_end_pred); + bm.operator()>("rng::find_end(list, pred) (bail 25%)", ranges_find_end_pred); } // Benchmark {std,ranges}::find_end where the subsequence is found @@ -87,12 +110,21 @@ int main(int argc, char** argv) { ->Arg(8192) ->Arg(1 << 20); }; + // {std,ranges}::find_end(it1, it1, it2, it2) bm.operator()>("std::find_end(vector) (bail 90%)", std_find_end); bm.operator()>("std::find_end(deque) (bail 90%)", std_find_end); bm.operator()>("std::find_end(list) (bail 90%)", std_find_end); bm.operator()>("rng::find_end(vector) (bail 90%)", std::ranges::find_end); bm.operator()>("rng::find_end(deque) (bail 90%)", std::ranges::find_end); bm.operator()>("rng::find_end(list) (bail 90%)", std::ranges::find_end); + + // {std,ranges}::find_end(it1, it1, it2, it2, pred) + bm.operator()>("std::find_end(vector, pred) (bail 90%)", std_find_end_pred); + bm.operator()>("std::find_end(deque, pred) (bail 90%)", std_find_end_pred); + bm.operator()>("std::find_end(list, pred) (bail 90%)", std_find_end_pred); + bm.operator()>("rng::find_end(vector, pred) (bail 90%)", ranges_find_end_pred); + bm.operator()>("rng::find_end(deque, pred) (bail 90%)", ranges_find_end_pred); + bm.operator()>("rng::find_end(list, pred) (bail 90%)", ranges_find_end_pred); } benchmark::Initialize(&argc, argv); diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp index cf83c2761232c..80fe862672299 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp @@ -23,6 +23,20 @@ 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 have a hit at 25% of the haystack // and at the end of the needle. This measures how quickly we're able to search inside @@ -55,6 +69,7 @@ int main(int argc, char** argv) { ->Arg(1024) ->Arg(8192); }; + // {std,ranges}::find_first_of(it1, it1, it2, it2) bm.operator()>("std::find_first_of(vector) (25% haystack, late needle)", std_find_first_of); bm.operator()>("std::find_first_of(deque) (25% haystack, late needle)", std_find_first_of); bm.operator()>("std::find_first_of(list) (25% haystack, late needle)", std_find_first_of); @@ -64,6 +79,19 @@ int main(int argc, char** argv) { "rng::find_first_of(deque) (25% haystack, late needle)", std::ranges::find_first_of); bm.operator()>( "rng::find_first_of(list) (25% haystack, late needle)", std::ranges::find_first_of); + + // {std,ranges}::find_first_of(it1, it1, it2, it2, pred) + bm.operator()>( + "std::find_first_of(vector, pred) (25% haystack, late needle)", std_find_first_of); + bm.operator()>( + "std::find_first_of(deque, pred) (25% haystack, late needle)", std_find_first_of); + bm.operator()>("std::find_first_of(list, pred) (25% haystack, late needle)", std_find_first_of); + bm.operator()>( + "rng::find_first_of(vector, pred) (25% haystack, late needle)", std::ranges::find_first_of); + bm.operator()>( + "rng::find_first_of(deque, pred) (25% haystack, late needle)", std::ranges::find_first_of); + bm.operator()>( + "rng::find_first_of(list, pred) (25% haystack, late needle)", std::ranges::find_first_of); } // Benchmark {std,ranges}::find_first_of where we have a hit at 90% of the haystack @@ -97,6 +125,7 @@ int main(int argc, char** argv) { ->Arg(1024) ->Arg(8192); }; + // {std,ranges}::find_first_of(it1, it1, it2, it2) bm.operator()>("std::find_first_of(vector) (90% haystack, early needle)", std_find_first_of); bm.operator()>("std::find_first_of(deque) (90% haystack, early needle)", std_find_first_of); bm.operator()>("std::find_first_of(list) (90% haystack, early needle)", std_find_first_of); @@ -106,6 +135,20 @@ int main(int argc, char** argv) { "rng::find_first_of(deque) (90% haystack, early needle)", std::ranges::find_first_of); bm.operator()>( "rng::find_first_of(list) (90% haystack, early needle)", std::ranges::find_first_of); + + // {std,ranges}::find_first_of(it1, it1, it2, it2, pred) + bm.operator()>( + "std::find_first_of(vector, pred) (90% haystack, early needle)", std_find_first_of_pred); + bm.operator()>( + "std::find_first_of(deque, pred) (90% haystack, early needle)", std_find_first_of_pred); + bm.operator()>( + "std::find_first_of(list, pred) (90% haystack, early needle)", std_find_first_of_pred); + bm.operator()>( + "rng::find_first_of(vector, pred) (90% haystack, early needle)", ranges_find_first_of_pred); + bm.operator()>( + "rng::find_first_of(deque, pred) (90% haystack, early needle)", ranges_find_first_of_pred); + bm.operator()>( + "rng::find_first_of(list, pred) (90% haystack, early needle)", ranges_find_first_of_pred); } benchmark::Initialize(&argc, argv); From 84f9db205e175fa44de142bc84a9dedb7e0bfd50 Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Tue, 25 Feb 2025 12:54:03 -0500 Subject: [PATCH 07/21] Take by value in predicates --- .../algorithms/nonmodifying/is_permutation.bench.cpp | 6 +++--- .../benchmarks/algorithms/nonmodifying/mismatch.bench.cpp | 6 +++--- .../benchmarks/algorithms/nonmodifying/search.bench.cpp | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/is_permutation.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/is_permutation.bench.cpp index d79e666836b8b..3e4d21945bf06 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/is_permutation.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/is_permutation.bench.cpp @@ -27,21 +27,21 @@ int main(int argc, char** argv) { 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) { + 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) { + 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) { + return std::ranges::is_permutation(first1, last1, first2, last2, [](auto x, auto y) { benchmark::DoNotOptimize(x); benchmark::DoNotOptimize(y); return x == y; diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/mismatch.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/mismatch.bench.cpp index 630701a0440c8..af797a7aa7e9f 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/mismatch.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/mismatch.bench.cpp @@ -26,21 +26,21 @@ int main(int argc, char** argv) { 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) { + 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) { + 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) { + return std::ranges::mismatch(first1, last1, first2, last2, [](auto x, auto y) { benchmark::DoNotOptimize(x); benchmark::DoNotOptimize(y); return x == y; diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp index 6c6a26509cc62..776be2c96ac4d 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp @@ -24,14 +24,14 @@ int main(int argc, char** argv) { 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) { + 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) { + return std::ranges::search(first1, last1, first2, last2, [](auto x, auto y) { benchmark::DoNotOptimize(x); benchmark::DoNotOptimize(y); return x == y; From 908850765f2294dd4d08d207639cceae4703485f Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Tue, 25 Feb 2025 13:00:23 -0500 Subject: [PATCH 08/21] Run benchmarks on non-powers-of-two --- .../benchmarks/algorithms/nonmodifying/adjacent_find.bench.cpp | 1 + .../algorithms/nonmodifying/any_all_none_of.bench.cpp | 2 ++ .../test/benchmarks/algorithms/nonmodifying/contains.bench.cpp | 2 ++ .../algorithms/nonmodifying/contains_subrange.bench.cpp | 2 ++ libcxx/test/benchmarks/algorithms/nonmodifying/count.bench.cpp | 2 ++ .../test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp | 2 ++ libcxx/test/benchmarks/algorithms/nonmodifying/equal.bench.cpp | 1 + libcxx/test/benchmarks/algorithms/nonmodifying/find.bench.cpp | 2 ++ .../test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp | 2 ++ .../benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp | 2 ++ .../test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp | 2 ++ libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp | 1 + .../test/benchmarks/algorithms/nonmodifying/for_each.bench.cpp | 1 + .../test/benchmarks/algorithms/nonmodifying/mismatch.bench.cpp | 1 + libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp | 2 ++ .../test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp | 2 ++ .../benchmarks/algorithms/nonmodifying/starts_with.bench.cpp | 1 + 17 files changed, 28 insertions(+) diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/adjacent_find.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/adjacent_find.bench.cpp index 1f6e20dfa4527..2bfdffdabdf02 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/adjacent_find.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/adjacent_find.bench.cpp @@ -58,6 +58,7 @@ int main(int argc, char** argv) { } }) ->Arg(8) + ->Arg(50) // non power-of-two ->Arg(1024) ->Arg(8192) ->Arg(1 << 20); 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 index 363ed96f6d201..66eba4e50f83d 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/any_all_none_of.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/any_all_none_of.bench.cpp @@ -64,6 +64,7 @@ int main(int argc, char** argv) { }) ->Arg(8) ->Arg(32) + ->Arg(50) // non power-of-two ->Arg(8192) ->Arg(32768); }; @@ -116,6 +117,7 @@ int main(int argc, char** argv) { }) ->Arg(8) ->Arg(32) + ->Arg(50) // non power-of-two ->Arg(8192) ->Arg(32768); }; diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp index a6301d5598e6c..50d8c5fd331cd 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp @@ -43,6 +43,7 @@ int main(int argc, char** argv) { }) ->Arg(8) ->Arg(32) + ->Arg(50) // non power-of-two ->Arg(8192) ->Arg(1 << 20); }; @@ -74,6 +75,7 @@ int main(int argc, char** argv) { }) ->Arg(8) ->Arg(32) + ->Arg(50) // non power-of-two ->Arg(8192) ->Arg(1 << 20); }; diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp index 456297afe93ba..96676bd2921f5 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp @@ -44,6 +44,7 @@ int main(int argc, char** argv) { }) ->Arg(16) ->Arg(32) + ->Arg(50) // non power-of-two ->Arg(8192) ->Arg(1 << 20); }; @@ -74,6 +75,7 @@ int main(int argc, char** argv) { }) ->Arg(16) ->Arg(32) + ->Arg(50) // non power-of-two ->Arg(8192) ->Arg(1 << 20); }; diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/count.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/count.bench.cpp index 13d8f9f2690e9..a4edd4402ce74 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/count.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/count.bench.cpp @@ -104,6 +104,7 @@ int main(int argc, char** argv) { } }) ->Arg(8) + ->Arg(50) // non power-of-two ->Arg(1024) ->Arg(8192) ->Arg(1 << 20); @@ -141,6 +142,7 @@ int main(int argc, char** argv) { benchmark::DoNotOptimize(result); } }) + ->Arg(1000) // non power-of-two ->Arg(1024) ->Arg(8192) ->Arg(1 << 20); diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp index 08ff693916f67..cf4e6b5781ec5 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp @@ -49,6 +49,7 @@ int main(int argc, char** argv) { } }) ->Arg(8) + ->Arg(50) // non power-of-two ->Arg(1024) ->Arg(8192) ->Arg(1 << 20); @@ -84,6 +85,7 @@ int main(int argc, char** argv) { } }) ->Arg(8) + ->Arg(50) // non power-of-two ->Arg(1024) ->Arg(8192) ->Arg(1 << 20); diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/equal.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/equal.bench.cpp index ad73ba3610abf..9feef4e1ac22b 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/equal.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/equal.bench.cpp @@ -67,6 +67,7 @@ int main(int argc, char** argv) { } }) ->Arg(8) + ->Arg(50) // non power-of-two ->Arg(1024) ->Arg(8192) ->Arg(1 << 20); diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find.bench.cpp index 3be7df0ac1131..b2ead1cc75585 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/find.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find.bench.cpp @@ -133,6 +133,7 @@ int main(int argc, char** argv) { } }) ->Arg(8) + ->Arg(50) // non power-of-two ->Arg(1024) ->Arg(8192) ->Arg(1 << 15); @@ -158,6 +159,7 @@ int main(int argc, char** argv) { } }) ->Arg(8) + ->Arg(50) // non power-of-two ->Arg(1024) ->Arg(8192) ->Arg(1 << 20); diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp index 419e2dd7406cb..19f6ed2d55042 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp @@ -61,6 +61,7 @@ int main(int argc, char** argv) { benchmark::DoNotOptimize(result); } }) + ->Arg(1000) // non power-of-two ->Arg(1024) ->Arg(8192) ->Arg(1 << 20); @@ -106,6 +107,7 @@ int main(int argc, char** argv) { benchmark::DoNotOptimize(result); } }) + ->Arg(1000) // non power-of-two ->Arg(1024) ->Arg(8192) ->Arg(1 << 20); diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp index 80fe862672299..6525f8c384f76 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp @@ -66,6 +66,7 @@ int main(int argc, char** argv) { } }) ->Arg(32) + ->Arg(50) // non power-of-two ->Arg(1024) ->Arg(8192); }; @@ -122,6 +123,7 @@ int main(int argc, char** argv) { } }) ->Arg(32) + ->Arg(50) // non power-of-two ->Arg(1024) ->Arg(8192); }; diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp index 536d7d4f0350c..cac999b080f7c 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp @@ -56,6 +56,7 @@ int main(int argc, char** argv) { } }) ->Arg(8) + ->Arg(50) // non power-of-two ->Arg(1024) ->Arg(8192) ->Arg(1 << 20); @@ -104,6 +105,7 @@ int main(int argc, char** argv) { } }) ->Arg(8) + ->Arg(50) // non power-of-two ->Arg(1024) ->Arg(8192) ->Arg(1 << 20); diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp index 84a03582d4cbf..6711cec760e2c 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp @@ -57,6 +57,7 @@ int main(int argc, char** argv) { }) ->Arg(8) ->Arg(32) + ->Arg(50) // non power-of-two ->Arg(8192) ->Arg(1 << 20); }; diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/for_each.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/for_each.bench.cpp index 693403bc8f671..5387c37d228c5 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/for_each.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/for_each.bench.cpp @@ -39,6 +39,7 @@ int main(int argc, char** argv) { }) ->Arg(8) ->Arg(32) + ->Arg(50) // non power-of-two ->Arg(8192) ->Arg(1 << 20); }; diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/mismatch.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/mismatch.bench.cpp index af797a7aa7e9f..5c5961adcd05d 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/mismatch.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/mismatch.bench.cpp @@ -72,6 +72,7 @@ int main(int argc, char** argv) { } }) ->Arg(8) + ->Arg(1000) // non power-of-two ->Arg(1024) ->Arg(8192) ->Arg(1 << 20); diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp index 776be2c96ac4d..9696c77aab649 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp @@ -58,6 +58,7 @@ int main(int argc, char** argv) { benchmark::DoNotOptimize(result); } }) + ->Arg(1000) // non power-of-two ->Arg(1024) ->Arg(8192) ->Arg(1 << 20); @@ -110,6 +111,7 @@ int main(int argc, char** argv) { benchmark::DoNotOptimize(result); } }) + ->Arg(1000) // non power-of-two ->Arg(1024) ->Arg(8192); }; diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp index d3127e0b1b023..a58386435fe85 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp @@ -59,6 +59,7 @@ int main(int argc, char** argv) { benchmark::DoNotOptimize(result); } }) + ->Arg(1000) // non power-of-two ->Arg(1024) ->Arg(8192) ->Arg(1 << 20); @@ -111,6 +112,7 @@ int main(int argc, char** argv) { benchmark::DoNotOptimize(result); } }) + ->Arg(1000) // non power-of-two ->Arg(1024) ->Arg(8192); }; diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/starts_with.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/starts_with.bench.cpp index cfaa2c9dec0eb..e141e0c48810c 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/starts_with.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/starts_with.bench.cpp @@ -49,6 +49,7 @@ int main(int argc, char** argv) { } }) ->Arg(8) + ->Arg(1000) // non power-of-two ->Arg(1024) ->Arg(8192) ->Arg(1 << 20); From a67d700fc509bbdaa8524fb9cb667b7a047733d6 Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Tue, 25 Feb 2025 13:03:05 -0500 Subject: [PATCH 09/21] Remove unnecessary benchmark for std::count --- .../algorithms/nonmodifying/count.bench.cpp | 47 ------------------- 1 file changed, 47 deletions(-) diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/count.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/count.bench.cpp index a4edd4402ce74..c90b23eda8672 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/count.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/count.bench.cpp @@ -80,53 +80,6 @@ int main(int argc, char** argv) { bm.operator()>("rng::count_if(list) (every other)", ranges_count_if); } - // Benchmark {std,ranges}::{count,count_if} on a sequence where only a few elements are counted. - // In theory, we could blaze through contiguous sequences where there are no elements to count. - { - 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 % (size / 5) == 0 ? x : y); // intersperse 5 elements to count at regular intervals - } - - 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(50) // non power-of-two - ->Arg(1024) - ->Arg(8192) - ->Arg(1 << 20); - }; - - // count - bm.operator()>("std::count(vector) (sparse)", std_count); - bm.operator()>("std::count(deque) (sparse)", std_count); - bm.operator()>("std::count(list) (sparse)", std_count); - bm.operator()>("rng::count(vector) (sparse)", ranges_count); - bm.operator()>("rng::count(deque) (sparse)", ranges_count); - bm.operator()>("rng::count(list) (sparse)", ranges_count); - - // count_if - bm.operator()>("std::count_if(vector) (sparse)", std_count_if); - bm.operator()>("std::count_if(deque) (sparse)", std_count_if); - bm.operator()>("std::count_if(list) (sparse)", std_count_if); - bm.operator()>("rng::count_if(vector) (sparse)", ranges_count_if); - bm.operator()>("rng::count_if(deque) (sparse)", ranges_count_if); - bm.operator()>("rng::count_if(list) (sparse)", ranges_count_if); - } - // Benchmark {std,ranges}::count(vector) { auto bm = [](std::string name, auto count) { From b92ad0cf7760c3f92bde507bb6c62956058c70a3 Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Tue, 25 Feb 2025 13:12:51 -0500 Subject: [PATCH 10/21] Remove redundant benchmark for search_n --- .../nonmodifying/search_n.bench.cpp | 54 +------------------ 1 file changed, 1 insertion(+), 53 deletions(-) diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp index a58386435fe85..de404fedaed3a 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/search_n.bench.cpp @@ -38,7 +38,7 @@ int main(int argc, char** argv) { }); }; - // Benchmark {std,ranges}::search_n where the needle is never found + // Benchmark {std,ranges}::search_n where the needle is never found (worst case). { auto bm = [](std::string name, auto search_n) { benchmark::RegisterBenchmark( @@ -81,58 +81,6 @@ int main(int argc, char** argv) { bm.operator()>("rng::search_n(list, pred) (no match)", ranges_search_n_pred); } - // Benchmark {std,ranges}::search_n where we intersperse "near matches" inside the haystack - { - 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 - assert(n > 0); - - // intersperse near-matches inside the haystack - { - auto first = haystack.begin(); - for (int i = 0; i != 10; ++i) { - first = std::fill_n(first, n - 1, y); - ++first; // this causes the subsequence not to match because it has length n-1 - } - } - - 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); - }; - // {std,ranges}::search_n - bm.operator()>("std::search_n(vector) (near matches)", std_search_n); - bm.operator()>("std::search_n(deque) (near matches)", std_search_n); - bm.operator()>("std::search_n(list) (near matches)", std_search_n); - bm.operator()>("rng::search_n(vector) (near matches)", std::ranges::search_n); - bm.operator()>("rng::search_n(deque) (near matches)", std::ranges::search_n); - bm.operator()>("rng::search_n(list) (near matches)", std::ranges::search_n); - - // {std,ranges}::search_n(pred) - bm.operator()>("std::search_n(vector, pred) (near matches)", std_search_n_pred); - bm.operator()>("std::search_n(deque, pred) (near matches)", std_search_n_pred); - bm.operator()>("std::search_n(list, pred) (near matches)", std_search_n_pred); - bm.operator()>("rng::search_n(vector, pred) (near matches)", ranges_search_n_pred); - bm.operator()>("rng::search_n(deque, pred) (near matches)", ranges_search_n_pred); - bm.operator()>("rng::search_n(list, pred) (near matches)", ranges_search_n_pred); - } - benchmark::Initialize(&argc, argv); benchmark::RunSpecifiedBenchmarks(); benchmark::Shutdown(); From c2cc283cbbbe8d873d8d76870a6fd1b612b4a23d Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Tue, 25 Feb 2025 13:15:24 -0500 Subject: [PATCH 11/21] Simplify fold benchmark by using unsigned arithmetic --- .../algorithms/nonmodifying/fold.bench.cpp | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp index 6711cec760e2c..379dec046b42f 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp @@ -9,13 +9,12 @@ // UNSUPPORTED: c++03, c++11, c++14, c++17 #include -#include #include #include #include -#include #include #include +#include #include #include @@ -30,15 +29,11 @@ int main(int argc, char** argv) { [fold](auto& st) { std::size_t const size = st.range(0); using ValueType = typename Container::value_type; - ValueType const limit = 1000; // ensure we never overflow in the addition - assert(static_cast(size) <= std::numeric_limits::max()); - assert(std::numeric_limits::max() > static_cast(size) * limit); - assert(std::numeric_limits::min() < static_cast(size) * limit * -1); + 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 std::clamp(Generate::random(), -1 * limit, limit); - }); + std::generate_n(std::back_inserter(c), size, [&] { return Generate::random(); }); ValueType init = c.back(); c.pop_back(); @@ -61,14 +56,14 @@ int main(int argc, char** argv) { ->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); + 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); + // 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); From e6075be6d7e241143f03003baa3fb0191dae3b86 Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Tue, 25 Feb 2025 13:24:41 -0500 Subject: [PATCH 12/21] Add a few benchmarks on forward_list --- .../algorithms/nonmodifying/ends_with.bench.cpp | 14 +++++++++++++- .../algorithms/nonmodifying/find_end.bench.cpp | 13 +++++++++++-- .../algorithms/nonmodifying/find_last.bench.cpp | 9 +++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp index cf4e6b5781ec5..f572773b10814 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp @@ -9,8 +9,10 @@ // UNSUPPORTED: c++03, c++11, c++14, c++17 #include +#include #include #include +#include #include #include #include @@ -39,7 +41,8 @@ int main(int argc, char** argv) { ValueType y = random_different_from({x}); Container c1(size, x); Container c2(size, x); - c2.back() = y; + assert(size != 0); + *std::next(c2.begin(), size - 1) = y; // set last element to y for ([[maybe_unused]] auto _ : st) { benchmark::DoNotOptimize(c1); @@ -57,10 +60,14 @@ int main(int argc, char** argv) { 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. @@ -75,6 +82,7 @@ int main(int argc, char** argv) { 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) { @@ -93,10 +101,14 @@ int main(int argc, char** argv) { 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); diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp index 19f6ed2d55042..c61002e3617a2 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -52,7 +53,7 @@ int main(int argc, char** argv) { Container subrange(size / 10, y); // subrange of length 10% of the full range // put the element we're searching for at 25% of the sequence - std::ranges::copy(subrange, std::next(c.begin(), c.size() / 4)); + std::ranges::copy(subrange, std::next(c.begin(), size / 4)); for ([[maybe_unused]] auto _ : st) { benchmark::DoNotOptimize(c); @@ -70,17 +71,21 @@ int main(int argc, char** argv) { bm.operator()>("std::find_end(vector) (bail 25%)", std_find_end); bm.operator()>("std::find_end(deque) (bail 25%)", std_find_end); bm.operator()>("std::find_end(list) (bail 25%)", std_find_end); + bm.operator()>("std::find_end(forward_list) (bail 25%)", std_find_end); bm.operator()>("rng::find_end(vector) (bail 25%)", std::ranges::find_end); bm.operator()>("rng::find_end(deque) (bail 25%)", std::ranges::find_end); bm.operator()>("rng::find_end(list) (bail 25%)", std::ranges::find_end); + bm.operator()>("rng::find_end(forward_list) (bail 25%)", std::ranges::find_end); // {std,ranges}::find_end(it1, it1, it2, it2, pred) bm.operator()>("std::find_end(vector, pred) (bail 25%)", std_find_end_pred); bm.operator()>("std::find_end(deque, pred) (bail 25%)", std_find_end_pred); bm.operator()>("std::find_end(list, pred) (bail 25%)", std_find_end_pred); + bm.operator()>("std::find_end(forward_list, pred) (bail 25%)", std_find_end_pred); bm.operator()>("rng::find_end(vector, pred) (bail 25%)", ranges_find_end_pred); bm.operator()>("rng::find_end(deque, pred) (bail 25%)", ranges_find_end_pred); bm.operator()>("rng::find_end(list, pred) (bail 25%)", ranges_find_end_pred); + bm.operator()>("rng::find_end(forward_list, pred) (bail 25%)", ranges_find_end_pred); } // Benchmark {std,ranges}::find_end where the subsequence is found @@ -98,7 +103,7 @@ int main(int argc, char** argv) { Container subrange(size / 10, y); // subrange of length 10% of the full range // put the element we're searching for at 90% of the sequence - std::ranges::copy(subrange, std::next(c.begin(), 9 * (c.size() / 10))); + std::ranges::copy(subrange, std::next(c.begin(), (9 * size) / 10)); for ([[maybe_unused]] auto _ : st) { benchmark::DoNotOptimize(c); @@ -116,17 +121,21 @@ int main(int argc, char** argv) { bm.operator()>("std::find_end(vector) (bail 90%)", std_find_end); bm.operator()>("std::find_end(deque) (bail 90%)", std_find_end); bm.operator()>("std::find_end(list) (bail 90%)", std_find_end); + bm.operator()>("std::find_end(forward_list) (bail 90%)", std_find_end); bm.operator()>("rng::find_end(vector) (bail 90%)", std::ranges::find_end); bm.operator()>("rng::find_end(deque) (bail 90%)", std::ranges::find_end); bm.operator()>("rng::find_end(list) (bail 90%)", std::ranges::find_end); + bm.operator()>("rng::find_end(forward_list) (bail 90%)", std::ranges::find_end); // {std,ranges}::find_end(it1, it1, it2, it2, pred) bm.operator()>("std::find_end(vector, pred) (bail 90%)", std_find_end_pred); bm.operator()>("std::find_end(deque, pred) (bail 90%)", std_find_end_pred); bm.operator()>("std::find_end(list, pred) (bail 90%)", std_find_end_pred); + bm.operator()>("std::find_end(forward_list, pred) (bail 90%)", std_find_end_pred); bm.operator()>("rng::find_end(vector, pred) (bail 90%)", ranges_find_end_pred); bm.operator()>("rng::find_end(deque, pred) (bail 90%)", ranges_find_end_pred); bm.operator()>("rng::find_end(list, pred) (bail 90%)", ranges_find_end_pred); + bm.operator()>("rng::find_end(forward_list, pred) (bail 90%)", ranges_find_end_pred); } benchmark::Initialize(&argc, argv); diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp index cac999b080f7c..ee65ace061941 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -67,18 +68,22 @@ int main(int argc, char** argv) { bm.operator()>("rng::find_last(vector) (bail 25%)", std::ranges::find_last); bm.operator()>("rng::find_last(deque) (bail 25%)", std::ranges::find_last); bm.operator()>("rng::find_last(list) (bail 25%)", std::ranges::find_last); + bm.operator()>("rng::find_last(forward_list) (bail 25%)", std::ranges::find_last); // find_last_if bm.operator()>("rng::find_last_if(vector) (bail 25%)", ranges_find_last_if); bm.operator()>("rng::find_last_if(vector) (bail 25%)", ranges_find_last_if); bm.operator()>("rng::find_last_if(deque) (bail 25%)", ranges_find_last_if); bm.operator()>("rng::find_last_if(list) (bail 25%)", ranges_find_last_if); + bm.operator()>("rng::find_last_if(forward_list) (bail 25%)", ranges_find_last_if); // find_last_if_not bm.operator()>("rng::find_last_if_not(vector) (bail 25%)", ranges_find_last_if_not); bm.operator()>("rng::find_last_if_not(vector) (bail 25%)", ranges_find_last_if_not); bm.operator()>("rng::find_last_if_not(deque) (bail 25%)", ranges_find_last_if_not); bm.operator()>("rng::find_last_if_not(list) (bail 25%)", ranges_find_last_if_not); + bm.operator()>( + "rng::find_last_if_not(forward_list) (bail 25%)", ranges_find_last_if_not); } // Benchmark ranges::{find_last,find_last_if,find_last_if_not} where the last element @@ -115,18 +120,22 @@ int main(int argc, char** argv) { 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); From 750b01c1b77213abdf1e66c70ff37038870a3284 Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Mon, 17 Mar 2025 10:07:46 -0400 Subject: [PATCH 13/21] Re-add benchmark for std::equal with vector --- .../algorithms/nonmodifying/equal.bench.cpp | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/equal.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/equal.bench.cpp index 9feef4e1ac22b..03533ec4304ab 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/equal.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/equal.bench.cpp @@ -100,6 +100,42 @@ int main(int argc, char** argv) { 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 (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(); From fc02f9266e638166c6e983f6d1ab186683d47909 Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Mon, 17 Mar 2025 10:12:09 -0400 Subject: [PATCH 14/21] Remove some benchmarks bailing out at 25% where that doesn't provide additional information --- .../nonmodifying/any_all_none_of.bench.cpp | 58 +------------------ .../nonmodifying/contains.bench.cpp | 36 +----------- .../nonmodifying/contains_subrange.bench.cpp | 37 +----------- 3 files changed, 6 insertions(+), 125 deletions(-) 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 index 66eba4e50f83d..2bbd233655ce5 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/any_all_none_of.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/any_all_none_of.bench.cpp @@ -39,62 +39,8 @@ int main(int argc, char** argv) { return !std::ranges::none_of(first, last, pred); }; - // Benchmark {std,ranges}::{any_of,all_of,none_of} where we bail out early - // (after visiting 25% of the elements). - { - 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); - *std::next(c.begin(), size / 4) = y; // bail out after the first 25% elements - - for (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) (bail 25%)", std_any_of); - bm.operator()>("std::any_of(deque) (bail 25%)", std_any_of); - bm.operator()>("std::any_of(list) (bail 25%)", std_any_of); - bm.operator()>("rng::any_of(vector) (bail 25%)", std::ranges::any_of); - bm.operator()>("rng::any_of(deque) (bail 25%)", std::ranges::any_of); - bm.operator()>("rng::any_of(list) (bail 25%)", std::ranges::any_of); - - // all_of - bm.operator()>("std::all_of(vector) (bail 25%)", std_all_of); - bm.operator()>("std::all_of(deque) (bail 25%)", std_all_of); - bm.operator()>("std::all_of(list) (bail 25%)", std_all_of); - bm.operator()>("rng::all_of(vector) (bail 25%)", ranges_all_of); - bm.operator()>("rng::all_of(deque) (bail 25%)", ranges_all_of); - bm.operator()>("rng::all_of(list) (bail 25%)", ranges_all_of); - - // none_of - bm.operator()>("std::none_of(vector) (bail 25%)", std_none_of); - bm.operator()>("std::none_of(deque) (bail 25%)", std_none_of); - bm.operator()>("std::none_of(list) (bail 25%)", std_none_of); - bm.operator()>("rng::none_of(vector) (bail 25%)", ranges_none_of); - bm.operator()>("rng::none_of(deque) (bail 25%)", ranges_none_of); - bm.operator()>("rng::none_of(list) (bail 25%)", ranges_none_of); - } - - // Benchmark {std,ranges}::{any_of,all_of,none_of} where we process the whole sequence. + // 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( diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp index 50d8c5fd331cd..5b41adbd1f4d9 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp @@ -19,40 +19,8 @@ #include "../../GenerateInput.h" int main(int argc, char** argv) { - // Benchmark ranges::contains where we bail out early (after visiting 25% of the elements). - { - 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); - *std::next(c.begin(), size / 4) = y; // bail out after checking 25% of values - auto first = c.begin(); - auto last = c.end(); - - for (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) (bail 25%)"); - bm.operator()>("rng::contains(deque) (bail 25%)"); - bm.operator()>("rng::contains(list) (bail 25%)"); - } - - // Benchmark ranges::contains where we process the whole sequence. + // Benchmark ranges::contains where we process the whole sequence, which is the + // worst case. { auto bm = [](std::string name) { benchmark::RegisterBenchmark( diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp index 96676bd2921f5..b203bb45fa3a2 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp @@ -19,41 +19,8 @@ #include "../../GenerateInput.h" int main(int argc, char** argv) { - // Benchmark ranges::contains_subrange where we find our target starting at 25% of the elements - { - 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); - Container subrange(size / 10, y); // subrange of length 10% of the full range - - // At 25% of the range, put the subrange we're going to find - std::ranges::copy(subrange, std::next(c.begin(), c.size() / 4)); - - for (auto _ : st) { - benchmark::DoNotOptimize(c); - benchmark::DoNotOptimize(subrange); - auto result = std::ranges::contains_subrange(c, subrange); - benchmark::DoNotOptimize(result); - } - }) - ->Arg(16) - ->Arg(32) - ->Arg(50) // non power-of-two - ->Arg(8192) - ->Arg(1 << 20); - }; - bm.operator()>("rng::contains_subrange(vector) (bail 25%)"); - bm.operator()>("rng::contains_subrange(deque) (bail 25%)"); - bm.operator()>("rng::contains_subrange(list) (bail 25%)"); - } - - // Benchmark ranges::contains_subrange where we never find our target + // Benchmark ranges::contains_subrange where we never find our target, which is the + // worst case. { auto bm = [](std::string name) { benchmark::RegisterBenchmark( From 76d15cc101c55dcd8c7a7b7435cf99eb3faeefee Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Mon, 17 Mar 2025 10:20:31 -0400 Subject: [PATCH 15/21] Use 90% and 10% for symmetry --- .../nonmodifying/find_first_of.bench.cpp | 20 +++++----- .../nonmodifying/find_last.bench.cpp | 38 +++++++++---------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp index 6525f8c384f76..9a8818038fe16 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp @@ -38,7 +38,7 @@ int main(int argc, char** argv) { }); }; - // Benchmark {std,ranges}::find_first_of where we have a hit at 25% of the haystack + // Benchmark {std,ranges}::find_first_of where we have a hit at 10% of the haystack // and at the end of the needle. This measures how quickly we're able to search inside // the needle. { @@ -55,8 +55,8 @@ int main(int argc, char** argv) { Container needle(size, y); needle.back() = z; // hit at the very end of the needle - // put the needle at 25% of the haystack - *std::next(haystack.begin(), haystack.size() / 4) = z; + // put the needle at 10% of the haystack + *std::next(haystack.begin(), haystack.size() / 10) = z; for ([[maybe_unused]] auto _ : st) { benchmark::DoNotOptimize(haystack); @@ -71,15 +71,15 @@ int main(int argc, char** argv) { ->Arg(8192); }; // {std,ranges}::find_first_of(it1, it1, it2, it2) - bm.operator()>("std::find_first_of(vector) (25% haystack, late needle)", std_find_first_of); - bm.operator()>("std::find_first_of(deque) (25% haystack, late needle)", std_find_first_of); - bm.operator()>("std::find_first_of(list) (25% haystack, late needle)", std_find_first_of); + bm.operator()>("std::find_first_of(vector) (10% haystack, late needle)", std_find_first_of); + bm.operator()>("std::find_first_of(deque) (10% haystack, late needle)", std_find_first_of); + bm.operator()>("std::find_first_of(list) (10% haystack, late needle)", std_find_first_of); bm.operator()>( - "rng::find_first_of(vector) (25% haystack, late needle)", std::ranges::find_first_of); + "rng::find_first_of(vector) (10% haystack, late needle)", std::ranges::find_first_of); bm.operator()>( - "rng::find_first_of(deque) (25% haystack, late needle)", std::ranges::find_first_of); + "rng::find_first_of(deque) (10% haystack, late needle)", std::ranges::find_first_of); bm.operator()>( - "rng::find_first_of(list) (25% haystack, late needle)", std::ranges::find_first_of); + "rng::find_first_of(list) (10% haystack, late needle)", std::ranges::find_first_of); // {std,ranges}::find_first_of(it1, it1, it2, it2, pred) bm.operator()>( @@ -113,7 +113,7 @@ int main(int argc, char** argv) { *std::next(needle.begin(), needle.size() / 10) = z; // hit at 10% of the needle // put the needle at 90% of the haystack - *std::next(haystack.begin(), 9 * (haystack.size() / 10)) = z; + *std::next(haystack.begin(), (9 * haystack.size()) / 10) = z; for ([[maybe_unused]] auto _ : st) { benchmark::DoNotOptimize(haystack); diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp index ee65ace061941..f0a48a830c2c6 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp @@ -34,7 +34,7 @@ int main(int argc, char** argv) { }; // Benchmark ranges::{find_last,find_last_if,find_last_if_not} where the last element - // is found 25% into the sequence + // is found 10% into the sequence { auto bm = [](std::string name, auto find_last) { benchmark::RegisterBenchmark( @@ -46,8 +46,8 @@ int main(int argc, char** argv) { 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; + // 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); @@ -64,26 +64,26 @@ int main(int argc, char** argv) { }; // find_last - bm.operator()>("rng::find_last(vector) (bail 25%)", std::ranges::find_last); - bm.operator()>("rng::find_last(vector) (bail 25%)", std::ranges::find_last); - bm.operator()>("rng::find_last(deque) (bail 25%)", std::ranges::find_last); - bm.operator()>("rng::find_last(list) (bail 25%)", std::ranges::find_last); - bm.operator()>("rng::find_last(forward_list) (bail 25%)", std::ranges::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 25%)", ranges_find_last_if); - bm.operator()>("rng::find_last_if(vector) (bail 25%)", ranges_find_last_if); - bm.operator()>("rng::find_last_if(deque) (bail 25%)", ranges_find_last_if); - bm.operator()>("rng::find_last_if(list) (bail 25%)", ranges_find_last_if); - bm.operator()>("rng::find_last_if(forward_list) (bail 25%)", ranges_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 25%)", ranges_find_last_if_not); - bm.operator()>("rng::find_last_if_not(vector) (bail 25%)", ranges_find_last_if_not); - bm.operator()>("rng::find_last_if_not(deque) (bail 25%)", ranges_find_last_if_not); - bm.operator()>("rng::find_last_if_not(list) (bail 25%)", 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(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 25%)", ranges_find_last_if_not); + "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 @@ -100,7 +100,7 @@ int main(int argc, char** argv) { Container c(size, x); // put the element we're searching for at 90% of the sequence - *std::next(c.begin(), 9 * (size / 10)) = y; + *std::next(c.begin(), (9 * size) / 10) = y; for ([[maybe_unused]] auto _ : st) { benchmark::DoNotOptimize(c); From 252941e71c2957074ae121e17ff6070145c20ce8 Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Mon, 17 Mar 2025 15:12:59 -0400 Subject: [PATCH 16/21] Fix benchmarks for contains_subrange and find_end --- .../nonmodifying/contains_subrange.bench.cpp | 116 +++++++++- .../nonmodifying/find_end.bench.cpp | 199 ++++++++++++------ 2 files changed, 250 insertions(+), 65 deletions(-) diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp index b203bb45fa3a2..4e20173aa5859 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp @@ -9,6 +9,7 @@ // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 #include +#include #include #include #include @@ -19,7 +20,7 @@ #include "../../GenerateInput.h" int main(int argc, char** argv) { - // Benchmark ranges::contains_subrange where we never find our target, which is the + // Benchmark ranges::contains_subrange where we never find the needle, which is the // worst case. { auto bm = [](std::string name) { @@ -30,13 +31,15 @@ int main(int argc, char** argv) { using ValueType = typename Container::value_type; ValueType x = Generate::random(); ValueType y = random_different_from({x}); - Container c(size, x); - Container subrange(size / 10, y); // subrange of length 10% of the full range, but we'll never find it + 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 (auto _ : st) { - benchmark::DoNotOptimize(c); - benchmark::DoNotOptimize(subrange); - auto result = std::ranges::contains_subrange(c, subrange); + benchmark::DoNotOptimize(haystack); + benchmark::DoNotOptimize(needle); + auto result = std::ranges::contains_subrange(haystack, needle); benchmark::DoNotOptimize(result); } }) @@ -51,6 +54,107 @@ int main(int argc, char** argv) { 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 (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 (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(); diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp index c61002e3617a2..32a4d1e91d7d6 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp @@ -9,6 +9,7 @@ // UNSUPPORTED: c++03, c++11, c++14, c++17 #include +#include #include #include #include @@ -38,8 +39,34 @@ int main(int argc, char** argv) { }); }; - // Benchmark {std,ranges}::find_end where the subsequence is found - // 25% into the sequence + 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( @@ -49,16 +76,15 @@ int main(int argc, char** argv) { using ValueType = typename Container::value_type; ValueType x = Generate::random(); ValueType y = random_different_from({x}); - Container c(size, x); - Container subrange(size / 10, y); // subrange of length 10% of the full range + 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); - // put the element we're searching for at 25% of the sequence - std::ranges::copy(subrange, std::next(c.begin(), size / 4)); - - for ([[maybe_unused]] auto _ : st) { - benchmark::DoNotOptimize(c); - benchmark::DoNotOptimize(subrange); - auto result = find_end(c.begin(), c.end(), subrange.begin(), subrange.end()); + for (auto _ : st) { + benchmark::DoNotOptimize(haystack); + benchmark::DoNotOptimize(needle); + auto result = find_end(haystack.begin(), haystack.end(), needle.begin(), needle.end()); benchmark::DoNotOptimize(result); } }) @@ -67,29 +93,10 @@ int main(int argc, char** argv) { ->Arg(8192) ->Arg(1 << 20); }; - // {std,ranges}::find_end(it1, it1, it2, it2) - bm.operator()>("std::find_end(vector) (bail 25%)", std_find_end); - bm.operator()>("std::find_end(deque) (bail 25%)", std_find_end); - bm.operator()>("std::find_end(list) (bail 25%)", std_find_end); - bm.operator()>("std::find_end(forward_list) (bail 25%)", std_find_end); - bm.operator()>("rng::find_end(vector) (bail 25%)", std::ranges::find_end); - bm.operator()>("rng::find_end(deque) (bail 25%)", std::ranges::find_end); - bm.operator()>("rng::find_end(list) (bail 25%)", std::ranges::find_end); - bm.operator()>("rng::find_end(forward_list) (bail 25%)", std::ranges::find_end); - - // {std,ranges}::find_end(it1, it1, it2, it2, pred) - bm.operator()>("std::find_end(vector, pred) (bail 25%)", std_find_end_pred); - bm.operator()>("std::find_end(deque, pred) (bail 25%)", std_find_end_pred); - bm.operator()>("std::find_end(list, pred) (bail 25%)", std_find_end_pred); - bm.operator()>("std::find_end(forward_list, pred) (bail 25%)", std_find_end_pred); - bm.operator()>("rng::find_end(vector, pred) (bail 25%)", ranges_find_end_pred); - bm.operator()>("rng::find_end(deque, pred) (bail 25%)", ranges_find_end_pred); - bm.operator()>("rng::find_end(list, pred) (bail 25%)", ranges_find_end_pred); - bm.operator()>("rng::find_end(forward_list, pred) (bail 25%)", ranges_find_end_pred); + register_benchmarks(bm, "process all"); } - // Benchmark {std,ranges}::find_end where the subsequence is found - // 90% into the sequence (i.e. near the end) + // Benchmark {std,ranges}::find_end where we intersperse "near matches" inside the haystack. { auto bm = [](std::string name, auto find_end) { benchmark::RegisterBenchmark( @@ -99,43 +106,117 @@ int main(int argc, char** argv) { using ValueType = typename Container::value_type; ValueType x = Generate::random(); ValueType y = random_different_from({x}); - Container c(size, x); - Container subrange(size / 10, y); // subrange of length 10% of the full range + 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 element we're searching for at 90% of the sequence - std::ranges::copy(subrange, std::next(c.begin(), (9 * size) / 10)); + // 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(c); - benchmark::DoNotOptimize(subrange); - auto result = find_end(c.begin(), c.end(), subrange.begin(), subrange.end()); + 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); + ->Arg(8192); }; - // {std,ranges}::find_end(it1, it1, it2, it2) - bm.operator()>("std::find_end(vector) (bail 90%)", std_find_end); - bm.operator()>("std::find_end(deque) (bail 90%)", std_find_end); - bm.operator()>("std::find_end(list) (bail 90%)", std_find_end); - bm.operator()>("std::find_end(forward_list) (bail 90%)", std_find_end); - bm.operator()>("rng::find_end(vector) (bail 90%)", std::ranges::find_end); - bm.operator()>("rng::find_end(deque) (bail 90%)", std::ranges::find_end); - bm.operator()>("rng::find_end(list) (bail 90%)", std::ranges::find_end); - bm.operator()>("rng::find_end(forward_list) (bail 90%)", std::ranges::find_end); + register_benchmarks(bm, "near matches"); + } - // {std,ranges}::find_end(it1, it1, it2, it2, pred) - bm.operator()>("std::find_end(vector, pred) (bail 90%)", std_find_end_pred); - bm.operator()>("std::find_end(deque, pred) (bail 90%)", std_find_end_pred); - bm.operator()>("std::find_end(list, pred) (bail 90%)", std_find_end_pred); - bm.operator()>("std::find_end(forward_list, pred) (bail 90%)", std_find_end_pred); - bm.operator()>("rng::find_end(vector, pred) (bail 90%)", ranges_find_end_pred); - bm.operator()>("rng::find_end(deque, pred) (bail 90%)", ranges_find_end_pred); - bm.operator()>("rng::find_end(list, pred) (bail 90%)", ranges_find_end_pred); - bm.operator()>("rng::find_end(forward_list, pred) (bail 90%)", ranges_find_end_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 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 (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 (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 (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); From 86e9b0efb3999e80dcf14e9857453744c1f7e3e6 Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Mon, 17 Mar 2025 15:14:32 -0400 Subject: [PATCH 17/21] Add UNSUPPORTED for starts_with --- .../benchmarks/algorithms/nonmodifying/starts_with.bench.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/starts_with.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/starts_with.bench.cpp index e141e0c48810c..2950ad5322e84 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/starts_with.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/starts_with.bench.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 #include #include From a302cf81f9abc4a38d03b5c3967ffac599992437 Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Mon, 17 Mar 2025 16:16:19 -0400 Subject: [PATCH 18/21] Review comments for std::search --- .../algorithms/nonmodifying/search.bench.cpp | 85 ++++++++++++++++++- 1 file changed, 83 insertions(+), 2 deletions(-) diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp index 9696c77aab649..a9d4a788d1b03 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/search.bench.cpp @@ -38,7 +38,7 @@ int main(int argc, char** argv) { }); }; - // Benchmark {std,ranges}::search where the needle is never found + // Benchmark {std,ranges}::search where the needle is never found (worst case). { auto bm = [](std::string name, auto search) { benchmark::RegisterBenchmark( @@ -80,7 +80,7 @@ int main(int argc, char** argv) { bm.operator()>("rng::search(list, pred) (no match)", ranges_search_pred); } - // Benchmark {std,ranges}::search where we intersperse "near matches" inside the haystack + // Benchmark {std,ranges}::search where we intersperse "near matches" inside the haystack. { auto bm = [](std::string name, auto search) { benchmark::RegisterBenchmark( @@ -132,6 +132,87 @@ int main(int argc, char** argv) { 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(); From e8c40d47cfe5184d634a0e7550486d423ae95d4a Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Mon, 17 Mar 2025 22:27:39 -0400 Subject: [PATCH 19/21] Tweak find_first_of per comments --- .../nonmodifying/find_first_of.bench.cpp | 89 +++++++------------ 1 file changed, 31 insertions(+), 58 deletions(-) diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp index 9a8818038fe16..fca1aaf233988 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_first_of.bench.cpp @@ -38,9 +38,8 @@ int main(int argc, char** argv) { }); }; - // Benchmark {std,ranges}::find_first_of where we have a hit at 10% of the haystack - // and at the end of the needle. This measures how quickly we're able to search inside - // the needle. + // 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( @@ -50,13 +49,8 @@ int main(int argc, char** argv) { using ValueType = typename Container::value_type; ValueType x = Generate::random(); ValueType y = random_different_from({x}); - ValueType z = random_different_from({x, y}); Container haystack(size, x); - Container needle(size, y); - needle.back() = z; // hit at the very end of the needle - - // put the needle at 10% of the haystack - *std::next(haystack.begin(), haystack.size() / 10) = z; + Container needle(10, y); for ([[maybe_unused]] auto _ : st) { benchmark::DoNotOptimize(haystack); @@ -71,33 +65,23 @@ int main(int argc, char** argv) { ->Arg(8192); }; // {std,ranges}::find_first_of(it1, it1, it2, it2) - bm.operator()>("std::find_first_of(vector) (10% haystack, late needle)", std_find_first_of); - bm.operator()>("std::find_first_of(deque) (10% haystack, late needle)", std_find_first_of); - bm.operator()>("std::find_first_of(list) (10% haystack, late needle)", std_find_first_of); - bm.operator()>( - "rng::find_first_of(vector) (10% haystack, late needle)", std::ranges::find_first_of); - bm.operator()>( - "rng::find_first_of(deque) (10% haystack, late needle)", std::ranges::find_first_of); - bm.operator()>( - "rng::find_first_of(list) (10% haystack, late needle)", std::ranges::find_first_of); + 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) (25% haystack, late needle)", std_find_first_of); - bm.operator()>( - "std::find_first_of(deque, pred) (25% haystack, late needle)", std_find_first_of); - bm.operator()>("std::find_first_of(list, pred) (25% haystack, late needle)", std_find_first_of); - bm.operator()>( - "rng::find_first_of(vector, pred) (25% haystack, late needle)", std::ranges::find_first_of); - bm.operator()>( - "rng::find_first_of(deque, pred) (25% haystack, late needle)", std::ranges::find_first_of); - bm.operator()>( - "rng::find_first_of(list, pred) (25% haystack, late needle)", std::ranges::find_first_of); + 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); } - // Benchmark {std,ranges}::find_first_of where we have a hit at 90% of the haystack - // but at the beginning of the needle. This measures how quickly we're able to search - // inside the haystack. + // 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( @@ -107,13 +91,11 @@ int main(int argc, char** argv) { using ValueType = typename Container::value_type; ValueType x = Generate::random(); ValueType y = random_different_from({x}); - ValueType z = random_different_from({x, y}); Container haystack(size, x); - Container needle(size, y); - *std::next(needle.begin(), needle.size() / 10) = z; // hit at 10% of the needle + Container needle(size * 10, y); - // put the needle at 90% of the haystack - *std::next(haystack.begin(), (9 * haystack.size()) / 10) = z; + // put a match at 10% of the haystack + *std::next(haystack.begin(), haystack.size() / 10) = y; for ([[maybe_unused]] auto _ : st) { benchmark::DoNotOptimize(haystack); @@ -128,29 +110,20 @@ int main(int argc, char** argv) { ->Arg(8192); }; // {std,ranges}::find_first_of(it1, it1, it2, it2) - bm.operator()>("std::find_first_of(vector) (90% haystack, early needle)", std_find_first_of); - bm.operator()>("std::find_first_of(deque) (90% haystack, early needle)", std_find_first_of); - bm.operator()>("std::find_first_of(list) (90% haystack, early needle)", std_find_first_of); - bm.operator()>( - "rng::find_first_of(vector) (90% haystack, early needle)", std::ranges::find_first_of); - bm.operator()>( - "rng::find_first_of(deque) (90% haystack, early needle)", std::ranges::find_first_of); - bm.operator()>( - "rng::find_first_of(list) (90% haystack, early needle)", std::ranges::find_first_of); + 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) (90% haystack, early needle)", std_find_first_of_pred); - bm.operator()>( - "std::find_first_of(deque, pred) (90% haystack, early needle)", std_find_first_of_pred); - bm.operator()>( - "std::find_first_of(list, pred) (90% haystack, early needle)", std_find_first_of_pred); - bm.operator()>( - "rng::find_first_of(vector, pred) (90% haystack, early needle)", ranges_find_first_of_pred); - bm.operator()>( - "rng::find_first_of(deque, pred) (90% haystack, early needle)", ranges_find_first_of_pred); - bm.operator()>( - "rng::find_first_of(list, pred) (90% haystack, early needle)", ranges_find_first_of_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); From 6502e381c49dc660c9d425b937655aa9283ace93 Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Tue, 18 Mar 2025 01:01:24 -0400 Subject: [PATCH 20/21] Fix unsupported --- .../test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp | 2 +- .../test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp | 2 +- libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp index f572773b10814..0c01833453156 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/ends_with.bench.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 #include #include diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp index f0a48a830c2c6..17074250c6c72 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_last.bench.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 #include #include diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp index 379dec046b42f..3da151d4c1b12 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 #include #include From dfe853b747fec374f8b473f4f86191ddb669ded4 Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Tue, 18 Mar 2025 08:30:00 -0400 Subject: [PATCH 21/21] [[maybe_unused]] --- .../algorithms/nonmodifying/any_all_none_of.bench.cpp | 2 +- .../benchmarks/algorithms/nonmodifying/contains.bench.cpp | 2 +- .../algorithms/nonmodifying/contains_subrange.bench.cpp | 6 +++--- .../benchmarks/algorithms/nonmodifying/equal.bench.cpp | 2 +- .../benchmarks/algorithms/nonmodifying/find_end.bench.cpp | 8 ++++---- .../benchmarks/algorithms/nonmodifying/fold.bench.cpp | 2 +- .../benchmarks/algorithms/nonmodifying/for_each.bench.cpp | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) 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 index 2bbd233655ce5..fb17ef12f1fce 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/any_all_none_of.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/any_all_none_of.bench.cpp @@ -52,7 +52,7 @@ int main(int argc, char** argv) { ValueType y = random_different_from({x}); Container c(size, x); - for (auto _ : st) { + for ([[maybe_unused]] auto _ : st) { benchmark::DoNotOptimize(c); auto result = any_of(c.begin(), c.end(), [&](auto element) { benchmark::DoNotOptimize(element); diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp index 5b41adbd1f4d9..1bb9400113ef9 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/contains.bench.cpp @@ -34,7 +34,7 @@ int main(int argc, char** argv) { auto first = c.begin(); auto last = c.end(); - for (auto _ : st) { + for ([[maybe_unused]] auto _ : st) { benchmark::DoNotOptimize(c); benchmark::DoNotOptimize(y); auto result = std::ranges::contains(first, last, y); diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp index 4e20173aa5859..cce2e42142ab0 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/contains_subrange.bench.cpp @@ -36,7 +36,7 @@ int main(int argc, char** argv) { assert(n > 0); Container needle(n, y); - for (auto _ : st) { + for ([[maybe_unused]] auto _ : st) { benchmark::DoNotOptimize(haystack); benchmark::DoNotOptimize(needle); auto result = std::ranges::contains_subrange(haystack, needle); @@ -106,7 +106,7 @@ int main(int argc, char** argv) { Container haystack(size, x); Container needle(size, x); - for (auto _ : st) { + for ([[maybe_unused]] auto _ : st) { benchmark::DoNotOptimize(haystack); benchmark::DoNotOptimize(needle); auto result = std::ranges::contains_subrange(haystack, needle); @@ -137,7 +137,7 @@ int main(int argc, char** argv) { Container haystack(size, x); Container needle(1, y); - for (auto _ : st) { + for ([[maybe_unused]] auto _ : st) { benchmark::DoNotOptimize(haystack); benchmark::DoNotOptimize(needle); auto result = std::ranges::contains_subrange(haystack, needle); diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/equal.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/equal.bench.cpp index 03533ec4304ab..4da164d5f0f94 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/equal.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/equal.bench.cpp @@ -113,7 +113,7 @@ int main(int argc, char** argv) { auto last1 = c1.end(); auto first2 = aligned ? c2.begin() : c2.begin() + 4; auto last2 = aligned ? c2.end() : c2.end() - 4; - for (auto _ : st) { + for ([[maybe_unused]] auto _ : st) { benchmark::DoNotOptimize(c1); benchmark::DoNotOptimize(c2); auto result = equal(first1, last1, first2, last2); diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp index 32a4d1e91d7d6..f1ad8a65c3235 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/find_end.bench.cpp @@ -81,7 +81,7 @@ int main(int argc, char** argv) { assert(n > 0); Container needle(n, y); - for (auto _ : st) { + for ([[maybe_unused]] auto _ : st) { benchmark::DoNotOptimize(haystack); benchmark::DoNotOptimize(needle); auto result = find_end(haystack.begin(), haystack.end(), needle.begin(), needle.end()); @@ -146,7 +146,7 @@ int main(int argc, char** argv) { Container haystack(size, x); Container needle(size, x); - for (auto _ : st) { + for ([[maybe_unused]] auto _ : st) { benchmark::DoNotOptimize(haystack); benchmark::DoNotOptimize(needle); auto result = find_end(haystack.begin(), haystack.end(), needle.begin(), needle.end()); @@ -173,7 +173,7 @@ int main(int argc, char** argv) { Container haystack(size, x); Container needle(1, y); - for (auto _ : st) { + for ([[maybe_unused]] auto _ : st) { benchmark::DoNotOptimize(haystack); benchmark::DoNotOptimize(needle); auto result = find_end(haystack.begin(), haystack.end(), needle.begin(), needle.end()); @@ -205,7 +205,7 @@ int main(int argc, char** argv) { // put the needle at 90% of the haystack std::ranges::copy(needle, std::next(haystack.begin(), (9 * size) / 10)); - for (auto _ : st) { + for ([[maybe_unused]] auto _ : st) { benchmark::DoNotOptimize(haystack); benchmark::DoNotOptimize(needle); auto result = find_end(haystack.begin(), haystack.end(), needle.begin(), needle.end()); diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp index 3da151d4c1b12..a795b5df7f7f4 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/fold.bench.cpp @@ -43,7 +43,7 @@ int main(int argc, char** argv) { return x + y; }; - for (auto _ : st) { + for ([[maybe_unused]] auto _ : st) { benchmark::DoNotOptimize(c); benchmark::DoNotOptimize(init); auto result = fold(c.begin(), c.end(), init, f); diff --git a/libcxx/test/benchmarks/algorithms/nonmodifying/for_each.bench.cpp b/libcxx/test/benchmarks/algorithms/nonmodifying/for_each.bench.cpp index 5387c37d228c5..760accbe4d929 100644 --- a/libcxx/test/benchmarks/algorithms/nonmodifying/for_each.bench.cpp +++ b/libcxx/test/benchmarks/algorithms/nonmodifying/for_each.bench.cpp @@ -31,7 +31,7 @@ int main(int argc, char** argv) { auto first = c.begin(); auto last = c.end(); - for (auto _ : st) { + 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);