Skip to content

Commit fb59f1d

Browse files
Vectorize find_first_not_of/find_last_not_of member functions (single character) (#5102)
Co-authored-by: Stephan T. Lavavej <stl@nuwen.net>
1 parent 1f6e5b1 commit fb59f1d

4 files changed

Lines changed: 260 additions & 43 deletions

File tree

benchmarks/src/find_and_count.cpp

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ enum class Op {
1919
Count,
2020
StringFind,
2121
StringRFind,
22+
StringFindNotFirstOne,
23+
StringFindNotLastOne,
2224
};
2325

2426
using namespace std;
@@ -28,13 +30,13 @@ void bm(benchmark::State& state) {
2830
const auto size = static_cast<size_t>(state.range(0));
2931
const auto pos = static_cast<size_t>(state.range(1));
3032

31-
using Container = conditional_t<Operation == Op::StringFind || Operation == Op::StringRFind,
32-
basic_string<T, char_traits<T>, Alloc<T>>, vector<T, Alloc<T>>>;
33+
using Container =
34+
conditional_t<Operation >= Op::StringFind, basic_string<T, char_traits<T>, Alloc<T>>, vector<T, Alloc<T>>>;
3335

3436
Container a(size, T{'0'});
3537

3638
if (pos < size) {
37-
if constexpr (Operation == Op::StringRFind) {
39+
if constexpr (Operation == Op::StringRFind || Operation == Op::StringFindNotLastOne) {
3840
a[size - pos - 1] = T{'1'};
3941
} else {
4042
a[pos] = T{'1'};
@@ -56,6 +58,10 @@ void bm(benchmark::State& state) {
5658
benchmark::DoNotOptimize(a.find(T{'1'}));
5759
} else if constexpr (Operation == Op::StringRFind) {
5860
benchmark::DoNotOptimize(a.rfind(T{'1'}));
61+
} else if constexpr (Operation == Op::StringFindNotFirstOne) {
62+
benchmark::DoNotOptimize(a.find_first_not_of(T{'0'}));
63+
} else if constexpr (Operation == Op::StringFindNotLastOne) {
64+
benchmark::DoNotOptimize(a.find_last_not_of(T{'0'}));
5965
}
6066
}
6167
}
@@ -66,27 +72,29 @@ void common_args(auto bm) {
6672
bm->Args({63, 62})->Args({31, 30})->Args({15, 14})->Args({7, 6});
6773
}
6874

69-
7075
BENCHMARK(bm<uint8_t, not_highly_aligned_allocator, Op::FindSized>)->Apply(common_args);
7176
BENCHMARK(bm<uint8_t, highly_aligned_allocator, Op::FindSized>)->Apply(common_args);
7277
BENCHMARK(bm<uint8_t, not_highly_aligned_allocator, Op::FindUnsized>)->Apply(common_args);
7378
BENCHMARK(bm<uint8_t, highly_aligned_allocator, Op::FindUnsized>)->Apply(common_args);
7479
BENCHMARK(bm<uint8_t, not_highly_aligned_allocator, Op::Count>)->Apply(common_args);
75-
BENCHMARK(bm<uint8_t, highly_aligned_allocator, Op::Count>)->Apply(common_args);
7680
BENCHMARK(bm<char, not_highly_aligned_allocator, Op::StringFind>)->Apply(common_args);
77-
BENCHMARK(bm<char, highly_aligned_allocator, Op::StringFind>)->Apply(common_args);
7881
BENCHMARK(bm<char, not_highly_aligned_allocator, Op::StringRFind>)->Apply(common_args);
79-
BENCHMARK(bm<char, highly_aligned_allocator, Op::StringRFind>)->Apply(common_args);
82+
BENCHMARK(bm<char, not_highly_aligned_allocator, Op::StringFindNotFirstOne>)->Apply(common_args);
83+
BENCHMARK(bm<char, not_highly_aligned_allocator, Op::StringFindNotLastOne>)->Apply(common_args);
8084

8185
BENCHMARK(bm<uint16_t, not_highly_aligned_allocator, Op::FindSized>)->Apply(common_args);
8286
BENCHMARK(bm<uint16_t, not_highly_aligned_allocator, Op::Count>)->Apply(common_args);
8387
BENCHMARK(bm<wchar_t, not_highly_aligned_allocator, Op::StringFind>)->Apply(common_args);
8488
BENCHMARK(bm<wchar_t, not_highly_aligned_allocator, Op::StringRFind>)->Apply(common_args);
89+
BENCHMARK(bm<wchar_t, not_highly_aligned_allocator, Op::StringFindNotFirstOne>)->Apply(common_args);
90+
BENCHMARK(bm<wchar_t, not_highly_aligned_allocator, Op::StringFindNotLastOne>)->Apply(common_args);
8591

8692
BENCHMARK(bm<uint32_t, not_highly_aligned_allocator, Op::FindSized>)->Apply(common_args);
8793
BENCHMARK(bm<uint32_t, not_highly_aligned_allocator, Op::Count>)->Apply(common_args);
8894
BENCHMARK(bm<char32_t, not_highly_aligned_allocator, Op::StringFind>)->Apply(common_args);
8995
BENCHMARK(bm<char32_t, not_highly_aligned_allocator, Op::StringRFind>)->Apply(common_args);
96+
BENCHMARK(bm<char32_t, not_highly_aligned_allocator, Op::StringFindNotFirstOne>)->Apply(common_args);
97+
BENCHMARK(bm<char32_t, not_highly_aligned_allocator, Op::StringFindNotLastOne>)->Apply(common_args);
9098

9199
BENCHMARK(bm<uint64_t, not_highly_aligned_allocator, Op::FindSized>)->Apply(common_args);
92100
BENCHMARK(bm<uint64_t, not_highly_aligned_allocator, Op::Count>)->Apply(common_args);

stl/inc/__msvc_string_view.hpp

Lines changed: 75 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,20 @@ __declspec(noalias) size_t __stdcall __std_find_last_of_trivial_pos_1(
4343
__declspec(noalias) size_t __stdcall __std_find_last_of_trivial_pos_2(
4444
const void* _Haystack, size_t _Haystack_length, const void* _Needle, size_t _Needle_length) noexcept;
4545

46+
const void* __stdcall __std_find_not_ch_1(const void* _First, const void* _Last, uint8_t _Val) noexcept;
47+
const void* __stdcall __std_find_not_ch_2(const void* _First, const void* _Last, uint16_t _Val) noexcept;
48+
const void* __stdcall __std_find_not_ch_4(const void* _First, const void* _Last, uint32_t _Val) noexcept;
49+
const void* __stdcall __std_find_not_ch_8(const void* _First, const void* _Last, uint64_t _Val) noexcept;
50+
51+
__declspec(noalias) size_t __stdcall __std_find_last_not_ch_pos_1(
52+
const void* _First, const void* _Last, uint8_t _Val) noexcept;
53+
__declspec(noalias) size_t __stdcall __std_find_last_not_ch_pos_2(
54+
const void* _First, const void* _Last, uint16_t _Val) noexcept;
55+
__declspec(noalias) size_t __stdcall __std_find_last_not_ch_pos_4(
56+
const void* _First, const void* _Last, uint32_t _Val) noexcept;
57+
__declspec(noalias) size_t __stdcall __std_find_last_not_ch_pos_8(
58+
const void* _First, const void* _Last, uint64_t _Val) noexcept;
59+
4660
} // extern "C"
4761

4862
_STD_BEGIN
@@ -77,6 +91,35 @@ size_t _Find_last_of_pos_vectorized(const _Ty1* const _Haystack, const size_t _H
7791
}
7892
}
7993

94+
template <class _Ty>
95+
const _Ty* _Find_not_ch_vectorized(const _Ty* const _First, const _Ty* const _Last, const _Ty _Ch) noexcept {
96+
if constexpr (sizeof(_Ty) == 1) {
97+
return static_cast<const _Ty*>(::__std_find_not_ch_1(_First, _Last, static_cast<uint8_t>(_Ch)));
98+
} else if constexpr (sizeof(_Ty) == 2) {
99+
return static_cast<const _Ty*>(::__std_find_not_ch_2(_First, _Last, static_cast<uint16_t>(_Ch)));
100+
} else if constexpr (sizeof(_Ty) == 4) {
101+
return static_cast<const _Ty*>(::__std_find_not_ch_4(_First, _Last, static_cast<uint32_t>(_Ch)));
102+
} else if constexpr (sizeof(_Ty) == 8) {
103+
return static_cast<const _Ty*>(::__std_find_not_ch_8(_First, _Last, static_cast<uint64_t>(_Ch)));
104+
} else {
105+
_STL_INTERNAL_STATIC_ASSERT(false); // unexpected size
106+
}
107+
}
108+
109+
template <class _Ty>
110+
size_t _Find_last_not_ch_pos_vectorized(const _Ty* const _First, const _Ty* const _Last, const _Ty _Ch) noexcept {
111+
if constexpr (sizeof(_Ty) == 1) {
112+
return ::__std_find_last_not_ch_pos_1(_First, _Last, static_cast<uint8_t>(_Ch));
113+
} else if constexpr (sizeof(_Ty) == 2) {
114+
return ::__std_find_last_not_ch_pos_2(_First, _Last, static_cast<uint16_t>(_Ch));
115+
} else if constexpr (sizeof(_Ty) == 4) {
116+
return ::__std_find_last_not_ch_pos_4(_First, _Last, static_cast<uint32_t>(_Ch));
117+
} else if constexpr (sizeof(_Ty) == 8) {
118+
return ::__std_find_last_not_ch_pos_8(_First, _Last, static_cast<uint64_t>(_Ch));
119+
} else {
120+
_STL_INTERNAL_STATIC_ASSERT(false); // unexpected size
121+
}
122+
}
80123
_STD_END
81124

82125
#endif // _USE_STD_VECTOR_ALGORITHMS
@@ -987,14 +1030,30 @@ template <class _Traits>
9871030
constexpr size_t _Traits_find_not_ch(_In_reads_(_Hay_size) const _Traits_ptr_t<_Traits> _Haystack,
9881031
const size_t _Hay_size, const size_t _Start_at, const _Traits_ch_t<_Traits> _Ch) noexcept {
9891032
// search [_Haystack, _Haystack + _Hay_size) for any value other than _Ch, at/after _Start_at
990-
if (_Start_at < _Hay_size) { // room for match, look for it
991-
const auto _End = _Haystack + _Hay_size;
992-
for (auto _Match_try = _Haystack + _Start_at; _Match_try < _End; ++_Match_try) {
993-
if (!_Traits::eq(*_Match_try, _Ch)) {
994-
return static_cast<size_t>(_Match_try - _Haystack); // found a match
1033+
if (_Start_at >= _Hay_size) { // no room for match
1034+
return static_cast<size_t>(-1); // no match
1035+
}
1036+
1037+
const auto _End = _Haystack + _Hay_size;
1038+
1039+
#if _USE_STD_VECTOR_ALGORITHMS
1040+
if constexpr (_Is_implementation_handled_char_traits<_Traits>) {
1041+
if (!_STD _Is_constant_evaluated()) {
1042+
const auto _Result = _STD _Find_not_ch_vectorized(_Haystack + _Start_at, _End, _Ch);
1043+
if (_Result != _End) {
1044+
return static_cast<size_t>(_Result - _Haystack);
1045+
} else {
1046+
return static_cast<size_t>(-1); // no match
9951047
}
9961048
}
9971049
}
1050+
#endif // _USE_STD_VECTOR_ALGORITHMS
1051+
1052+
for (auto _Match_try = _Haystack + _Start_at; _Match_try < _End; ++_Match_try) {
1053+
if (!_Traits::eq(*_Match_try, _Ch)) {
1054+
return static_cast<size_t>(_Match_try - _Haystack); // found a match
1055+
}
1056+
}
9981057

9991058
return static_cast<size_t>(-1); // no match
10001059
}
@@ -1047,7 +1106,17 @@ constexpr size_t _Traits_rfind_not_ch(_In_reads_(_Hay_size) const _Traits_ptr_t<
10471106
return static_cast<size_t>(-1);
10481107
}
10491108

1050-
for (auto _Match_try = _Haystack + (_STD min)(_Start_at, _Hay_size - 1);; --_Match_try) {
1109+
const size_t _Actual_start_at = (_STD min)(_Start_at, _Hay_size - 1);
1110+
1111+
#if _USE_STD_VECTOR_ALGORITHMS
1112+
if constexpr (_Is_implementation_handled_char_traits<_Traits>) {
1113+
if (!_STD _Is_constant_evaluated()) {
1114+
return _STD _Find_last_not_ch_pos_vectorized(_Haystack, _Haystack + _Actual_start_at + 1, _Ch);
1115+
}
1116+
}
1117+
#endif // _USE_STD_VECTOR_ALGORITHMS
1118+
1119+
for (auto _Match_try = _Haystack + _Actual_start_at;; --_Match_try) {
10511120
if (!_Traits::eq(*_Match_try, _Ch)) {
10521121
return static_cast<size_t>(_Match_try - _Haystack); // found a match
10531122
}

0 commit comments

Comments
 (0)