Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vectorize find_first_not_of/find_last_not_of member functions (single character) #5102

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 15 additions & 7 deletions benchmarks/src/find_and_count.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ enum class Op {
Count,
StringFind,
StringRFind,
StringFindNotFirstOne,
StringFindNotLastOne,
};

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

using Container = conditional_t<Operation == Op::StringFind || Operation == Op::StringRFind,
basic_string<T, char_traits<T>, Alloc<T>>, vector<T, Alloc<T>>>;
using Container =
conditional_t<Operation >= Op::StringFind, basic_string<T, char_traits<T>, Alloc<T>>, vector<T, Alloc<T>>>;

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

if (pos < size) {
if constexpr (Operation == Op::StringRFind) {
if constexpr (Operation == Op::StringRFind || Operation == Op::StringFindNotLastOne) {
a[size - pos - 1] = T{'1'};
} else {
a[pos] = T{'1'};
Expand All @@ -56,6 +58,10 @@ void bm(benchmark::State& state) {
benchmark::DoNotOptimize(a.find(T{'1'}));
} else if constexpr (Operation == Op::StringRFind) {
benchmark::DoNotOptimize(a.rfind(T{'1'}));
} else if constexpr (Operation == Op::StringFindNotFirstOne) {
benchmark::DoNotOptimize(a.find_first_not_of(T{'0'}));
} else if constexpr (Operation == Op::StringFindNotLastOne) {
benchmark::DoNotOptimize(a.find_last_not_of(T{'0'}));
}
}
}
Expand All @@ -66,27 +72,29 @@ void common_args(auto bm) {
bm->Args({63, 62})->Args({31, 30})->Args({15, 14})->Args({7, 6});
}


BENCHMARK(bm<uint8_t, not_highly_aligned_allocator, Op::FindSized>)->Apply(common_args);
BENCHMARK(bm<uint8_t, highly_aligned_allocator, Op::FindSized>)->Apply(common_args);
BENCHMARK(bm<uint8_t, not_highly_aligned_allocator, Op::FindUnsized>)->Apply(common_args);
BENCHMARK(bm<uint8_t, highly_aligned_allocator, Op::FindUnsized>)->Apply(common_args);
BENCHMARK(bm<uint8_t, not_highly_aligned_allocator, Op::Count>)->Apply(common_args);
BENCHMARK(bm<uint8_t, highly_aligned_allocator, Op::Count>)->Apply(common_args);
BENCHMARK(bm<char, not_highly_aligned_allocator, Op::StringFind>)->Apply(common_args);
BENCHMARK(bm<char, highly_aligned_allocator, Op::StringFind>)->Apply(common_args);
BENCHMARK(bm<char, not_highly_aligned_allocator, Op::StringRFind>)->Apply(common_args);
BENCHMARK(bm<char, highly_aligned_allocator, Op::StringRFind>)->Apply(common_args);
BENCHMARK(bm<char, not_highly_aligned_allocator, Op::StringFindNotFirstOne>)->Apply(common_args);
BENCHMARK(bm<char, not_highly_aligned_allocator, Op::StringFindNotLastOne>)->Apply(common_args);

BENCHMARK(bm<uint16_t, not_highly_aligned_allocator, Op::FindSized>)->Apply(common_args);
BENCHMARK(bm<uint16_t, not_highly_aligned_allocator, Op::Count>)->Apply(common_args);
BENCHMARK(bm<wchar_t, not_highly_aligned_allocator, Op::StringFind>)->Apply(common_args);
BENCHMARK(bm<wchar_t, not_highly_aligned_allocator, Op::StringRFind>)->Apply(common_args);
BENCHMARK(bm<wchar_t, not_highly_aligned_allocator, Op::StringFindNotFirstOne>)->Apply(common_args);
BENCHMARK(bm<wchar_t, not_highly_aligned_allocator, Op::StringFindNotLastOne>)->Apply(common_args);

BENCHMARK(bm<uint32_t, not_highly_aligned_allocator, Op::FindSized>)->Apply(common_args);
BENCHMARK(bm<uint32_t, not_highly_aligned_allocator, Op::Count>)->Apply(common_args);
BENCHMARK(bm<char32_t, not_highly_aligned_allocator, Op::StringFind>)->Apply(common_args);
BENCHMARK(bm<char32_t, not_highly_aligned_allocator, Op::StringRFind>)->Apply(common_args);
BENCHMARK(bm<char32_t, not_highly_aligned_allocator, Op::StringFindNotFirstOne>)->Apply(common_args);
BENCHMARK(bm<char32_t, not_highly_aligned_allocator, Op::StringFindNotLastOne>)->Apply(common_args);

BENCHMARK(bm<uint64_t, not_highly_aligned_allocator, Op::FindSized>)->Apply(common_args);
BENCHMARK(bm<uint64_t, not_highly_aligned_allocator, Op::Count>)->Apply(common_args);
Expand Down
81 changes: 75 additions & 6 deletions stl/inc/__msvc_string_view.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,20 @@ __declspec(noalias) size_t __stdcall __std_find_last_of_trivial_pos_1(
__declspec(noalias) size_t __stdcall __std_find_last_of_trivial_pos_2(
const void* _Haystack, size_t _Haystack_length, const void* _Needle, size_t _Needle_length) noexcept;

const void* __stdcall __std_find_not_ch_1(const void* _First, const void* _Last, uint8_t _Val) noexcept;
const void* __stdcall __std_find_not_ch_2(const void* _First, const void* _Last, uint16_t _Val) noexcept;
const void* __stdcall __std_find_not_ch_4(const void* _First, const void* _Last, uint32_t _Val) noexcept;
const void* __stdcall __std_find_not_ch_8(const void* _First, const void* _Last, uint64_t _Val) noexcept;

__declspec(noalias) size_t __stdcall __std_find_last_not_ch_pos_1(
const void* _First, const void* _Last, uint8_t _Val) noexcept;
__declspec(noalias) size_t __stdcall __std_find_last_not_ch_pos_2(
const void* _First, const void* _Last, uint16_t _Val) noexcept;
__declspec(noalias) size_t __stdcall __std_find_last_not_ch_pos_4(
const void* _First, const void* _Last, uint32_t _Val) noexcept;
__declspec(noalias) size_t __stdcall __std_find_last_not_ch_pos_8(
const void* _First, const void* _Last, uint64_t _Val) noexcept;

} // extern "C"

_STD_BEGIN
Expand All @@ -51,6 +65,35 @@ size_t _Find_last_of_pos_vectorized(const _Ty1* const _Haystack, const size_t _H
}
}

