This changes the algorithm to more efficiently skip ranges which cannot match the needle for random access iterators. Specifically, we now search for a mismatching element from the back of the subrange we want to check. When a mismatch occurs we can directly start one after the mismatched element, since there cannot possibly be a matching subrange starting between the start of the subrange we checked and the mismatched element (since all elements have to be equal). The algorithm also remembers the subrange which was already match as being equal and doesn't try to compare it a second time, reducing the time spent in case of a match. ``` Benchmark old new Difference % Difference --------------------------------------------------- -------------- -------------- ------------ -------------- rng::search_n(deque<int>)_(no_match)/1000 458.33 14.22 -444.11 -96.90% rng::search_n(deque<int>)_(no_match)/1024 456.17 13.89 -442.28 -96.95% rng::search_n(deque<int>)_(no_match)/1048576 453420.38 17.69 -453402.69 -100.00% rng::search_n(deque<int>)_(no_match)/8192 3566.08 17.60 -3548.49 -99.51% rng::search_n(deque<int>,_pred)_(no_match)/1000 597.88 15.25 -582.63 -97.45% rng::search_n(deque<int>,_pred)_(no_match)/1024 608.42 15.39 -593.03 -97.47% rng::search_n(deque<int>,_pred)_(no_match)/1048576 594533.99 18.91 -594515.08 -100.00% rng::search_n(deque<int>,_pred)_(no_match)/8192 4670.23 18.88 -4651.35 -99.60% rng::search_n(list<int>)_(no_match)/1000 733.72 730.22 -3.50 -0.48% rng::search_n(list<int>)_(no_match)/1024 759.93 753.10 -6.84 -0.90% rng::search_n(list<int>)_(no_match)/1048576 833841.54 813483.75 -20357.79 -2.44% rng::search_n(list<int>)_(no_match)/8192 8352.18 8417.31 65.14 0.78% rng::search_n(list<int>,_pred)_(no_match)/1000 776.79 789.72 12.93 1.66% rng::search_n(list<int>,_pred)_(no_match)/1024 788.42 806.70 18.28 2.32% rng::search_n(list<int>,_pred)_(no_match)/1048576 955536.40 982976.81 27440.41 2.87% rng::search_n(list<int>,_pred)_(no_match)/8192 8874.02 8915.18 41.16 0.46% rng::search_n(vector<int>)_(no_match)/1000 212.69 3.79 -208.90 -98.22% rng::search_n(vector<int>)_(no_match)/1024 219.67 3.70 -215.96 -98.31% rng::search_n(vector<int>)_(no_match)/1048576 209622.54 3.67 -209618.87 -100.00% rng::search_n(vector<int>)_(no_match)/8192 1643.80 3.83 -1639.98 -99.77% rng::search_n(vector<int>,_pred)_(no_match)/1000 461.93 7.55 -454.38 -98.36% rng::search_n(vector<int>,_pred)_(no_match)/1024 472.43 7.74 -464.69 -98.36% rng::search_n(vector<int>,_pred)_(no_match)/1048576 546180.29 8.71 -546171.58 -100.00% rng::search_n(vector<int>,_pred)_(no_match)/8192 3786.26 7.88 -3778.38 -99.79% std::search_n(deque<int>)_(no_match)/1000 455.53 14.19 -441.34 -96.88% std::search_n(deque<int>)_(no_match)/1024 459.79 13.98 -445.81 -96.96% std::search_n(deque<int>)_(no_match)/1048576 449780.32 17.99 -449762.33 -100.00% std::search_n(deque<int>)_(no_match)/8192 3508.55 17.97 -3490.58 -99.49% std::search_n(deque<int>,_pred)_(no_match)/1000 571.53 17.16 -554.37 -97.00% std::search_n(deque<int>,_pred)_(no_match)/1024 584.43 17.09 -567.34 -97.08% std::search_n(deque<int>,_pred)_(no_match)/1048576 581418.31 19.16 -581399.15 -100.00% std::search_n(deque<int>,_pred)_(no_match)/8192 4661.97 19.36 -4642.61 -99.58% std::search_n(list<int>)_(no_match)/1000 722.45 710.39 -12.06 -1.67% std::search_n(list<int>)_(no_match)/1024 748.50 727.08 -21.42 -2.86% std::search_n(list<int>)_(no_match)/1048576 821655.28 784520.12 -37135.16 -4.52% std::search_n(list<int>)_(no_match)/8192 7941.73 8002.05 60.32 0.76% std::search_n(list<int>,_pred)_(no_match)/1000 766.59 786.31 19.72 2.57% std::search_n(list<int>,_pred)_(no_match)/1024 785.92 804.43 18.51 2.35% std::search_n(list<int>,_pred)_(no_match)/1048576 948252.76 969125.41 20872.65 2.20% std::search_n(list<int>,_pred)_(no_match)/8192 8658.99 8825.71 166.72 1.93% std::search_n(vector<int>)_(no_match)/1000 210.36 3.47 -206.89 -98.35% std::search_n(vector<int>)_(no_match)/1024 217.60 4.13 -213.47 -98.10% std::search_n(vector<int>)_(no_match)/1048576 209386.43 3.51 -209382.92 -100.00% std::search_n(vector<int>)_(no_match)/8192 1643.79 3.50 -1640.29 -99.79% std::search_n(vector<int>,_pred)_(no_match)/1000 460.88 5.44 -455.45 -98.82% std::search_n(vector<int>,_pred)_(no_match)/1024 475.36 5.43 -469.93 -98.86% std::search_n(vector<int>,_pred)_(no_match)/1048576 682722.75 7.15 -682715.60 -100.00% std::search_n(vector<int>,_pred)_(no_match)/8192 3779.95 5.43 -3774.52 -99.86% Geomean 4956.15 87.96 -4868.19 -98.23% ``` Fixes #129327
330 lines
11 KiB
C++
330 lines
11 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// <algorithm>
|
|
|
|
// UNSUPPORTED: c++03, c++11, c++14, c++17
|
|
|
|
// template<forward_iterator I, sentinel_for<I> S, class T,
|
|
// class Pred = ranges::equal_to, class Proj = identity>
|
|
// requires indirectly_comparable<I, const T*, Pred, Proj>
|
|
// constexpr subrange<I>
|
|
// ranges::search_n(I first, S last, iter_difference_t<I> count,
|
|
// const T& value, Pred pred = {}, Proj proj = {});
|
|
// template<forward_range R, class T, class Pred = ranges::equal_to,
|
|
// class Proj = identity>
|
|
// requires indirectly_comparable<iterator_t<R>, const T*, Pred, Proj>
|
|
// constexpr borrowed_subrange_t<R>
|
|
// ranges::search_n(R&& r, range_difference_t<R> count,
|
|
// const T& value, Pred pred = {}, Proj proj = {});
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <ranges>
|
|
|
|
#include "almost_satisfies_types.h"
|
|
#include "test_iterators.h"
|
|
|
|
template <class Iter1, class Sent1 = Iter1>
|
|
concept HasSearchNIt = requires (Iter1 first1, Sent1 last1) {
|
|
std::ranges::search_n(first1, last1, 0, 0);
|
|
};
|
|
|
|
static_assert(HasSearchNIt<int*>);
|
|
static_assert(!HasSearchNIt<ForwardIteratorNotDerivedFrom>);
|
|
static_assert(!HasSearchNIt<ForwardIteratorNotIncrementable>);
|
|
static_assert(!HasSearchNIt<int*, SentinelForNotSemiregular>);
|
|
static_assert(!HasSearchNIt<int*, SentinelForNotWeaklyEqualityComparableWith>);
|
|
static_assert(!HasSearchNIt<int**, int**>); // not indirectly comparable
|
|
|
|
template <class Range1, class Range2 = UncheckedRange<int*>>
|
|
concept HasSearchNR = requires (Range1 range) {
|
|
std::ranges::search_n(range, 0, 0);
|
|
};
|
|
|
|
static_assert(HasSearchNR<UncheckedRange<int*>>);
|
|
static_assert(!HasSearchNR<ForwardRangeNotDerivedFrom>);
|
|
static_assert(!HasSearchNR<ForwardIteratorNotIncrementable>);
|
|
static_assert(!HasSearchNR<ForwardRangeNotSentinelSemiregular>);
|
|
static_assert(!HasSearchNR<ForwardRangeNotSentinelEqualityComparableWith>);
|
|
static_assert(!HasSearchNR<UncheckedRange<int**>>); // not indirectly comparable
|
|
|
|
template <class Iter, class Sent = Iter>
|
|
constexpr void test_iterators() {
|
|
{ // simple test
|
|
{
|
|
int a[] = {1, 2, 3, 4, 5, 6};
|
|
std::same_as<std::ranges::subrange<Iter, Iter>> decltype(auto) ret =
|
|
std::ranges::search_n(Iter(a), Sent(Iter(a + 6)), 1, 3);
|
|
assert(base(ret.begin()) == a + 2);
|
|
assert(base(ret.end()) == a + 3);
|
|
}
|
|
{
|
|
int a[] = {1, 2, 3, 4, 5, 6};
|
|
auto range = std::ranges::subrange(Iter(a), Sent(Iter(a + 6)));
|
|
std::same_as<std::ranges::subrange<Iter, Iter>> decltype(auto) ret = std::ranges::search_n(range, 1, 3);
|
|
assert(base(ret.begin()) == a + 2);
|
|
assert(base(ret.end()) == a + 3);
|
|
}
|
|
}
|
|
|
|
{ // matching part begins at the front
|
|
{
|
|
int a[] = {7, 7, 3, 7, 3, 6};
|
|
auto ret = std::ranges::search_n(Iter(a), Sent(Iter(a + 6)), 2, 7);
|
|
assert(base(ret.begin()) == a);
|
|
assert(base(ret.end()) == a + 2);
|
|
}
|
|
{
|
|
int a[] = {7, 7, 3, 7, 3, 6};
|
|
auto range = std::ranges::subrange(Iter(a), Sent(Iter(a + 6)));
|
|
auto ret = std::ranges::search_n(range, 2, 7);
|
|
assert(base(ret.begin()) == a);
|
|
assert(base(ret.end()) == a + 2);
|
|
}
|
|
}
|
|
|
|
{ // matching part ends at the back
|
|
{
|
|
int a[] = {9, 3, 6, 4, 4};
|
|
auto ret = std::ranges::search_n(Iter(a), Sent(Iter(a + 5)), 2, 4);
|
|
assert(base(ret.begin()) == a + 3);
|
|
assert(base(ret.end()) == a + 5);
|
|
}
|
|
{
|
|
int a[] = {9, 3, 6, 4, 4};
|
|
auto range = std::ranges::subrange(Iter(a), Sent(Iter(a + 5)));
|
|
auto ret = std::ranges::search_n(range, 2, 4);
|
|
assert(base(ret.begin()) == a + 3);
|
|
assert(base(ret.end()) == a + 5);
|
|
}
|
|
}
|
|
|
|
{ // pattern does not match
|
|
{
|
|
int a[] = {9, 3, 6, 4, 8};
|
|
auto ret = std::ranges::search_n(Iter(a), Sent(Iter(a + 5)), 1, 1);
|
|
assert(base(ret.begin()) == a + 5);
|
|
assert(base(ret.end()) == a + 5);
|
|
}
|
|
{
|
|
int a[] = {9, 3, 6, 4, 8};
|
|
auto range = std::ranges::subrange(Iter(a), Sent(Iter(a + 5)));
|
|
auto ret = std::ranges::search_n(range, 1, 1);
|
|
assert(base(ret.begin()) == a + 5);
|
|
assert(base(ret.end()) == a + 5);
|
|
}
|
|
}
|
|
|
|
{ // range and pattern are identical
|
|
{
|
|
int a[] = {1, 1, 1, 1};
|
|
auto ret = std::ranges::search_n(Iter(a), Sent(Iter(a + 4)), 4, 1);
|
|
assert(base(ret.begin()) == a);
|
|
assert(base(ret.end()) == a + 4);
|
|
}
|
|
{
|
|
int a[] = {1, 1, 1, 1};
|
|
auto range = std::ranges::subrange(Iter(a), Sent(Iter(a + 4)));
|
|
auto ret = std::ranges::search_n(range, 4, 1);
|
|
assert(base(ret.begin()) == a);
|
|
assert(base(ret.end()) == a + 4);
|
|
}
|
|
}
|
|
|
|
{ // pattern is longer than range
|
|
{
|
|
int a[] = {3, 3, 3};
|
|
auto ret = std::ranges::search_n(Iter(a), Sent(Iter(a + 3)), 4, 3);
|
|
assert(base(ret.begin()) == a + 3);
|
|
assert(base(ret.end()) == a + 3);
|
|
}
|
|
{
|
|
int a[] = {3, 3, 3};
|
|
auto range = std::ranges::subrange(Iter(a), Sent(Iter(a + 3)));
|
|
auto ret = std::ranges::search_n(range, 4, 3);
|
|
assert(base(ret.begin()) == a + 3);
|
|
assert(base(ret.end()) == a + 3);
|
|
}
|
|
}
|
|
|
|
{ // pattern has zero length
|
|
{
|
|
int a[] = {6, 7, 8};
|
|
auto ret = std::ranges::search_n(Iter(a), Sent(Iter(a + 3)), 0, 7);
|
|
assert(base(ret.begin()) == a);
|
|
assert(base(ret.end()) == a);
|
|
}
|
|
{
|
|
int a[] = {6, 7, 8};
|
|
auto range = std::ranges::subrange(Iter(a), Sent(Iter(a + 3)));
|
|
auto ret = std::ranges::search_n(range, 0, 7);
|
|
assert(base(ret.begin()) == a);
|
|
assert(base(ret.end()) == a);
|
|
}
|
|
}
|
|
|
|
{ // range has zero length
|
|
{
|
|
std::array<int, 0> a = {};
|
|
auto ret = std::ranges::search_n(Iter(a.data()), Sent(Iter(a.data())), 1, 1);
|
|
assert(base(ret.begin()) == a.data());
|
|
assert(base(ret.end()) == a.data());
|
|
}
|
|
{
|
|
std::array<int, 0> a = {};
|
|
auto range = std::ranges::subrange(Iter(a.data()), Sent(Iter(a.data())));
|
|
auto ret = std::ranges::search_n(range, 1, 1);
|
|
assert(base(ret.begin()) == a.data());
|
|
assert(base(ret.end()) == a.data());
|
|
}
|
|
}
|
|
|
|
{ // check that the first match is returned
|
|
{ // Match is at the start
|
|
{
|
|
int a[] = {6, 6, 8, 6, 6, 8, 6, 6, 8};
|
|
auto ret = std::ranges::search_n(Iter(a), Sent(Iter(a + 9)), 2, 6);
|
|
assert(base(ret.begin()) == a);
|
|
assert(base(ret.end()) == a + 2);
|
|
}
|
|
{
|
|
int a[] = {6, 6, 8, 6, 6, 8, 6, 6, 8};
|
|
auto range = std::ranges::subrange(Iter(a), Sent(Iter(a + 9)));
|
|
auto ret = std::ranges::search_n(range, 2, 6);
|
|
assert(base(ret.begin()) == a);
|
|
assert(base(ret.end()) == a + 2);
|
|
}
|
|
}
|
|
{ // Match is in the middle
|
|
{
|
|
int a[] = {6, 8, 8, 6, 6, 8, 6, 6, 8};
|
|
auto ret = std::ranges::search_n(Iter(a), Sent(Iter(a + 9)), 2, 6);
|
|
assert(base(ret.begin()) == a + 3);
|
|
assert(base(ret.end()) == a + 5);
|
|
}
|
|
{
|
|
int a[] = {6, 8, 8, 6, 6, 8, 6, 6, 8};
|
|
auto range = std::ranges::subrange(Iter(a), Sent(Iter(a + 9)));
|
|
auto ret = std::ranges::search_n(range, 2, 6);
|
|
assert(base(ret.begin()) == a + 3);
|
|
assert(base(ret.end()) == a + 5);
|
|
}
|
|
}
|
|
{ // Match is at the end
|
|
{
|
|
int a[] = {6, 6, 8, 6, 6, 8, 6, 6, 6};
|
|
auto ret = std::ranges::search_n(Iter(a), Sent(Iter(a + 9)), 3, 6);
|
|
assert(base(ret.begin()) == a + 6);
|
|
assert(base(ret.end()) == a + 9);
|
|
}
|
|
{
|
|
int a[] = {6, 6, 8, 6, 6, 8, 6, 6, 6};
|
|
auto range = std::ranges::subrange(Iter(a), Sent(Iter(a + 9)));
|
|
auto ret = std::ranges::search_n(range, 3, 6);
|
|
assert(base(ret.begin()) == a + 6);
|
|
assert(base(ret.end()) == a + 9);
|
|
}
|
|
}
|
|
}
|
|
|
|
{ // check that the predicate is used
|
|
{
|
|
int a[] = {1, 4, 4, 3, 6, 1};
|
|
auto ret = std::ranges::search_n(Iter(a), Sent(Iter(a + 6)),
|
|
3,
|
|
4,
|
|
[](int l, int r) { return l == r || l == 1; });
|
|
assert(base(ret.begin()) == a);
|
|
assert(base(ret.end()) == a + 3);
|
|
}
|
|
{
|
|
int a[] = {1, 4, 4, 3, 6, 1};
|
|
auto range = std::ranges::subrange(Iter(a), Sent(Iter(a + 6)));
|
|
auto ret = std::ranges::search_n(range, 3, 4, [](int l, int r) { return l == r || l == 1; });
|
|
assert(base(ret.begin()) == a);
|
|
assert(base(ret.end()) == a + 3);
|
|
}
|
|
}
|
|
|
|
{ // check that the projections are used
|
|
{
|
|
int a[] = {1, 3, 1, 6, 5, 6};
|
|
auto ret = std::ranges::search_n(Iter(a), Sent(Iter(a + 6)),
|
|
3,
|
|
6,
|
|
{},
|
|
[](int i) { return i % 2 == 0 ? i : i + 1; });
|
|
assert(base(ret.begin()) == a + 3);
|
|
assert(base(ret.end()) == a + 6);
|
|
}
|
|
{
|
|
int a[] = {1, 3, 1, 6, 5, 6};
|
|
auto range = std::ranges::subrange(Iter(a), Sent(Iter(a + 6)));
|
|
auto ret = std::ranges::search_n(range,
|
|
3,
|
|
6,
|
|
{},
|
|
[](int i) { return i % 2 == 0 ? i : i + 1; });
|
|
assert(base(ret.begin()) == a + 3);
|
|
assert(base(ret.end()) == a + 6);
|
|
}
|
|
}
|
|
}
|
|
constexpr bool test() {
|
|
test_iterators<forward_iterator<int*>>();
|
|
test_iterators<forward_iterator<int*>, sized_sentinel<forward_iterator<int*>>>();
|
|
test_iterators<bidirectional_iterator<int*>>();
|
|
test_iterators<bidirectional_iterator<int*>, sized_sentinel<bidirectional_iterator<int*>>>();
|
|
test_iterators<random_access_iterator<int*>>();
|
|
test_iterators<random_access_iterator<int*>, sized_sentinel<random_access_iterator<int*>>>();
|
|
test_iterators<contiguous_iterator<int*>>();
|
|
test_iterators<contiguous_iterator<int*>, sized_sentinel<contiguous_iterator<int*>>>();
|
|
test_iterators<int*>();
|
|
|
|
{ // check that std::invoke is used
|
|
struct S {
|
|
int i;
|
|
|
|
constexpr S identity() {
|
|
return *this;
|
|
}
|
|
|
|
constexpr bool compare(int o) {
|
|
return i == o;
|
|
}
|
|
};
|
|
{
|
|
S a[] = {{1}, {2}, {3}, {4}};
|
|
auto ret = std::ranges::search_n(a, a + 4, 1, 2, &S::compare, &S::identity);
|
|
assert(ret.begin() == a + 1);
|
|
assert(ret.end() == a + 2);
|
|
}
|
|
{
|
|
S a[] = {{1}, {2}, {3}, {4}};
|
|
auto ret = std::ranges::search_n(a, 1, 2, &S::compare, &S::identity);
|
|
assert(ret.begin() == a + 1);
|
|
assert(ret.end() == a + 2);
|
|
}
|
|
}
|
|
|
|
{ // check that std::ranges::dangling is returned
|
|
[[maybe_unused]] std::same_as<std::ranges::dangling> decltype(auto) ret =
|
|
std::ranges::search_n(std::array {1, 2, 3, 4}, 1, 0);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int main(int, char**) {
|
|
test();
|
|
static_assert(test());
|
|
|
|
return 0;
|
|
}
|