diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index 22503345a4a..d1367bc9d3f 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -124,6 +124,7 @@ add_benchmark(random_integer_generation src/random_integer_generation.cpp) add_benchmark(remove src/remove.cpp) add_benchmark(replace src/replace.cpp) add_benchmark(search src/search.cpp) +add_benchmark(search_n src/search_n.cpp) add_benchmark(std_copy src/std_copy.cpp) add_benchmark(sv_equal src/sv_equal.cpp) add_benchmark(swap_ranges src/swap_ranges.cpp) diff --git a/benchmarks/src/search_n.cpp b/benchmarks/src/search_n.cpp new file mode 100644 index 00000000000..b35cfd9eb2d --- /dev/null +++ b/benchmarks/src/search_n.cpp @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include + +#include "skewed_allocator.hpp" + +using namespace std; + +// NB: This particular algorithm has std and ranges implementations with different perf characteristics! + +enum class AlgType { Std, Rng }; + +template +void bm(benchmark::State& state) { + const auto size = static_cast(state.range(0)); + + constexpr size_t N = 1; + + constexpr T no_match{'-'}; + constexpr T match{'*'}; + + vector> v(size, no_match); + + fill(v.begin() + v.size() / 2, v.end(), match); + + for (auto _ : state) { + if constexpr (Alg == AlgType::Std) { + benchmark::DoNotOptimize(search_n(v.begin(), v.end(), N, match)); + } else if constexpr (Alg == AlgType::Rng) { + benchmark::DoNotOptimize(ranges::search_n(v, N, match)); + } + } +} + +void common_args(auto bm) { + bm->Arg(3000); +} + +BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); + +BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); + +BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); + +BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); + +BENCHMARK_MAIN(); diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 8109baf3a16..d1f0a6684b9 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -2220,6 +2220,16 @@ _NODISCARD _CONSTEXPR20 _FwdIt search_n( return _First; } + if constexpr (_Is_any_of_v<_Pr, +#if _HAS_CXX20 + _RANGES equal_to, +#endif + equal_to<>>) { + if (_Count == 1) { + return _STD find(_First, _Last, _Val); + } + } + if (static_cast(_Count) > static_cast(_STD _Max_limit<_Iter_diff_t<_FwdIt>>())) { // if the number of _Vals searched for is larger than the longest possible sequence, we can't find it return _Last; @@ -2320,6 +2330,19 @@ namespace ranges { return {_First, _First}; } + if constexpr (_Is_any_of_v<_Pr, _STD equal_to<>, _RANGES equal_to>) { + if (_Count == 1) { + auto _Res = _RANGES find(_First, _Last, _Val, _Pass_fn(_Proj)); + if (_Res != _Last) { + auto _Res_end = _Res; + ++_Res_end; + return {_STD move(_Res), _STD move(_Res_end)}; + } else { + return {_Res, _Res}; + } + } + } + auto _UFirst = _RANGES _Unwrap_iter<_Se>(_STD move(_First)); auto _ULast = _RANGES _Unwrap_sent<_It>(_STD move(_Last)); @@ -2346,6 +2369,20 @@ namespace ranges { return {_First, _First}; } + if constexpr (_Is_any_of_v<_Pr, _STD equal_to<>, _RANGES equal_to>) { + if (_Count == 1) { + auto _Res = _RANGES find(_Range, _Val, _Pass_fn(_Proj)); + auto _Last = _RANGES end(_Range); + if (_Res != _Last) { + auto _Res_end = _Res; + ++_Res_end; + return {_STD move(_Res), _STD move(_Res_end)}; + } else { + return {_Res, _Res}; + } + } + } + if constexpr (sized_range<_Rng>) { const auto _Dist = _RANGES distance(_Range); diff --git a/tests/std/tests/P0896R4_ranges_alg_search_n/test.cpp b/tests/std/tests/P0896R4_ranges_alg_search_n/test.cpp index 3f3a609e568..46a2590bdb7 100644 --- a/tests/std/tests/P0896R4_ranges_alg_search_n/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_search_n/test.cpp @@ -98,6 +98,34 @@ struct instantiator { assert(result.end() == range.begin()); } + // trivial case: unit needle + { + const auto result = ranges::search_n(range, 1, P{1, 42}); + static_assert(same_as>>); + assert(result.begin() == ranges::next(range.begin(), 1)); + assert(result.end() == ranges::next(range.begin(), 2)); + } + { + const auto result = ranges::search_n(ranges::begin(range), ranges::end(range), 1, P{1, 42}); + static_assert(same_as>>); + assert(result.begin() == ranges::next(range.begin(), 1)); + assert(result.end() == ranges::next(range.begin(), 2)); + } + + // trivial case: unit needle with predicate + { + const auto result = ranges::search_n(range, 1, 0, cmp, get_first); + static_assert(same_as>>); + assert(result.begin() == ranges::next(range.begin(), 1)); + assert(result.end() == ranges::next(range.begin(), 2)); + } + { + const auto result = ranges::search_n(ranges::begin(range), ranges::end(range), 1, 0, cmp, get_first); + static_assert(same_as>>); + assert(result.begin() == ranges::next(range.begin(), 1)); + assert(result.end() == ranges::next(range.begin(), 2)); + } + // trivial case: range too small { const auto result = ranges::search_n(range, 99999, 0, cmp, get_first);