template <class _Ty>
const _Ty* _Find_not_ch_vectorized(const _Ty* const _First, const _Ty* const _Last, const _Ty _Ch) noexcept {
if constexpr (sizeof(_Ty) == 1) {
return static_cast<const _Ty*>(::__std_find_not_ch_1(_First, _Last, static_cast<uint8_t>(_Ch)));
} else if constexpr (sizeof(_Ty) == 2) {
return static_cast<const _Ty*>(::__std_find_not_ch_2(_First, _Last, static_cast<uint16_t>(_Ch)));
} else if constexpr (sizeof(_Ty) == 4) {
return static_cast<const _Ty*>(::__std_find_not_ch_4(_First, _Last, static_cast<uint32_t>(_Ch)));
} else if constexpr (sizeof(_Ty) == 8) {
return static_cast<const _Ty*>(::__std_find_not_ch_8(_First, _Last, static_cast<uint64_t>(_Ch)));
} else {
_STL_INTERNAL_STATIC_ASSERT(false); // unexpected size
}
}

template <class _Ty>
size_t _Find_last_not_ch_pos_vectorized(const _Ty* const _First, const _Ty* const _Last, const _Ty _Ch) noexcept {
if constexpr (sizeof(_Ty) == 1) {
return ::__std_find_last_not_ch_pos_1(_First, _Last, static_cast<uint8_t>(_Ch));
} else if constexpr (sizeof(_Ty) == 2) {
return ::__std_find_last_not_ch_pos_2(_First, _Last, static_cast<uint16_t>(_Ch));
} else if constexpr (sizeof(_Ty) == 4) {
return ::__std_find_last_not_ch_pos_4(_First, _Last, static_cast<uint32_t>(_Ch));
} else if constexpr (sizeof(_Ty) == 8) {
return ::__std_find_last_not_ch_pos_8(_First, _Last, static_cast<uint64_t>(_Ch));
} else {
_STL_INTERNAL_STATIC_ASSERT(false); // unexpected size
}
}
_STD_END

#endif // _USE_STD_VECTOR_ALGORITHMS
Expand Down Expand Up @@ -993,14 +1036,30 @@ template <class _Traits>
constexpr size_t _Traits_find_not_ch(_In_reads_(_Hay_size) const _Traits_ptr_t<_Traits> _Haystack,
const size_t _Hay_size, const size_t _Start_at, const _Traits_ch_t<_Traits> _Ch) noexcept {
// search [_Haystack, _Haystack + _Hay_size) for any value other than _Ch, at/after _Start_at
if (_Start_at < _Hay_size) { // room for match, look for it
const auto _End = _Haystack + _Hay_size;
for (auto _Match_try = _Haystack + _Start_at; _Match_try < _End; ++_Match_try) {
if (!_Traits::eq(*_Match_try, _Ch)) {
return static_cast<size_t>(_Match_try - _Haystack); // found a match
if (_Start_at >= _Hay_size) { // no room for match
return static_cast<size_t>(-1); // no match
}

const auto _End = _Haystack + _Hay_size;

#if _USE_STD_VECTOR_ALGORITHMS
if constexpr (_Is_implementation_handled_char_traits<_Traits>) {
if (!_STD _Is_constant_evaluated()) {
const auto* _Result = _Find_not_ch_vectorized(_Haystack + _Start_at, _End, _Ch);
if (_Result != _End) {
return static_cast<size_t>(_Result - _Haystack);
} else {
return static_cast<size_t>(-1); // no match
}
}
}
#endif // _USE_STD_VECTOR_ALGORITHMS

for (auto _Match_try = _Haystack + _Start_at; _Match_try < _End; ++_Match_try) {
if (!_Traits::eq(*_Match_try, _Ch)) {
return static_cast<size_t>(_Match_try - _Haystack); // found a match
}
}

return static_cast<size_t>(-1); // no match
}
Expand Down Expand Up @@ -1053,7 +1112,17 @@ constexpr size_t _Traits_rfind_not_ch(_In_reads_(_Hay_size) const _Traits_ptr_t<
return static_cast<size_t>(-1);
}

for (auto _Match_try = _Haystack + (_STD min)(_Start_at, _Hay_size - 1);; --_Match_try) {
const size_t _Actual_start_at = (_STD min)(_Start_at, _Hay_size - 1);

#if _USE_STD_VECTOR_ALGORITHMS
if constexpr (_Is_implementation_handled_char_traits<_Traits>) {
if (!_STD _Is_constant_evaluated()) {
return _STD _Find_last_not_ch_pos_vectorized(_Haystack, _Haystack + _Actual_start_at + 1, _Ch);
}
}
#endif // _USE_STD_VECTOR_ALGORITHMS

for (auto _Match_try = _Haystack + _Actual_start_at;; --_Match_try) {
if (!_Traits::eq(*_Match_try, _Ch)) {
return static_cast<size_t>(_Match_try - _Haystack); // found a match
}
Expand Down
Loading