From e4bb843f4e214d5c013fbcf04c97b436dd3ec333 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Mon, 1 Apr 2024 22:54:44 +0300 Subject: [PATCH 01/12] Vectorize lexicographical_compare! --- benchmarks/src/mismatch.cpp | 27 +++- stl/inc/algorithm | 25 +++- stl/inc/regex | 5 +- stl/inc/xutility | 133 +++++++++++++----- .../env.lst | 3 + .../test.compile.pass.cpp | 44 +++--- .../VSO_0000000_vector_algorithms/test.cpp | 133 ++++++++++++------ 7 files changed, 265 insertions(+), 105 deletions(-) diff --git a/benchmarks/src/mismatch.cpp b/benchmarks/src/mismatch.cpp index f6a3069ac22..c2d468fdecd 100644 --- a/benchmarks/src/mismatch.cpp +++ b/benchmarks/src/mismatch.cpp @@ -12,7 +12,12 @@ using namespace std; constexpr int64_t no_pos = -1; -template +enum class op { + mismatch, + lexi, +}; + +template void bm(benchmark::State& state) { vector a(static_cast(state.range(0)), T{'.'}); vector b(static_cast(state.range(0)), T{'.'}); @@ -22,15 +27,25 @@ void bm(benchmark::State& state) { } for (auto _ : state) { - benchmark::DoNotOptimize(ranges::mismatch(a, b)); + if constexpr (Op == op::mismatch) { + benchmark::DoNotOptimize(ranges::mismatch(a, b)); + } else if constexpr (Op == op::lexi) { + benchmark::DoNotOptimize(ranges::lexicographical_compare(a, b)); + } } } #define COMMON_ARGS Args({8, 3})->Args({24, 22})->Args({105, -1})->Args({4021, 3056}) -BENCHMARK(bm)->COMMON_ARGS; -BENCHMARK(bm)->COMMON_ARGS; -BENCHMARK(bm)->COMMON_ARGS; -BENCHMARK(bm)->COMMON_ARGS; +BENCHMARK(bm)->COMMON_ARGS; +BENCHMARK(bm)->COMMON_ARGS; +BENCHMARK(bm)->COMMON_ARGS; +BENCHMARK(bm)->COMMON_ARGS; + +BENCHMARK(bm)->COMMON_ARGS; +BENCHMARK(bm)->COMMON_ARGS; +BENCHMARK(bm)->COMMON_ARGS; +BENCHMARK(bm)->COMMON_ARGS; +BENCHMARK(bm)->COMMON_ARGS; BENCHMARK_MAIN(); diff --git a/stl/inc/algorithm b/stl/inc/algorithm index ff1f8b65e3c..f24bfc318a2 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -10868,7 +10868,8 @@ namespace ranges { using _Memcmp_classification_pred = _Lex_compare_memcmp_classify<_It1, _It2, _Pr>; constexpr bool _Is_sized1 = sized_sentinel_for<_Se1, _It1>; constexpr bool _Is_sized2 = sized_sentinel_for<_Se2, _It2>; - if constexpr (!is_void_v<_Memcmp_classification_pred> && _Sized_or_unreachable_sentinel_for<_Se1, _It1> + if constexpr (!is_void_v + && _Sized_or_unreachable_sentinel_for<_Se1, _It1> && _Sized_or_unreachable_sentinel_for<_Se2, _It2> && same_as<_Pj1, identity> && same_as<_Pj2, identity> && (_Is_sized1 || _Is_sized2)) { if (!_STD is_constant_evaluated()) { @@ -10886,8 +10887,26 @@ namespace ranges { _Num2 = SIZE_MAX; } - const int _Ans = _STD _Memcmp_count(_First1, _First2, (_STD min)(_Num1, _Num2)); - return _Memcmp_classification_pred{}(_Ans, 0) || (_Ans == 0 && _Num1 < _Num2); + const size_t _Num = (_STD min)(_Num1, _Num2); + +#if _USE_STD_VECTOR_ALGORITHMS + if constexpr (_Memcmp_classification_pred::_Opt == _Lex_cmp_opt::_Mismatch) { + const auto _First1_ptr = _STD to_address(_First1); + const auto _First2_ptr = _STD to_address(_First2); + const size_t _Pos = __std_mismatch(_First1_ptr, _First2_ptr, _Num); + if (_Pos == _Num2) { + return false; + } else if (_Pos == _Num1) { + return true; + } else { + return _STD invoke(_Pred, _First1_ptr[_Pos], _First2_ptr[_Pos]); + } + } else +#endif // _USE_STD_VECTOR_ALGORITHMS + { + const int _Ans = _STD _Memcmp_count(_First1, _First2, _Num); + return typename _Memcmp_classification_pred::_Pred{}(_Ans, 0) || (_Ans == 0 && _Num1 < _Num2); + } } } diff --git a/stl/inc/regex b/stl/inc/regex index b9956592e0e..28d713719d0 100644 --- a/stl/inc/regex +++ b/stl/inc/regex @@ -203,8 +203,9 @@ _INLINE_VAR constexpr bool _Can_memcmp_elements_with_pred<_Elem, _Elem, _Char_tr // TRANSITION: This should not be activated for user-defined specializations of char_traits template struct _Lex_compare_memcmp_classify_pred<_Elem, _Elem, _Char_traits_lt>> { - using _UElem = make_unsigned_t<_Elem>; - using _Pred = conditional_t<_Lex_compare_memcmp_classify_elements<_UElem, _UElem>, less, void>; + using _UElem = make_unsigned_t<_Elem>; + static constexpr _Lex_cmp_opt _Opt = _Lex_compare_memcmp_classify_elements<_UElem, _UElem>; + using _Pred = conditional_t<_Opt != _Lex_cmp_opt::_None, less, void>; }; template diff --git a/stl/inc/xutility b/stl/inc/xutility index f2e0482894a..594075f4255 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -5608,13 +5608,31 @@ namespace ranges { } // namespace ranges #endif // _HAS_CXX20 +enum class _Lex_cmp_opt { + _None, + _Memcmp, +#if _USE_STD_VECTOR_ALGORITHMS + _Mismatch, +#endif // _USE_STD_VECTOR_ALGORITHMS +}; + template -_INLINE_VAR constexpr bool _Lex_compare_memcmp_classify_elements = conjunction_v<_Is_character_or_bool<_Elem1>, - _Is_character_or_bool<_Elem2>, is_unsigned<_Elem1>, is_unsigned<_Elem2>>; +_INLINE_VAR constexpr _Lex_cmp_opt _Lex_compare_memcmp_classify_elements = + conjunction_v<_Is_character_or_bool<_Elem1>, _Is_character_or_bool<_Elem2>, is_unsigned<_Elem1>, + is_unsigned<_Elem2>> + ? _Lex_cmp_opt::_Memcmp +#if _USE_STD_VECTOR_ALGORITHMS + : ((is_integral_v<_Elem1> && is_integral_v<_Elem2> && sizeof(_Elem1) == sizeof(_Elem2) + && is_unsigned_v<_Elem1> == is_unsigned_v<_Elem2>) + ? _Lex_cmp_opt::_Mismatch + : _Lex_cmp_opt::_None); +#else // ^^^ _USE_STD_VECTOR_ALGORITHMS / !_USE_STD_VECTOR_ALGORITHMS vvv + : _Lex_cmp_opt::_None; +#endif // ^^^ !_USE_STD_VECTOR_ALGORITHMS ^^^ #ifdef __cpp_lib_byte template <> -inline constexpr bool _Lex_compare_memcmp_classify_elements = true; +inline constexpr _Lex_cmp_opt _Lex_compare_memcmp_classify_elements = _Lex_cmp_opt::_Memcmp; #endif // defined(__cpp_lib_byte) template @@ -5624,46 +5642,55 @@ struct _Lex_compare_memcmp_classify_pred { template struct _Lex_compare_memcmp_classify_pred<_Elem1, _Elem2, less<_Elem3>> { - using _Pred = conditional_t<_Lex_compare_memcmp_classify_elements<_Elem3, _Elem3> - && _Iter_copy_cat<_Elem1*, _Elem3*>::_Bitcopy_constructible + static constexpr _Lex_cmp_opt _Opt = _Lex_compare_memcmp_classify_elements<_Elem3, _Elem3>; + using _Pred = conditional_t<_Opt != _Lex_cmp_opt::_None && _Iter_copy_cat<_Elem1*, _Elem3*>::_Bitcopy_constructible && _Iter_copy_cat<_Elem2*, _Elem3*>::_Bitcopy_constructible, less, void>; }; template struct _Lex_compare_memcmp_classify_pred<_Elem1, _Elem2, less<>> { - using _Pred = conditional_t<_Lex_compare_memcmp_classify_elements<_Elem1, _Elem2>, less, void>; + static constexpr _Lex_cmp_opt _Opt = _Lex_compare_memcmp_classify_elements<_Elem1, _Elem2>; + using _Pred = conditional_t<_Opt != _Lex_cmp_opt::_None, less, void>; }; template struct _Lex_compare_memcmp_classify_pred<_Elem1, _Elem2, greater<_Elem3>> { - using _Pred = conditional_t<_Lex_compare_memcmp_classify_elements<_Elem3, _Elem3> - && _Iter_copy_cat<_Elem1*, _Elem3*>::_Bitcopy_constructible + static constexpr _Lex_cmp_opt _Opt = _Lex_compare_memcmp_classify_elements<_Elem3, _Elem3>; + using _Pred = conditional_t<_Opt != _Lex_cmp_opt::_None && _Iter_copy_cat<_Elem1*, _Elem3*>::_Bitcopy_constructible && _Iter_copy_cat<_Elem2*, _Elem3*>::_Bitcopy_constructible, greater, void>; }; template struct _Lex_compare_memcmp_classify_pred<_Elem1, _Elem2, greater<>> { - using _Pred = conditional_t<_Lex_compare_memcmp_classify_elements<_Elem1, _Elem2>, greater, void>; + static constexpr _Lex_cmp_opt _Opt = _Lex_compare_memcmp_classify_elements<_Elem1, _Elem2>; + using _Pred = conditional_t<_Opt != _Lex_cmp_opt::_None, greater, void>; }; #if _HAS_CXX20 template struct _Lex_compare_memcmp_classify_pred<_Elem1, _Elem2, _RANGES less> { - using _Pred = conditional_t<_Lex_compare_memcmp_classify_elements<_Elem1, _Elem2>, less, void>; + static constexpr _Lex_cmp_opt _Opt = _Lex_compare_memcmp_classify_elements<_Elem1, _Elem2>; + using _Pred = conditional_t<_Opt != _Lex_cmp_opt::_None, less, void>; }; template struct _Lex_compare_memcmp_classify_pred<_Elem1, _Elem2, _RANGES greater> { - using _Pred = conditional_t<_Lex_compare_memcmp_classify_elements<_Elem1, _Elem2>, greater, void>; + static constexpr _Lex_cmp_opt _Opt = _Lex_compare_memcmp_classify_elements<_Elem1, _Elem2>; + using _Pred = conditional_t<_Opt != _Lex_cmp_opt::_None, greater, void>; }; #endif // _HAS_CXX20 +struct _Lex_compare_memcmp_disable { + static constexpr _Lex_cmp_opt _Opt = _Lex_cmp_opt::_None; + using _Pred = void; +}; + template using _Lex_compare_memcmp_classify = conditional_t<_Iterators_are_contiguous<_It1, _It2> && !_Iterator_is_volatile<_It1> && !_Iterator_is_volatile<_It2>, - typename _Lex_compare_memcmp_classify_pred<_Iter_value_t<_It1>, _Iter_value_t<_It2>, _Pr>::_Pred, void>; + _Lex_compare_memcmp_classify_pred<_Iter_value_t<_It1>, _Iter_value_t<_It2>, _Pr>, _Lex_compare_memcmp_disable>; _EXPORT_STD template _NODISCARD _CONSTEXPR20 bool lexicographical_compare( @@ -5677,15 +5704,32 @@ _NODISCARD _CONSTEXPR20 bool lexicographical_compare( const auto _ULast2 = _STD _Get_unwrapped(_Last2); using _Memcmp_pred = _Lex_compare_memcmp_classify; - if constexpr (!is_void_v<_Memcmp_pred>) { + if constexpr (!is_void_v) { #if _HAS_CXX20 if (!_STD is_constant_evaluated()) #endif // _HAS_CXX20 { - const auto _Num1 = static_cast(_ULast1 - _UFirst1); - const auto _Num2 = static_cast(_ULast2 - _UFirst2); - const int _Ans = _STD _Memcmp_count(_UFirst1, _UFirst2, (_STD min)(_Num1, _Num2)); - return _Memcmp_pred{}(_Ans, 0) || (_Ans == 0 && _Num1 < _Num2); + const auto _Num1 = static_cast(_ULast1 - _UFirst1); + const auto _Num2 = static_cast(_ULast2 - _UFirst2); + const size_t _Num = (_STD min)(_Num1, _Num2); +#if _USE_STD_VECTOR_ALGORITHMS + if constexpr (_Memcmp_pred::_Opt == _Lex_cmp_opt::_Mismatch) { + const auto _First1_ptr = _STD _To_address(_UFirst1); + const auto _First2_ptr = _STD _To_address(_UFirst2); + const size_t _Pos = __std_mismatch(_First1_ptr, _First2_ptr, _Num); + if (_Pos == _Num2) { + return false; + } else if (_Pos == _Num1) { + return true; + } else { + return _Pred(_First1_ptr[_Pos], _First2_ptr[_Pos]); + } + } else +#endif // _USE_STD_VECTOR_ALGORITHMS + { + const int _Ans = _STD _Memcmp_count(_UFirst1, _UFirst2, _Num); + return typename _Memcmp_pred::_Pred{}(_Ans, 0) || (_Ans == 0 && _Num1 < _Num2); + } } } @@ -5737,37 +5781,42 @@ struct _Lex_compare_three_way_memcmp_classify_comp { template struct _Lex_compare_three_way_memcmp_classify_comp<_Elem1, _Elem2, compare_three_way> { - using _Comp = conditional_t<_Lex_compare_memcmp_classify_elements<_Elem1, _Elem2> - && three_way_comparable_with, + static constexpr _Lex_cmp_opt _Opt = _Lex_compare_memcmp_classify_elements<_Elem1, _Elem2>; + using _Comp = conditional_t<_Opt != _Lex_cmp_opt::_None && three_way_comparable_with, compare_three_way, void>; }; template struct _Lex_compare_three_way_memcmp_classify_comp<_Elem1, _Elem2, _Strong_order::_Cpo> { + static constexpr _Lex_cmp_opt _Opt = _Lex_compare_memcmp_classify_elements<_Elem1, _Elem2>; using _Comp = - conditional_t<_Lex_compare_memcmp_classify_elements<_Elem1, _Elem2> && _Can_strong_order<_Elem1, _Elem2>, - _Strong_order::_Cpo, void>; + conditional_t<_Opt != _Lex_cmp_opt::_None && _Can_strong_order<_Elem1, _Elem2>, _Strong_order::_Cpo, void>; }; template struct _Lex_compare_three_way_memcmp_classify_comp<_Elem1, _Elem2, _Weak_order::_Cpo> { + static constexpr _Lex_cmp_opt _Opt = _Lex_compare_memcmp_classify_elements<_Elem1, _Elem2>; using _Comp = - conditional_t<_Lex_compare_memcmp_classify_elements<_Elem1, _Elem2> && _Can_weak_order<_Elem1, _Elem2>, - _Weak_order::_Cpo, void>; + conditional_t<_Opt != _Lex_cmp_opt::_None && _Can_weak_order<_Elem1, _Elem2>, _Weak_order::_Cpo, void>; }; template struct _Lex_compare_three_way_memcmp_classify_comp<_Elem1, _Elem2, _Partial_order::_Cpo> { + static constexpr _Lex_cmp_opt _Opt = _Lex_compare_memcmp_classify_elements<_Elem1, _Elem2>; using _Comp = - conditional_t<_Lex_compare_memcmp_classify_elements<_Elem1, _Elem2> && _Can_partial_order<_Elem1, _Elem2>, - _Partial_order::_Cpo, void>; + conditional_t<_Opt != _Lex_cmp_opt::_None && _Can_partial_order<_Elem1, _Elem2>, _Partial_order::_Cpo, void>; +}; + +struct _Lex_compare_three_way_memcmp_disable { + static constexpr _Lex_cmp_opt _Opt = _Lex_cmp_opt::_None; + using _Comp = void; }; template using _Lex_compare_three_way_memcmp_classify = conditional_t<_Iterators_are_contiguous<_It1, _It2> && !_Iterator_is_volatile<_It1> && !_Iterator_is_volatile<_It2>, - typename _Lex_compare_three_way_memcmp_classify_comp<_Iter_value_t<_It1>, _Iter_value_t<_It2>, _Cmp>::_Comp, - void>; + _Lex_compare_three_way_memcmp_classify_comp<_Iter_value_t<_It1>, _Iter_value_t<_It2>, _Cmp>, + _Lex_compare_three_way_memcmp_disable>; _EXPORT_STD template _NODISCARD constexpr auto lexicographical_compare_three_way(const _InIt1 _First1, const _InIt1 _Last1, @@ -5780,15 +5829,33 @@ _NODISCARD constexpr auto lexicographical_compare_three_way(const _InIt1 _First1 const auto _ULast2 = _STD _Get_unwrapped(_Last2); using _Memcmp_pred = _Lex_compare_three_way_memcmp_classify; - if constexpr (!is_void_v<_Memcmp_pred>) { + if constexpr (!is_void_v) { if (!_STD is_constant_evaluated()) { const auto _Num1 = static_cast(_ULast1 - _UFirst1); const auto _Num2 = static_cast(_ULast2 - _UFirst2); - const int _Ans = _STD _Memcmp_count(_UFirst1, _UFirst2, (_STD min)(_Num1, _Num2)); - if (_Ans == 0) { - return _Num1 <=> _Num2; - } else { - return _Memcmp_pred{}(_Ans, 0); + const size_t _Num = (_STD min)(_Num1, _Num2); + +#if _USE_STD_VECTOR_ALGORITHMS + if constexpr (_Memcmp_pred::_Opt == _Lex_cmp_opt::_Mismatch) { + const auto _First1_ptr = _STD to_address(_UFirst1); + const auto _First2_ptr = _STD to_address(_UFirst2); + const size_t _Pos = __std_mismatch(_First1_ptr, _First2_ptr, _Num); + if (_Pos == _Num1) { + return _Pos == _Num2 ? strong_ordering::equal : strong_ordering::less; + } else if (_Pos == _Num2) { + return strong_ordering::greater; + } else { + return _Comp(_First1_ptr[_Pos], _First2_ptr[_Pos]); + } + } else +#endif // _USE_STD_VECTOR_ALGORITHMS + { + const int _Ans = _STD _Memcmp_count(_UFirst1, _UFirst2, _Num); + if (_Ans == 0) { + return _Num1 <=> _Num2; + } else { + return typename _Memcmp_pred::_Comp{}(_Ans, 0); + } } } } diff --git a/tests/std/tests/GH_000431_lex_compare_memcmp_classify/env.lst b/tests/std/tests/GH_000431_lex_compare_memcmp_classify/env.lst index 6ccc73d16ea..eeb6bd1d9a4 100644 --- a/tests/std/tests/GH_000431_lex_compare_memcmp_classify/env.lst +++ b/tests/std/tests/GH_000431_lex_compare_memcmp_classify/env.lst @@ -2,3 +2,6 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception RUNALL_INCLUDE ..\char8_t_matrix.lst +RUNALL_CROSSLIST +* PM_CL="" # Test memcmp and manual vectorization +* PM_CL="/D_USE_STD_VECTOR_ALGORITHMS=0" # Test memcmp only \ No newline at end of file diff --git a/tests/std/tests/GH_000431_lex_compare_memcmp_classify/test.compile.pass.cpp b/tests/std/tests/GH_000431_lex_compare_memcmp_classify/test.compile.pass.cpp index 94d1542a686..7b9088b9010 100644 --- a/tests/std/tests/GH_000431_lex_compare_memcmp_classify/test.compile.pass.cpp +++ b/tests/std/tests/GH_000431_lex_compare_memcmp_classify/test.compile.pass.cpp @@ -22,13 +22,13 @@ using namespace std; template void assert_lex_compare_memcmp_classify() { - STATIC_ASSERT(is_same_v<_Lex_compare_memcmp_classify, Expected>); + STATIC_ASSERT(is_same_v::_Pred, Expected>); } #if _HAS_CXX20 template void assert_lex_compare_three_way_memcmp_classify() { - STATIC_ASSERT(is_same_v<_Lex_compare_three_way_memcmp_classify, Expected>); + STATIC_ASSERT(is_same_v::_Comp, Expected>); } #endif // _HAS_CXX20 @@ -130,8 +130,10 @@ void test_lex_compare_memcmp_classify_for_types() { test_lex_compare_memcmp_classify_for_pred>(); test_lex_compare_memcmp_classify_for_pred>(); - test_lex_compare_memcmp_classify_for_pred>(); - test_lex_compare_memcmp_classify_for_pred>(); + using bigger_type = conditional_t; + + test_lex_compare_memcmp_classify_for_pred>(); + test_lex_compare_memcmp_classify_for_pred>(); test_lex_compare_memcmp_classify_for_pred>(); test_lex_compare_memcmp_classify_for_pred>(); @@ -176,13 +178,15 @@ void test_lex_compare_memcmp_classify_for_types() { #endif // _HAS_CXX20 } +constexpr bool vec_alg = _USE_STD_VECTOR_ALGORITHMS; + template void test_lex_compare_memcmp_classify_for_1byte_integrals() { test_lex_compare_memcmp_classify_for_types(); - test_lex_compare_memcmp_classify_for_opaque_preds, Type1, Type2, char>(); + test_lex_compare_memcmp_classify_for_opaque_preds || vec_alg, Type1, Type2, char>(); test_lex_compare_memcmp_classify_for_opaque_preds(); - test_lex_compare_memcmp_classify_for_opaque_preds(); + test_lex_compare_memcmp_classify_for_opaque_preds(); #ifdef __cpp_lib_char8_t test_lex_compare_memcmp_classify_for_opaque_preds(); #endif // __cpp_lib_char8_t @@ -225,13 +229,13 @@ bool operator<(const user_struct&, const user_struct&) { void lex_compare_memcmp_classify_test_cases() { // Allow unsigned 1 byte integrals - test_lex_compare_memcmp_classify_for_1byte_integrals, char, char>(); + test_lex_compare_memcmp_classify_for_1byte_integrals || vec_alg, char, char>(); test_lex_compare_memcmp_classify_for_1byte_integrals, unsigned char, char>(); test_lex_compare_memcmp_classify_for_1byte_integrals, char, unsigned char>(); test_lex_compare_memcmp_classify_for_1byte_integrals(); - test_lex_compare_memcmp_classify_for_1byte_integrals(); - test_lex_compare_memcmp_classify_for_1byte_integrals(); - test_lex_compare_memcmp_classify_for_1byte_integrals(); + test_lex_compare_memcmp_classify_for_1byte_integrals(); + test_lex_compare_memcmp_classify_for_1byte_integrals, char, signed char>(); + test_lex_compare_memcmp_classify_for_1byte_integrals, signed char, char>(); test_lex_compare_memcmp_classify_for_1byte_integrals(); test_lex_compare_memcmp_classify_for_1byte_integrals(); #ifdef __cpp_lib_char8_t @@ -252,10 +256,6 @@ void lex_compare_memcmp_classify_test_cases() { test_lex_compare_memcmp_classify_for_1byte_integrals(); test_lex_compare_memcmp_classify_for_1byte_integrals(); #endif // __cpp_lib_char8_t - test_lex_compare_memcmp_classify_for_1byte_integrals(); - test_lex_compare_memcmp_classify_for_1byte_integrals(); - test_lex_compare_memcmp_classify_for_1byte_integrals(); - test_lex_compare_memcmp_classify_for_1byte_integrals(); // Don't allow enums test_lex_compare_memcmp_classify_for_types(); @@ -281,10 +281,10 @@ void lex_compare_memcmp_classify_test_cases() { // Don't allow bigger integrals test_lex_compare_memcmp_classify_for_types(); test_lex_compare_memcmp_classify_for_types(); - test_lex_compare_memcmp_classify_for_types(); - test_lex_compare_memcmp_classify_for_types(); - test_lex_compare_memcmp_classify_for_types(); - test_lex_compare_memcmp_classify_for_types(); + test_lex_compare_memcmp_classify_for_types(); + test_lex_compare_memcmp_classify_for_types(); + test_lex_compare_memcmp_classify_for_types(); + test_lex_compare_memcmp_classify_for_types(); // Don't allow pointers test_lex_compare_memcmp_classify_for_types(); @@ -298,9 +298,11 @@ void lex_compare_memcmp_classify_test_cases() { test_lex_compare_memcmp_classify_for_pred, char8_t, char8_t, _Char_traits_lt>>(); #endif // __cpp_lib_char8_t - test_lex_compare_memcmp_classify_for_pred>>(); - test_lex_compare_memcmp_classify_for_pred>>(); - test_lex_compare_memcmp_classify_for_pred>>(); + using vless = conditional_t, void>; + + test_lex_compare_memcmp_classify_for_pred>>(); + test_lex_compare_memcmp_classify_for_pred>>(); + test_lex_compare_memcmp_classify_for_pred>>(); // Test different containers #if _HAS_CXX20 diff --git a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp index e263e59d628..9e07d923513 100644 --- a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp +++ b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -388,20 +389,64 @@ auto last_known_good_mismatch(FwdIt first1, FwdIt last1, FwdIt first2, FwdIt las return make_pair(first1, first2); } +template +bool last_known_good_lex_compare(FwdIt first1, FwdIt last1, FwdIt first2, FwdIt last2) { + for (;; ++first1, ++first2) { + if (first2 == last2) { + return false; + } else if (first1 == last1) { + return true; + } else if (*first1 < *first2) { + return true; + } else if (*first2 < *first1) { + return false; + } + } +} + +template +auto last_known_good_lex_compare_3way(FwdIt first1, FwdIt last1, FwdIt first2, FwdIt last2) { + for (;; ++first1, ++first2) { + if (first2 == last2) { + if (first1 == last1) { + return strong_ordering::equal; + } else { + return strong_ordering::greater; + } + } else if (first1 == last1) { + return strong_ordering::less; + } else { + auto order = *first1 <=> *first2; + if (order != 0) { + return order; + } + } + } +} + template -void test_case_mismatch(const vector& a, const vector& b) { - auto expected = last_known_good_mismatch(a.begin(), a.end(), b.begin(), b.end()); - auto actual = mismatch(a.begin(), a.end(), b.begin(), b.end()); - assert(expected == actual); +void test_case_mismatch_and_lex_compare_family(const vector& a, const vector& b) { + auto expected_mismatch = last_known_good_mismatch(a.begin(), a.end(), b.begin(), b.end()); + auto actual_mismatch = mismatch(a.begin(), a.end(), b.begin(), b.end()); + assert(expected_mismatch == actual_mismatch); + + auto expected_lex = last_known_good_lex_compare(a.begin(), a.end(), b.begin(), b.end()); + auto actual_lex = lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); + assert(expected_lex == actual_lex); + #if _HAS_CXX20 - auto ranges_actual = ranges::mismatch(a, b); - assert(get<0>(expected) == ranges_actual.in1); - assert(get<1>(expected) == ranges_actual.in2); + auto ranges_actual_mismatch = ranges::mismatch(a, b); + assert(get<0>(expected_mismatch) == ranges_actual_mismatch.in1); + assert(get<1>(expected_mismatch) == ranges_actual_mismatch.in2); + + auto expected_lex_3way = last_known_good_lex_compare_3way(a.begin(), a.end(), b.begin(), b.end()); + auto actual_lex_3way = lexicographical_compare_three_way(a.begin(), a.end(), b.begin(), b.end()); + assert(expected_lex_3way == actual_lex_3way); #endif // _HAS_CXX20 } template -void test_mismatch(mt19937_64& gen) { +void test_mismatch_and_lex_compare_family(mt19937_64& gen) { constexpr size_t shrinkCount = 4; constexpr size_t mismatchCount = 30; using TD = conditional_t; @@ -413,13 +458,13 @@ void test_mismatch(mt19937_64& gen) { for (;;) { // equal - test_case_mismatch(input_a, input_b); + test_case_mismatch_and_lex_compare_family(input_a, input_b); // different sizes for (size_t i = 0; i != shrinkCount && !input_b.empty(); ++i) { - test_case_mismatch(input_a, input_b); - test_case_mismatch(input_b, input_a); input_b.pop_back(); + test_case_mismatch_and_lex_compare_family(input_a, input_b); + test_case_mismatch_and_lex_compare_family(input_b, input_a); } // actual mismatch (or maybe not, depending on random) @@ -429,8 +474,8 @@ void test_mismatch(mt19937_64& gen) { for (size_t attempts = 0; attempts < mismatchCount; ++attempts) { const size_t possible_mismatch_pos = mismatch_dis(gen); input_a[possible_mismatch_pos] = static_cast(dis(gen)); - test_case_mismatch(input_a, input_b); - test_case_mismatch(input_b, input_a); + test_case_mismatch_and_lex_compare_family(input_a, input_b); + test_case_mismatch_and_lex_compare_family(input_b, input_a); } } @@ -444,19 +489,27 @@ void test_mismatch(mt19937_64& gen) { } template -void test_mismatch_containers() { +void test_mismatch_and_lex_compare_family_containers() { C1 a{'m', 'e', 'o', 'w', ' ', 'C', 'A', 'T', 'S'}; C2 b{'m', 'e', 'o', 'w', ' ', 'K', 'I', 'T', 'T', 'E', 'N', 'S'}; - const auto result_4 = mismatch(a.begin(), a.end(), b.begin(), b.end()); - const auto result_3 = mismatch(a.begin(), a.end(), b.begin()); - assert(get<0>(result_4) == a.begin() + 5); - assert(get<1>(result_4) == b.begin() + 5); - assert(get<0>(result_3) == a.begin() + 5); - assert(get<1>(result_3) == b.begin() + 5); + + const auto result_mismatch_4 = mismatch(a.begin(), a.end(), b.begin(), b.end()); + const auto result_mismatch_3 = mismatch(a.begin(), a.end(), b.begin()); + assert(get<0>(result_mismatch_4) == a.begin() + 5); + assert(get<1>(result_mismatch_4) == b.begin() + 5); + assert(get<0>(result_mismatch_3) == a.begin() + 5); + assert(get<1>(result_mismatch_3) == b.begin() + 5); + + const auto result_lex = lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); + assert(result_lex == true); + #if _HAS_CXX20 - const auto result_r = ranges::mismatch(a, b); - assert(result_r.in1 == a.begin() + 5); - assert(result_r.in2 == b.begin() + 5); + const auto result_mismatch_r = ranges::mismatch(a, b); + assert(result_mismatch_r.in1 == a.begin() + 5); + assert(result_mismatch_r.in2 == b.begin() + 5); + + const auto result_lex_3way = lexicographical_compare_three_way(a.begin(), a.end(), b.begin(), b.end()); + assert(result_lex_3way == strong_ordering::less); #endif // _HAS_CXX20 } @@ -705,23 +758,23 @@ void test_vector_algorithms(mt19937_64& gen) { test_case_min_max_element( vector{-6604286336755016904, -4365366089374418225, 6104371530830675888, -8582621853879131834}); - test_mismatch(gen); - test_mismatch(gen); - test_mismatch(gen); - test_mismatch(gen); - test_mismatch(gen); - test_mismatch(gen); - test_mismatch(gen); - test_mismatch(gen); - test_mismatch(gen); - - test_mismatch_containers, vector>(); - test_mismatch_containers, vector>(); - test_mismatch_containers, vector>(); - test_mismatch_containers, const vector>(); - test_mismatch_containers, const vector>(); - test_mismatch_containers, vector>(); - test_mismatch_containers, vector>(); + test_mismatch_and_lex_compare_family(gen); + test_mismatch_and_lex_compare_family(gen); + test_mismatch_and_lex_compare_family(gen); + test_mismatch_and_lex_compare_family(gen); + test_mismatch_and_lex_compare_family(gen); + test_mismatch_and_lex_compare_family(gen); + test_mismatch_and_lex_compare_family(gen); + test_mismatch_and_lex_compare_family(gen); + test_mismatch_and_lex_compare_family(gen); + + test_mismatch_and_lex_compare_family_containers, vector>(); + test_mismatch_and_lex_compare_family_containers, vector>(); + test_mismatch_and_lex_compare_family_containers, vector>(); + test_mismatch_and_lex_compare_family_containers, const vector>(); + test_mismatch_and_lex_compare_family_containers, const vector>(); + test_mismatch_and_lex_compare_family_containers, vector>(); + test_mismatch_and_lex_compare_family_containers, vector>(); test_mismatch_sizes_and_alignments::test(); test_mismatch_sizes_and_alignments::test(); From 71f3c501beca62c2f0e23e9c08ec1271ec97bc7c Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Mon, 1 Apr 2024 23:05:02 +0300 Subject: [PATCH 02/12] format --- stl/inc/xutility | 6 +++--- .../std/tests/GH_000431_lex_compare_memcmp_classify/env.lst | 2 +- tests/std/tests/VSO_0000000_vector_algorithms/test.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index 594075f4255..0ccd5a5bc89 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -5651,7 +5651,7 @@ struct _Lex_compare_memcmp_classify_pred<_Elem1, _Elem2, less<_Elem3>> { template struct _Lex_compare_memcmp_classify_pred<_Elem1, _Elem2, less<>> { static constexpr _Lex_cmp_opt _Opt = _Lex_compare_memcmp_classify_elements<_Elem1, _Elem2>; - using _Pred = conditional_t<_Opt != _Lex_cmp_opt::_None, less, void>; + using _Pred = conditional_t<_Opt != _Lex_cmp_opt::_None, less, void>; }; template @@ -5831,8 +5831,8 @@ _NODISCARD constexpr auto lexicographical_compare_three_way(const _InIt1 _First1 using _Memcmp_pred = _Lex_compare_three_way_memcmp_classify; if constexpr (!is_void_v) { if (!_STD is_constant_evaluated()) { - const auto _Num1 = static_cast(_ULast1 - _UFirst1); - const auto _Num2 = static_cast(_ULast2 - _UFirst2); + const auto _Num1 = static_cast(_ULast1 - _UFirst1); + const auto _Num2 = static_cast(_ULast2 - _UFirst2); const size_t _Num = (_STD min)(_Num1, _Num2); #if _USE_STD_VECTOR_ALGORITHMS diff --git a/tests/std/tests/GH_000431_lex_compare_memcmp_classify/env.lst b/tests/std/tests/GH_000431_lex_compare_memcmp_classify/env.lst index eeb6bd1d9a4..e39b826ac9c 100644 --- a/tests/std/tests/GH_000431_lex_compare_memcmp_classify/env.lst +++ b/tests/std/tests/GH_000431_lex_compare_memcmp_classify/env.lst @@ -4,4 +4,4 @@ RUNALL_INCLUDE ..\char8_t_matrix.lst RUNALL_CROSSLIST * PM_CL="" # Test memcmp and manual vectorization -* PM_CL="/D_USE_STD_VECTOR_ALGORITHMS=0" # Test memcmp only \ No newline at end of file +* PM_CL="/D_USE_STD_VECTOR_ALGORITHMS=0" # Test memcmp only diff --git a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp index 9e07d923513..64213a1c983 100644 --- a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp +++ b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp @@ -429,7 +429,7 @@ void test_case_mismatch_and_lex_compare_family(const vector& a, const vector< auto expected_mismatch = last_known_good_mismatch(a.begin(), a.end(), b.begin(), b.end()); auto actual_mismatch = mismatch(a.begin(), a.end(), b.begin(), b.end()); assert(expected_mismatch == actual_mismatch); - + auto expected_lex = last_known_good_lex_compare(a.begin(), a.end(), b.begin(), b.end()); auto actual_lex = lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); assert(expected_lex == actual_lex); @@ -492,7 +492,7 @@ template void test_mismatch_and_lex_compare_family_containers() { C1 a{'m', 'e', 'o', 'w', ' ', 'C', 'A', 'T', 'S'}; C2 b{'m', 'e', 'o', 'w', ' ', 'K', 'I', 'T', 'T', 'E', 'N', 'S'}; - + const auto result_mismatch_4 = mismatch(a.begin(), a.end(), b.begin(), b.end()); const auto result_mismatch_3 = mismatch(a.begin(), a.end(), b.begin()); assert(get<0>(result_mismatch_4) == a.begin() + 5); From ea8f20e123d35dcb58b087e908fba3542718b2ba Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Mon, 1 Apr 2024 23:34:56 +0300 Subject: [PATCH 03/12] hide C++20 --- tests/std/tests/VSO_0000000_vector_algorithms/test.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp index 64213a1c983..a5bea4c4602 100644 --- a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp +++ b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include @@ -21,8 +20,9 @@ #include #if _HAS_CXX20 +#include #include -#endif +#endif // _HAS_CXX20 #include "test_min_max_element_support.hpp" @@ -404,6 +404,7 @@ bool last_known_good_lex_compare(FwdIt first1, FwdIt last1, FwdIt first2, FwdIt } } +#if _HAS_CXX20 template auto last_known_good_lex_compare_3way(FwdIt first1, FwdIt last1, FwdIt first2, FwdIt last2) { for (;; ++first1, ++first2) { @@ -423,6 +424,7 @@ auto last_known_good_lex_compare_3way(FwdIt first1, FwdIt last1, FwdIt first2, F } } } +#endif // _HAS_CXX20 template void test_case_mismatch_and_lex_compare_family(const vector& a, const vector& b) { From a82b9d6fcf664fa0b2bf68e443e900161a630446 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Tue, 2 Apr 2024 08:52:53 +0300 Subject: [PATCH 04/12] get em back --- .../test.compile.pass.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/std/tests/GH_000431_lex_compare_memcmp_classify/test.compile.pass.cpp b/tests/std/tests/GH_000431_lex_compare_memcmp_classify/test.compile.pass.cpp index 7b9088b9010..04371cb74f6 100644 --- a/tests/std/tests/GH_000431_lex_compare_memcmp_classify/test.compile.pass.cpp +++ b/tests/std/tests/GH_000431_lex_compare_memcmp_classify/test.compile.pass.cpp @@ -256,6 +256,10 @@ void lex_compare_memcmp_classify_test_cases() { test_lex_compare_memcmp_classify_for_1byte_integrals(); test_lex_compare_memcmp_classify_for_1byte_integrals(); #endif // __cpp_lib_char8_t + test_lex_compare_memcmp_classify_for_1byte_integrals, char, bool>(); + test_lex_compare_memcmp_classify_for_1byte_integrals, bool, char>(); + test_lex_compare_memcmp_classify_for_1byte_integrals(); + test_lex_compare_memcmp_classify_for_1byte_integrals(); // Don't allow enums test_lex_compare_memcmp_classify_for_types(); From fb8f5bca0cbb6f1eee4413b9ee765e780efd95d8 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Mon, 8 Apr 2024 07:06:02 +0300 Subject: [PATCH 05/12] reduce invasiveness --- stl/inc/algorithm | 33 ++-- stl/inc/regex | 5 +- stl/inc/xutility | 147 +++++++----------- .../test.compile.pass.cpp | 4 +- 4 files changed, 75 insertions(+), 114 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index f24bfc318a2..39c64a34828 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -10868,8 +10868,7 @@ namespace ranges { using _Memcmp_classification_pred = _Lex_compare_memcmp_classify<_It1, _It2, _Pr>; constexpr bool _Is_sized1 = sized_sentinel_for<_Se1, _It1>; constexpr bool _Is_sized2 = sized_sentinel_for<_Se2, _It2>; - if constexpr (!is_void_v - && _Sized_or_unreachable_sentinel_for<_Se1, _It1> + if constexpr (!is_void_v<_Memcmp_classification_pred> && _Sized_or_unreachable_sentinel_for<_Se1, _It1> && _Sized_or_unreachable_sentinel_for<_Se2, _It2> && same_as<_Pj1, identity> && same_as<_Pj2, identity> && (_Is_sized1 || _Is_sized2)) { if (!_STD is_constant_evaluated()) { @@ -10888,25 +10887,21 @@ namespace ranges { } const size_t _Num = (_STD min)(_Num1, _Num2); - #if _USE_STD_VECTOR_ALGORITHMS - if constexpr (_Memcmp_classification_pred::_Opt == _Lex_cmp_opt::_Mismatch) { - const auto _First1_ptr = _STD to_address(_First1); - const auto _First2_ptr = _STD to_address(_First2); - const size_t _Pos = __std_mismatch(_First1_ptr, _First2_ptr, _Num); - if (_Pos == _Num2) { - return false; - } else if (_Pos == _Num1) { - return true; - } else { - return _STD invoke(_Pred, _First1_ptr[_Pos], _First2_ptr[_Pos]); - } - } else -#endif // _USE_STD_VECTOR_ALGORITHMS - { - const int _Ans = _STD _Memcmp_count(_First1, _First2, _Num); - return typename _Memcmp_classification_pred::_Pred{}(_Ans, 0) || (_Ans == 0 && _Num1 < _Num2); + const auto _First1_ptr = _STD to_address(_First1); + const auto _First2_ptr = _STD to_address(_First2); + const size_t _Pos = __std_mismatch(_First1_ptr, _First2_ptr, _Num); + if (_Pos == _Num2) { + return false; + } else if (_Pos == _Num1) { + return true; + } else { + return _STD invoke(_Pred, _First1_ptr[_Pos], _First2_ptr[_Pos]); } +#else // ^^^ _USE_STD_VECTOR_ALGORITHMS / !_USE_STD_VECTOR_ALGORITHMS vvv + const int _Ans = _STD _Memcmp_count(_First1, _First2, _Num); + return _Memcmp_classification_pred{}(_Ans, 0) || (_Ans == 0 && _Num1 < _Num2); +#endif // ^^^ !_USE_STD_VECTOR_ALGORITHMS ^^^ } } diff --git a/stl/inc/regex b/stl/inc/regex index 28d713719d0..b9956592e0e 100644 --- a/stl/inc/regex +++ b/stl/inc/regex @@ -203,9 +203,8 @@ _INLINE_VAR constexpr bool _Can_memcmp_elements_with_pred<_Elem, _Elem, _Char_tr // TRANSITION: This should not be activated for user-defined specializations of char_traits template struct _Lex_compare_memcmp_classify_pred<_Elem, _Elem, _Char_traits_lt>> { - using _UElem = make_unsigned_t<_Elem>; - static constexpr _Lex_cmp_opt _Opt = _Lex_compare_memcmp_classify_elements<_UElem, _UElem>; - using _Pred = conditional_t<_Opt != _Lex_cmp_opt::_None, less, void>; + using _UElem = make_unsigned_t<_Elem>; + using _Pred = conditional_t<_Lex_compare_memcmp_classify_elements<_UElem, _UElem>, less, void>; }; template diff --git a/stl/inc/xutility b/stl/inc/xutility index 0ccd5a5bc89..d4cfda7622d 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -5608,31 +5608,19 @@ namespace ranges { } // namespace ranges #endif // _HAS_CXX20 -enum class _Lex_cmp_opt { - _None, - _Memcmp, -#if _USE_STD_VECTOR_ALGORITHMS - _Mismatch, -#endif // _USE_STD_VECTOR_ALGORITHMS -}; - template -_INLINE_VAR constexpr _Lex_cmp_opt _Lex_compare_memcmp_classify_elements = - conjunction_v<_Is_character_or_bool<_Elem1>, _Is_character_or_bool<_Elem2>, is_unsigned<_Elem1>, - is_unsigned<_Elem2>> - ? _Lex_cmp_opt::_Memcmp +constexpr bool _Lex_compare_memcmp_classify_elements = #if _USE_STD_VECTOR_ALGORITHMS - : ((is_integral_v<_Elem1> && is_integral_v<_Elem2> && sizeof(_Elem1) == sizeof(_Elem2) - && is_unsigned_v<_Elem1> == is_unsigned_v<_Elem2>) - ? _Lex_cmp_opt::_Mismatch - : _Lex_cmp_opt::_None); + is_integral_v<_Elem1> && is_integral_v<_Elem2> && sizeof(_Elem1) == sizeof(_Elem2) + && is_unsigned_v<_Elem1> == is_unsigned_v<_Elem2>; #else // ^^^ _USE_STD_VECTOR_ALGORITHMS / !_USE_STD_VECTOR_ALGORITHMS vvv - : _Lex_cmp_opt::_None; + conjunction_v<_Is_character_or_bool<_Elem1>, _Is_character_or_bool<_Elem2>, is_unsigned<_Elem1>, + is_unsigned<_Elem2>>; #endif // ^^^ !_USE_STD_VECTOR_ALGORITHMS ^^^ #ifdef __cpp_lib_byte template <> -inline constexpr _Lex_cmp_opt _Lex_compare_memcmp_classify_elements = _Lex_cmp_opt::_Memcmp; +inline constexpr bool _Lex_compare_memcmp_classify_elements = true; #endif // defined(__cpp_lib_byte) template @@ -5642,55 +5630,46 @@ struct _Lex_compare_memcmp_classify_pred { template struct _Lex_compare_memcmp_classify_pred<_Elem1, _Elem2, less<_Elem3>> { - static constexpr _Lex_cmp_opt _Opt = _Lex_compare_memcmp_classify_elements<_Elem3, _Elem3>; - using _Pred = conditional_t<_Opt != _Lex_cmp_opt::_None && _Iter_copy_cat<_Elem1*, _Elem3*>::_Bitcopy_constructible + using _Pred = conditional_t<_Lex_compare_memcmp_classify_elements<_Elem3, _Elem3> + && _Iter_copy_cat<_Elem1*, _Elem3*>::_Bitcopy_constructible && _Iter_copy_cat<_Elem2*, _Elem3*>::_Bitcopy_constructible, less, void>; }; template struct _Lex_compare_memcmp_classify_pred<_Elem1, _Elem2, less<>> { - static constexpr _Lex_cmp_opt _Opt = _Lex_compare_memcmp_classify_elements<_Elem1, _Elem2>; - using _Pred = conditional_t<_Opt != _Lex_cmp_opt::_None, less, void>; + using _Pred = conditional_t<_Lex_compare_memcmp_classify_elements<_Elem1, _Elem2>, less, void>; }; template struct _Lex_compare_memcmp_classify_pred<_Elem1, _Elem2, greater<_Elem3>> { - static constexpr _Lex_cmp_opt _Opt = _Lex_compare_memcmp_classify_elements<_Elem3, _Elem3>; - using _Pred = conditional_t<_Opt != _Lex_cmp_opt::_None && _Iter_copy_cat<_Elem1*, _Elem3*>::_Bitcopy_constructible + using _Pred = conditional_t<_Lex_compare_memcmp_classify_elements<_Elem3, _Elem3> + && _Iter_copy_cat<_Elem1*, _Elem3*>::_Bitcopy_constructible && _Iter_copy_cat<_Elem2*, _Elem3*>::_Bitcopy_constructible, greater, void>; }; template struct _Lex_compare_memcmp_classify_pred<_Elem1, _Elem2, greater<>> { - static constexpr _Lex_cmp_opt _Opt = _Lex_compare_memcmp_classify_elements<_Elem1, _Elem2>; - using _Pred = conditional_t<_Opt != _Lex_cmp_opt::_None, greater, void>; + using _Pred = conditional_t<_Lex_compare_memcmp_classify_elements<_Elem1, _Elem2>, greater, void>; }; #if _HAS_CXX20 template struct _Lex_compare_memcmp_classify_pred<_Elem1, _Elem2, _RANGES less> { - static constexpr _Lex_cmp_opt _Opt = _Lex_compare_memcmp_classify_elements<_Elem1, _Elem2>; - using _Pred = conditional_t<_Opt != _Lex_cmp_opt::_None, less, void>; + using _Pred = conditional_t<_Lex_compare_memcmp_classify_elements<_Elem1, _Elem2>, less, void>; }; template struct _Lex_compare_memcmp_classify_pred<_Elem1, _Elem2, _RANGES greater> { - static constexpr _Lex_cmp_opt _Opt = _Lex_compare_memcmp_classify_elements<_Elem1, _Elem2>; - using _Pred = conditional_t<_Opt != _Lex_cmp_opt::_None, greater, void>; + using _Pred = conditional_t<_Lex_compare_memcmp_classify_elements<_Elem1, _Elem2>, greater, void>; }; #endif // _HAS_CXX20 -struct _Lex_compare_memcmp_disable { - static constexpr _Lex_cmp_opt _Opt = _Lex_cmp_opt::_None; - using _Pred = void; -}; - template using _Lex_compare_memcmp_classify = conditional_t<_Iterators_are_contiguous<_It1, _It2> && !_Iterator_is_volatile<_It1> && !_Iterator_is_volatile<_It2>, - _Lex_compare_memcmp_classify_pred<_Iter_value_t<_It1>, _Iter_value_t<_It2>, _Pr>, _Lex_compare_memcmp_disable>; + typename _Lex_compare_memcmp_classify_pred<_Iter_value_t<_It1>, _Iter_value_t<_It2>, _Pr>::_Pred, void>; _EXPORT_STD template _NODISCARD _CONSTEXPR20 bool lexicographical_compare( @@ -5704,7 +5683,7 @@ _NODISCARD _CONSTEXPR20 bool lexicographical_compare( const auto _ULast2 = _STD _Get_unwrapped(_Last2); using _Memcmp_pred = _Lex_compare_memcmp_classify; - if constexpr (!is_void_v) { + if constexpr (!is_void_v<_Memcmp_pred>) { #if _HAS_CXX20 if (!_STD is_constant_evaluated()) #endif // _HAS_CXX20 @@ -5713,23 +5692,20 @@ _NODISCARD _CONSTEXPR20 bool lexicographical_compare( const auto _Num2 = static_cast(_ULast2 - _UFirst2); const size_t _Num = (_STD min)(_Num1, _Num2); #if _USE_STD_VECTOR_ALGORITHMS - if constexpr (_Memcmp_pred::_Opt == _Lex_cmp_opt::_Mismatch) { - const auto _First1_ptr = _STD _To_address(_UFirst1); - const auto _First2_ptr = _STD _To_address(_UFirst2); - const size_t _Pos = __std_mismatch(_First1_ptr, _First2_ptr, _Num); - if (_Pos == _Num2) { - return false; - } else if (_Pos == _Num1) { - return true; - } else { - return _Pred(_First1_ptr[_Pos], _First2_ptr[_Pos]); - } - } else -#endif // _USE_STD_VECTOR_ALGORITHMS - { - const int _Ans = _STD _Memcmp_count(_UFirst1, _UFirst2, _Num); - return typename _Memcmp_pred::_Pred{}(_Ans, 0) || (_Ans == 0 && _Num1 < _Num2); + const auto _First1_ptr = _STD _To_address(_UFirst1); + const auto _First2_ptr = _STD _To_address(_UFirst2); + const size_t _Pos = __std_mismatch(_First1_ptr, _First2_ptr, _Num); + if (_Pos == _Num2) { + return false; + } else if (_Pos == _Num1) { + return true; + } else { + return _Pred(_First1_ptr[_Pos], _First2_ptr[_Pos]); } +#else // ^^^ _USE_STD_VECTOR_ALGORITHMS / !_USE_STD_VECTOR_ALGORITHMS vvv + const int _Ans = _STD _Memcmp_count(_UFirst1, _UFirst2, _Num); + return _Memcmp_pred{}(_Ans, 0) || (_Ans == 0 && _Num1 < _Num2); +#endif // ^^^ !_USE_STD_VECTOR_ALGORITHMS ^^^ } } @@ -5781,42 +5757,37 @@ struct _Lex_compare_three_way_memcmp_classify_comp { template struct _Lex_compare_three_way_memcmp_classify_comp<_Elem1, _Elem2, compare_three_way> { - static constexpr _Lex_cmp_opt _Opt = _Lex_compare_memcmp_classify_elements<_Elem1, _Elem2>; - using _Comp = conditional_t<_Opt != _Lex_cmp_opt::_None && three_way_comparable_with, + using _Comp = conditional_t<_Lex_compare_memcmp_classify_elements<_Elem1, _Elem2> + && three_way_comparable_with, compare_three_way, void>; }; template struct _Lex_compare_three_way_memcmp_classify_comp<_Elem1, _Elem2, _Strong_order::_Cpo> { - static constexpr _Lex_cmp_opt _Opt = _Lex_compare_memcmp_classify_elements<_Elem1, _Elem2>; using _Comp = - conditional_t<_Opt != _Lex_cmp_opt::_None && _Can_strong_order<_Elem1, _Elem2>, _Strong_order::_Cpo, void>; + conditional_t<_Lex_compare_memcmp_classify_elements<_Elem1, _Elem2> && _Can_strong_order<_Elem1, _Elem2>, + _Strong_order::_Cpo, void>; }; template struct _Lex_compare_three_way_memcmp_classify_comp<_Elem1, _Elem2, _Weak_order::_Cpo> { - static constexpr _Lex_cmp_opt _Opt = _Lex_compare_memcmp_classify_elements<_Elem1, _Elem2>; using _Comp = - conditional_t<_Opt != _Lex_cmp_opt::_None && _Can_weak_order<_Elem1, _Elem2>, _Weak_order::_Cpo, void>; + conditional_t<_Lex_compare_memcmp_classify_elements<_Elem1, _Elem2> && _Can_weak_order<_Elem1, _Elem2>, + _Weak_order::_Cpo, void>; }; template struct _Lex_compare_three_way_memcmp_classify_comp<_Elem1, _Elem2, _Partial_order::_Cpo> { - static constexpr _Lex_cmp_opt _Opt = _Lex_compare_memcmp_classify_elements<_Elem1, _Elem2>; using _Comp = - conditional_t<_Opt != _Lex_cmp_opt::_None && _Can_partial_order<_Elem1, _Elem2>, _Partial_order::_Cpo, void>; -}; - -struct _Lex_compare_three_way_memcmp_disable { - static constexpr _Lex_cmp_opt _Opt = _Lex_cmp_opt::_None; - using _Comp = void; + conditional_t<_Lex_compare_memcmp_classify_elements<_Elem1, _Elem2> && _Can_partial_order<_Elem1, _Elem2>, + _Partial_order::_Cpo, void>; }; template using _Lex_compare_three_way_memcmp_classify = conditional_t<_Iterators_are_contiguous<_It1, _It2> && !_Iterator_is_volatile<_It1> && !_Iterator_is_volatile<_It2>, - _Lex_compare_three_way_memcmp_classify_comp<_Iter_value_t<_It1>, _Iter_value_t<_It2>, _Cmp>, - _Lex_compare_three_way_memcmp_disable>; + typename _Lex_compare_three_way_memcmp_classify_comp<_Iter_value_t<_It1>, _Iter_value_t<_It2>, _Cmp>::_Comp, + void>; _EXPORT_STD template _NODISCARD constexpr auto lexicographical_compare_three_way(const _InIt1 _First1, const _InIt1 _Last1, @@ -5829,34 +5800,30 @@ _NODISCARD constexpr auto lexicographical_compare_three_way(const _InIt1 _First1 const auto _ULast2 = _STD _Get_unwrapped(_Last2); using _Memcmp_pred = _Lex_compare_three_way_memcmp_classify; - if constexpr (!is_void_v) { + if constexpr (!is_void_v<_Memcmp_pred>) { if (!_STD is_constant_evaluated()) { const auto _Num1 = static_cast(_ULast1 - _UFirst1); const auto _Num2 = static_cast(_ULast2 - _UFirst2); const size_t _Num = (_STD min)(_Num1, _Num2); - #if _USE_STD_VECTOR_ALGORITHMS - if constexpr (_Memcmp_pred::_Opt == _Lex_cmp_opt::_Mismatch) { - const auto _First1_ptr = _STD to_address(_UFirst1); - const auto _First2_ptr = _STD to_address(_UFirst2); - const size_t _Pos = __std_mismatch(_First1_ptr, _First2_ptr, _Num); - if (_Pos == _Num1) { - return _Pos == _Num2 ? strong_ordering::equal : strong_ordering::less; - } else if (_Pos == _Num2) { - return strong_ordering::greater; - } else { - return _Comp(_First1_ptr[_Pos], _First2_ptr[_Pos]); - } - } else -#endif // _USE_STD_VECTOR_ALGORITHMS - { - const int _Ans = _STD _Memcmp_count(_UFirst1, _UFirst2, _Num); - if (_Ans == 0) { - return _Num1 <=> _Num2; - } else { - return typename _Memcmp_pred::_Comp{}(_Ans, 0); - } + const auto _First1_ptr = _STD to_address(_UFirst1); + const auto _First2_ptr = _STD to_address(_UFirst2); + const size_t _Pos = __std_mismatch(_First1_ptr, _First2_ptr, _Num); + if (_Pos == _Num1) { + return _Pos == _Num2 ? strong_ordering::equal : strong_ordering::less; + } else if (_Pos == _Num2) { + return strong_ordering::greater; + } else { + return _Comp(_First1_ptr[_Pos], _First2_ptr[_Pos]); } +#else // ^^^ _USE_STD_VECTOR_ALGORITHMS / !_USE_STD_VECTOR_ALGORITHMS vvv + const int _Ans = _STD _Memcmp_count(_UFirst1, _UFirst2, _Num); + if (_Ans == 0) { + return _Num1 <=> _Num2; + } else { + return _Memcmp_pred{}(_Ans, 0); + } +#endif // ^^^ !_USE_STD_VECTOR_ALGORITHMS ^^^ } } diff --git a/tests/std/tests/GH_000431_lex_compare_memcmp_classify/test.compile.pass.cpp b/tests/std/tests/GH_000431_lex_compare_memcmp_classify/test.compile.pass.cpp index 04371cb74f6..a0f16f87237 100644 --- a/tests/std/tests/GH_000431_lex_compare_memcmp_classify/test.compile.pass.cpp +++ b/tests/std/tests/GH_000431_lex_compare_memcmp_classify/test.compile.pass.cpp @@ -22,13 +22,13 @@ using namespace std; template void assert_lex_compare_memcmp_classify() { - STATIC_ASSERT(is_same_v::_Pred, Expected>); + STATIC_ASSERT(is_same_v<_Lex_compare_memcmp_classify, Expected>); } #if _HAS_CXX20 template void assert_lex_compare_three_way_memcmp_classify() { - STATIC_ASSERT(is_same_v::_Comp, Expected>); + STATIC_ASSERT(is_same_v<_Lex_compare_three_way_memcmp_classify, Expected>); } #endif // _HAS_CXX20 From c29ebb43479703b8fd6dab28173e1c08a4544255 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Mon, 8 Apr 2024 17:51:15 +0300 Subject: [PATCH 06/12] benchmark: fancy way to have common args --- benchmarks/src/mismatch.cpp | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/benchmarks/src/mismatch.cpp b/benchmarks/src/mismatch.cpp index c2d468fdecd..0aab0ef21ed 100644 --- a/benchmarks/src/mismatch.cpp +++ b/benchmarks/src/mismatch.cpp @@ -35,17 +35,19 @@ void bm(benchmark::State& state) { } } -#define COMMON_ARGS Args({8, 3})->Args({24, 22})->Args({105, -1})->Args({4021, 3056}) - -BENCHMARK(bm)->COMMON_ARGS; -BENCHMARK(bm)->COMMON_ARGS; -BENCHMARK(bm)->COMMON_ARGS; -BENCHMARK(bm)->COMMON_ARGS; - -BENCHMARK(bm)->COMMON_ARGS; -BENCHMARK(bm)->COMMON_ARGS; -BENCHMARK(bm)->COMMON_ARGS; -BENCHMARK(bm)->COMMON_ARGS; -BENCHMARK(bm)->COMMON_ARGS; +void common_args(auto bm) { + bm->Args({8, 3})->Args({24, 22})->Args({105, -1})->Args({4021, 3056}); +} + +BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); + +BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); BENCHMARK_MAIN(); From b4f5ca4d1713105721adac016c02af7c1c034d76 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Mon, 8 Apr 2024 17:53:17 +0300 Subject: [PATCH 07/12] benchmark: explain signed / unsigned 8-bit integer --- benchmarks/src/mismatch.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/benchmarks/src/mismatch.cpp b/benchmarks/src/mismatch.cpp index 0aab0ef21ed..2efe44a0a21 100644 --- a/benchmarks/src/mismatch.cpp +++ b/benchmarks/src/mismatch.cpp @@ -44,8 +44,8 @@ BENCHMARK(bm)->Apply(common_args); BENCHMARK(bm)->Apply(common_args); BENCHMARK(bm)->Apply(common_args); -BENCHMARK(bm)->Apply(common_args); -BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); // still optimized without vector algorithms using memcmp +BENCHMARK(bm)->Apply(common_args); // optimized with vector algorithms only BENCHMARK(bm)->Apply(common_args); BENCHMARK(bm)->Apply(common_args); BENCHMARK(bm)->Apply(common_args); From 808ff8fc007dd587533761c50262890638eb5b24 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Tue, 9 Apr 2024 12:22:10 +0300 Subject: [PATCH 08/12] More precise comments in env.lst --- tests/std/tests/GH_000431_lex_compare_memcmp_classify/env.lst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/std/tests/GH_000431_lex_compare_memcmp_classify/env.lst b/tests/std/tests/GH_000431_lex_compare_memcmp_classify/env.lst index e39b826ac9c..00256420b81 100644 --- a/tests/std/tests/GH_000431_lex_compare_memcmp_classify/env.lst +++ b/tests/std/tests/GH_000431_lex_compare_memcmp_classify/env.lst @@ -3,5 +3,5 @@ RUNALL_INCLUDE ..\char8_t_matrix.lst RUNALL_CROSSLIST -* PM_CL="" # Test memcmp and manual vectorization -* PM_CL="/D_USE_STD_VECTOR_ALGORITHMS=0" # Test memcmp only +* PM_CL="" # Test manual vectorization +* PM_CL="/D_USE_STD_VECTOR_ALGORITHMS=0" # Test memcmp optimization From 26606be2f54b92904963446cd7110d28fcff5497 Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Tue, 9 Apr 2024 13:42:02 +0300 Subject: [PATCH 09/12] avoid one comparison --- stl/inc/xutility | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index d4cfda7622d..fde9e715959 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -5814,7 +5814,10 @@ _NODISCARD constexpr auto lexicographical_compare_three_way(const _InIt1 _First1 } else if (_Pos == _Num2) { return strong_ordering::greater; } else { - return _Comp(_First1_ptr[_Pos], _First2_ptr[_Pos]); + const auto _Val1 = _First1_ptr[_Pos]; + const auto _Val2 = _First2_ptr[_Pos]; + __assume(_Val1 != _Val2); // avoid one comparison + return _Comp(_Val1, _Val2); } #else // ^^^ _USE_STD_VECTOR_ALGORITHMS / !_USE_STD_VECTOR_ALGORITHMS vvv const int _Ans = _STD _Memcmp_count(_UFirst1, _UFirst2, _Num); From 9e65f99defa52ac351cbf3b2ba1e0d2c06e08fdb Mon Sep 17 00:00:00 2001 From: Alex Guteniev Date: Wed, 10 Apr 2024 07:38:59 +0300 Subject: [PATCH 10/12] missed _Meow_vectorized --- stl/inc/algorithm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 7ce65113c78..df17fea0dba 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -10975,7 +10975,7 @@ namespace ranges { #if _USE_STD_VECTOR_ALGORITHMS const auto _First1_ptr = _STD to_address(_First1); const auto _First2_ptr = _STD to_address(_First2); - const size_t _Pos = __std_mismatch(_First1_ptr, _First2_ptr, _Num); + const size_t _Pos = _Mismatch_vectorized(_First1_ptr, _First2_ptr, _Num); if (_Pos == _Num2) { return false; } else if (_Pos == _Num1) { From 3990a81192ff7739a6939f25a6a4a1031c9b658f Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 11 Apr 2024 16:57:54 -0700 Subject: [PATCH 11/12] Test `ranges::lexicographical_compare`. --- tests/std/tests/VSO_0000000_vector_algorithms/test.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp index 94800357c70..9d0dc896cfd 100644 --- a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp +++ b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp @@ -441,6 +441,9 @@ void test_case_mismatch_and_lex_compare_family(const vector& a, const vector< assert(get<0>(expected_mismatch) == ranges_actual_mismatch.in1); assert(get<1>(expected_mismatch) == ranges_actual_mismatch.in2); + auto ranges_actual_lex = ranges::lexicographical_compare(a, b); + assert(expected_lex == ranges_actual_lex); + auto expected_lex_3way = last_known_good_lex_compare_3way(a.begin(), a.end(), b.begin(), b.end()); auto actual_lex_3way = lexicographical_compare_three_way(a.begin(), a.end(), b.begin(), b.end()); assert(expected_lex_3way == actual_lex_3way); @@ -510,6 +513,9 @@ void test_mismatch_and_lex_compare_family_containers() { assert(result_mismatch_r.in1 == a.begin() + 5); assert(result_mismatch_r.in2 == b.begin() + 5); + const auto result_lex_r = ranges::lexicographical_compare(a, b); + assert(result_lex_r == true); + const auto result_lex_3way = lexicographical_compare_three_way(a.begin(), a.end(), b.begin(), b.end()); assert(result_lex_3way == strong_ordering::less); #endif // _HAS_CXX20 From dc9db99c8d6a64c3705bb700ea918b0e28c85a73 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 11 Apr 2024 17:28:50 -0700 Subject: [PATCH 12/12] Update test comments as more cases are allowed. --- .../test.compile.pass.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/std/tests/GH_000431_lex_compare_memcmp_classify/test.compile.pass.cpp b/tests/std/tests/GH_000431_lex_compare_memcmp_classify/test.compile.pass.cpp index a0f16f87237..8f6be97461a 100644 --- a/tests/std/tests/GH_000431_lex_compare_memcmp_classify/test.compile.pass.cpp +++ b/tests/std/tests/GH_000431_lex_compare_memcmp_classify/test.compile.pass.cpp @@ -228,7 +228,7 @@ bool operator<(const user_struct&, const user_struct&) { } void lex_compare_memcmp_classify_test_cases() { - // Allow unsigned 1 byte integrals + // Test 1 byte integrals test_lex_compare_memcmp_classify_for_1byte_integrals || vec_alg, char, char>(); test_lex_compare_memcmp_classify_for_1byte_integrals, unsigned char, char>(); test_lex_compare_memcmp_classify_for_1byte_integrals, char, unsigned char>(); @@ -282,7 +282,7 @@ void lex_compare_memcmp_classify_test_cases() { test_lex_compare_memcmp_classify_for_types(); #endif // __cpp_lib_byte - // Don't allow bigger integrals + // Test bigger integrals test_lex_compare_memcmp_classify_for_types(); test_lex_compare_memcmp_classify_for_types(); test_lex_compare_memcmp_classify_for_types();