diff --git a/benchmarks/src/mismatch.cpp b/benchmarks/src/mismatch.cpp index f6a3069ac22..2efe44a0a21 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,27 @@ 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}) +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)->COMMON_ARGS; -BENCHMARK(bm)->COMMON_ARGS; -BENCHMARK(bm)->COMMON_ARGS; -BENCHMARK(bm)->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); BENCHMARK_MAIN(); diff --git a/stl/inc/algorithm b/stl/inc/algorithm index f4bfbd75a14..df17fea0dba 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -10971,8 +10971,22 @@ namespace ranges { _Num2 = SIZE_MAX; } - const int _Ans = _STD _Memcmp_count(_First1, _First2, (_STD min)(_Num1, _Num2)); + const size_t _Num = (_STD min)(_Num1, _Num2); +#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 = _Mismatch_vectorized(_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/xutility b/stl/inc/xutility index 7deba10afd4..a58ccea242c 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -5629,8 +5629,14 @@ namespace ranges { #endif // _HAS_CXX20 template -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>>; +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>; +#else // ^^^ _USE_STD_VECTOR_ALGORITHMS / !_USE_STD_VECTOR_ALGORITHMS vvv + 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 <> @@ -5702,10 +5708,24 @@ _NODISCARD _CONSTEXPR20 bool lexicographical_compare( 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)); + 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 + const auto _First1_ptr = _STD _To_address(_UFirst1); + const auto _First2_ptr = _STD _To_address(_UFirst2); + const size_t _Pos = _Mismatch_vectorized(_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 ^^^ } } @@ -5802,14 +5822,31 @@ _NODISCARD constexpr auto lexicographical_compare_three_way(const _InIt1 _First1 using _Memcmp_pred = _Lex_compare_three_way_memcmp_classify; 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 int _Ans = _STD _Memcmp_count(_UFirst1, _UFirst2, (_STD min)(_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 + const auto _First1_ptr = _STD to_address(_UFirst1); + const auto _First2_ptr = _STD to_address(_UFirst2); + const size_t _Pos = _Mismatch_vectorized(_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 { + 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); 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/env.lst b/tests/std/tests/GH_000431_lex_compare_memcmp_classify/env.lst index 6ccc73d16ea..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 @@ -2,3 +2,6 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception RUNALL_INCLUDE ..\char8_t_matrix.lst +RUNALL_CROSSLIST +* PM_CL="" # Test manual vectorization +* PM_CL="/D_USE_STD_VECTOR_ALGORITHMS=0" # Test memcmp optimization 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..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 @@ -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 @@ -224,14 +228,14 @@ 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 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>(); 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,8 +256,8 @@ 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, 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(); @@ -278,13 +282,13 @@ 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(); - 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 +302,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 49fec16ea07..9d0dc896cfd 100644 --- a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp +++ b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp @@ -20,8 +20,9 @@ #include #if _HAS_CXX20 +#include #include -#endif +#endif // _HAS_CXX20 #include "test_min_max_element_support.hpp" @@ -388,20 +389,69 @@ 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; + } + } +} + +#if _HAS_CXX20 +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; + } + } + } +} +#endif // _HAS_CXX20 + 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 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); #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 +463,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 +479,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 +494,30 @@ 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_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 } @@ -751,23 +812,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();