Skip to content

Commit 0a15db6

Browse files
committed
[libc++] Optimize std::find_if
1 parent 0400b9a commit 0a15db6

File tree

5 files changed

+105
-19
lines changed

5 files changed

+105
-19
lines changed

libcxx/include/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ set(files
22
__algorithm/adjacent_find.h
33
__algorithm/all_of.h
44
__algorithm/any_of.h
5+
__algorithm/assume_valid_range.h
56
__algorithm/binary_search.h
67
__algorithm/clamp.h
78
__algorithm/comp.h
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// -*- C++ -*-
2+
//===----------------------------------------------------------------------===//
3+
//
4+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5+
// See https://llvm.org/LICENSE.txt for license information.
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
//
8+
//===----------------------------------------------------------------------===//
9+
10+
#ifndef _LIBCPP___ALGORITHM_ASSUME_VALID_RANGE_H
11+
#define _LIBCPP___ALGORITHM_ASSUME_VALID_RANGE_H
12+
13+
#include <__config>
14+
#include <__memory/assume_aligned.h>
15+
#include <__utility/move.h>
16+
#include <__utility/pair.h>
17+
18+
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
19+
# pragma GCC system_header
20+
#endif
21+
22+
_LIBCPP_BEGIN_NAMESPACE_STD
23+
24+
template <class _Iter, class _Sent>
25+
pair<_Iter, _Sent> __assume_valid_range(_Iter __first, _Sent __last) {
26+
return std::make_pair(std::move(__first), std::move(__last));
27+
}
28+
29+
template <class _Tp>
30+
pair<_Tp*, _Tp*> __assume_valid_range(_Tp* __first, _Tp* __last) {
31+
#if __has_builtin(__builtin_assume_dereferenceable)
32+
__builtin_assume_dereferenceable(__first, (__last - __first) * sizeof(_Tp));
33+
#endif
34+
__first = std::__assume_aligned<_LIBCPP_ALIGNOF(_Tp)>(__first);
35+
__last = std::__assume_aligned<_LIBCPP_ALIGNOF(_Tp)>(__last);
36+
return std::make_pair(__first, __last);
37+
}
38+
39+
_LIBCPP_END_NAMESPACE_STD
40+
41+
#endif // _LIBCPP___ALGORITHM_ASSUME_VALID_RANGE_H

libcxx/include/__algorithm/find_if.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#ifndef _LIBCPP___ALGORITHM_FIND_IF_H
1111
#define _LIBCPP___ALGORITHM_FIND_IF_H
1212

13+
#include <__algorithm/assume_valid_range.h>
14+
#include <__algorithm/unwrap_iter.h>
1315
#include <__config>
1416

1517
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -21,10 +23,13 @@ _LIBCPP_BEGIN_NAMESPACE_STD
2123
template <class _InputIterator, class _Predicate>
2224
[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _InputIterator
2325
find_if(_InputIterator __first, _InputIterator __last, _Predicate __pred) {
24-
for (; __first != __last; ++__first)
25-
if (__pred(*__first))
26+
auto [__ufirst, __ulast] = std::__assume_valid_range(std::__unwrap_iter(__first), std::__unwrap_iter(__last));
27+
28+
for (; __ufirst != __ulast; ++__ufirst) {
29+
if (__pred(*__ufirst))
2630
break;
27-
return __first;
31+
}
32+
return std::__rewrap_iter(__first, __ufirst);
2833
}
2934

3035
_LIBCPP_END_NAMESPACE_STD

libcxx/test/benchmarks/algorithms/nonmodifying/find.bench.cpp

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,30 +21,18 @@
2121
int main(int argc, char** argv) {
2222
auto std_find = [](auto first, auto last, auto const& value) { return std::find(first, last, value); };
2323
auto std_find_if = [](auto first, auto last, auto const& value) {
24-
return std::find_if(first, last, [&](auto element) {
25-
benchmark::DoNotOptimize(element);
26-
return element == value;
27-
});
24+
return std::find_if(first, last, [&](auto element) { return element == value; });
2825
};
2926
auto std_find_if_not = [](auto first, auto last, auto const& value) {
30-
return std::find_if_not(first, last, [&](auto element) {
31-
benchmark::DoNotOptimize(element);
32-
return element != value;
33-
});
27+
return std::find_if_not(first, last, [&](auto element) { return element != value; });
3428
};
3529

3630
auto ranges_find = [](auto first, auto last, auto const& value) { return std::ranges::find(first, last, value); };
3731
auto ranges_find_if = [](auto first, auto last, auto const& value) {
38-
return std::ranges::find_if(first, last, [&](auto element) {
39-
benchmark::DoNotOptimize(element);
40-
return element == value;
41-
});
32+
return std::ranges::find_if(first, last, [&](auto element) { return element == value; });
4233
};
4334
auto ranges_find_if_not = [](auto first, auto last, auto const& value) {
44-
return std::ranges::find_if_not(first, last, [&](auto element) {
45-
benchmark::DoNotOptimize(element);
46-
return element != value;
47-
});
35+
return std::ranges::find_if_not(first, last, [&](auto element) { return element != value; });
4836
};
4937

5038
auto register_benchmarks = [&](auto bm, std::string comment) {
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#include <benchmark/benchmark.h>
2+
3+
template <class Iter, class ValueT>
4+
Iter my_find(Iter first, Iter last, const ValueT& i) {
5+
for (; first != last; ++first) {
6+
if (*first == i)
7+
break;
8+
}
9+
return first;
10+
}
11+
12+
static auto bm_find_if_no_vectorization(benchmark::State& state) {
13+
std::size_t const size = 8192;
14+
std::vector<short> c(size, 0);
15+
16+
for ([[maybe_unused]] auto _ : state) {
17+
benchmark::DoNotOptimize(c);
18+
std::vector<short>::iterator result;
19+
[[clang::noinline]] result = my_find(c.begin(), c.end(), 1);
20+
benchmark::DoNotOptimize(result);
21+
}
22+
}
23+
BENCHMARK(bm_find_if_no_vectorization);
24+
25+
static auto bm_find_if_autovectorization(benchmark::State& state) {
26+
std::size_t const size = 8192;
27+
std::vector<short> c(size, 0);
28+
29+
for ([[maybe_unused]] auto _ : state) {
30+
benchmark::DoNotOptimize(c);
31+
std::vector<short>::iterator result;
32+
[[clang::noinline]] result = find_if(c.begin(), c.end(), [](short i) { return i == 1; });
33+
benchmark::DoNotOptimize(result);
34+
}
35+
}
36+
BENCHMARK(bm_find_if_autovectorization);
37+
38+
static auto bm_find_manual_vectorization(benchmark::State& state) {
39+
std::size_t const size = 8192;
40+
std::vector<short> c(size, 0);
41+
42+
for ([[maybe_unused]] auto _ : state) {
43+
benchmark::DoNotOptimize(c);
44+
std::vector<short>::iterator result;
45+
[[clang::noinline]] result = find(c.begin(), c.end(), 1);
46+
benchmark::DoNotOptimize(result);
47+
}
48+
}
49+
BENCHMARK(bm_find_manual_vectorization);
50+
51+
BENCHMARK_MAIN();

0 commit comments

Comments
 (0)