Skip to content

Commit a17c365

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

File tree

7 files changed

+82
-22
lines changed

7 files changed

+82
-22
lines changed

libcxx/include/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,7 @@ set(files
604604
__memory/unique_temporary_buffer.h
605605
__memory/uses_allocator.h
606606
__memory/uses_allocator_construction.h
607+
__memory/valid_range.h
607608
__memory_resource/memory_resource.h
608609
__memory_resource/monotonic_buffer_resource.h
609610
__memory_resource/polymorphic_allocator.h
@@ -927,7 +928,6 @@ set(files
927928
__utility/in_place.h
928929
__utility/integer_sequence.h
929930
__utility/is_pointer_in_range.h
930-
__utility/is_valid_range.h
931931
__utility/lazy_synth_three_way_comparator.h
932932
__utility/move.h
933933
__utility/no_destroy.h

libcxx/include/__algorithm/find_if.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#define _LIBCPP___ALGORITHM_FIND_IF_H
1212

1313
#include <__config>
14+
#include <__memory/valid_range.h>
1415

1516
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
1617
# pragma GCC system_header
@@ -21,6 +22,8 @@ _LIBCPP_BEGIN_NAMESPACE_STD
2122
template <class _InputIterator, class _Predicate>
2223
[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _InputIterator
2324
find_if(_InputIterator __first, _InputIterator __last, _Predicate __pred) {
25+
std::__assume_valid_range(__first, __last);
26+
2427
for (; __first != __last; ++__first)
2528
if (__pred(*__first))
2629
break;

libcxx/include/__utility/is_valid_range.h renamed to libcxx/include/__memory/valid_range.h

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,15 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9-
#ifndef _LIBCPP___UTILITY_IS_VALID_RANGE_H
10-
#define _LIBCPP___UTILITY_IS_VALID_RANGE_H
9+
#ifndef _LIBCPP___MEMORY_VALID_RANGE_H
10+
#define _LIBCPP___MEMORY_VALID_RANGE_H
1111

1212
#include <__algorithm/comp.h>
13+
#include <__assert>
1314
#include <__config>
15+
#include <__iterator/iterator_traits.h>
16+
#include <__memory/assume_aligned.h>
17+
#include <__memory/pointer_traits.h>
1418
#include <__type_traits/is_constant_evaluated.h>
1519

1620
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -32,6 +36,20 @@ __is_valid_range(const _Tp* __first, const _Tp* __last) {
3236
return !__less<>()(__last, __first);
3337
}
3438

39+
template <class _Iter, class _Sent>
40+
_LIBCPP_HIDE_FROM_ABI void __assume_valid_range(_Iter&& __first, _Sent&& __last) {
41+
#if __has_builtin(__builtin_assume_dereferenceable) && !defined(_LIBCPP_CXX03_LANG)
42+
if constexpr (__libcpp_is_contiguous_iterator<_Iter>::value && is_same<_Iter, _Sent>::value) {
43+
_LIBCPP_ASSERT_INTERNAL(std::__is_valid_range(std::__to_address(__first), std::__to_address(__last)),
44+
"Valid range assumption does not hold");
45+
using __value_type = typename iterator_traits<_Iter>::value_type;
46+
__builtin_assume_dereferenceable(std::__to_address(__first), (__last - __first) * sizeof(__value_type));
47+
(void)std::__assume_aligned<_LIBCPP_ALIGNOF(__value_type)>(std::__to_address(__first));
48+
(void)std::__assume_aligned<_LIBCPP_ALIGNOF(__value_type)>(std::__to_address(__last));
49+
}
50+
#endif
51+
}
52+
3553
_LIBCPP_END_NAMESPACE_STD
3654

37-
#endif // _LIBCPP___UTILITY_IS_VALID_RANGE_H
55+
#endif // _LIBCPP___MEMORY_VALID_RANGE_H

libcxx/include/__utility/is_pointer_in_range.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@
1212
#include <__algorithm/comp.h>
1313
#include <__assert>
1414
#include <__config>
15+
#include <__memory/valid_range.h>
1516
#include <__type_traits/enable_if.h>
1617
#include <__type_traits/integral_constant.h>
1718
#include <__type_traits/is_constant_evaluated.h>
1819
#include <__type_traits/void_t.h>
1920
#include <__utility/declval.h>
20-
#include <__utility/is_valid_range.h>
2121

2222
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
2323
# pragma GCC system_header

libcxx/include/streambuf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,8 @@ protected:
117117
# include <__assert>
118118
# include <__fwd/streambuf.h>
119119
# include <__locale>
120+
# include <__memory/valid_range.h>
120121
# include <__type_traits/is_same.h>
121-
# include <__utility/is_valid_range.h>
122122
# include <__utility/scope_guard.h>
123123
# include <climits>
124124
# include <ios>

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)