[libcxx-commits] [libcxx] d07fdf9 - [libc++] Optimize lexicographical_compare (#65279)
via libcxx-commits
libcxx-commits at lists.llvm.org
Sun Aug 4 01:02:46 PDT 2024
Author: Nikolas Klauser
Date: 2024-08-04T10:02:43+02:00
New Revision: d07fdf9779f7dead2828cfb18bafbd9a2c085920
URL: https://github.com/llvm/llvm-project/commit/d07fdf9779f7dead2828cfb18bafbd9a2c085920
DIFF: https://github.com/llvm/llvm-project/commit/d07fdf9779f7dead2828cfb18bafbd9a2c085920.diff
LOG: [libc++] Optimize lexicographical_compare (#65279)
If the comparison operation is equivalent to < and that is a total
order, we know that we can use equality comparison on that type instead
to extract some information. Furthermore, if equality comparison on that
type is trivial, the user can't observe that we're calling it. So
instead of using the user-provided total order, we use std::mismatch,
which uses equality comparison (and is vertorized). Additionally, if the
type is trivially lexicographically comparable, we can go one step
further and use std::memcmp directly instead of calling std::mismatch.
Benchmarks:
```
-------------------------------------------------------------------------------------
Benchmark old new
-------------------------------------------------------------------------------------
bm_lexicographical_compare<unsigned char>/1 1.17 ns 2.34 ns
bm_lexicographical_compare<unsigned char>/2 1.64 ns 2.57 ns
bm_lexicographical_compare<unsigned char>/3 2.23 ns 2.58 ns
bm_lexicographical_compare<unsigned char>/4 2.82 ns 2.57 ns
bm_lexicographical_compare<unsigned char>/5 3.34 ns 2.11 ns
bm_lexicographical_compare<unsigned char>/6 3.94 ns 2.21 ns
bm_lexicographical_compare<unsigned char>/7 4.56 ns 2.11 ns
bm_lexicographical_compare<unsigned char>/8 5.25 ns 2.11 ns
bm_lexicographical_compare<unsigned char>/16 9.88 ns 2.11 ns
bm_lexicographical_compare<unsigned char>/64 38.9 ns 2.36 ns
bm_lexicographical_compare<unsigned char>/512 317 ns 6.54 ns
bm_lexicographical_compare<unsigned char>/4096 2517 ns 41.4 ns
bm_lexicographical_compare<unsigned char>/32768 20052 ns 488 ns
bm_lexicographical_compare<unsigned char>/262144 159579 ns 4409 ns
bm_lexicographical_compare<unsigned char>/1048576 640456 ns 20342 ns
bm_lexicographical_compare<signed char>/1 1.18 ns 2.37 ns
bm_lexicographical_compare<signed char>/2 1.65 ns 2.60 ns
bm_lexicographical_compare<signed char>/3 2.23 ns 2.83 ns
bm_lexicographical_compare<signed char>/4 2.81 ns 3.06 ns
bm_lexicographical_compare<signed char>/5 3.35 ns 3.30 ns
bm_lexicographical_compare<signed char>/6 3.90 ns 3.99 ns
bm_lexicographical_compare<signed char>/7 4.56 ns 3.78 ns
bm_lexicographical_compare<signed char>/8 5.20 ns 4.02 ns
bm_lexicographical_compare<signed char>/16 9.80 ns 6.21 ns
bm_lexicographical_compare<signed char>/64 39.0 ns 3.16 ns
bm_lexicographical_compare<signed char>/512 318 ns 7.58 ns
bm_lexicographical_compare<signed char>/4096 2514 ns 47.4 ns
bm_lexicographical_compare<signed char>/32768 20096 ns 504 ns
bm_lexicographical_compare<signed char>/262144 156617 ns 4146 ns
bm_lexicographical_compare<signed char>/1048576 624265 ns 19810 ns
bm_lexicographical_compare<int>/1 1.15 ns 2.12 ns
bm_lexicographical_compare<int>/2 1.60 ns 2.36 ns
bm_lexicographical_compare<int>/3 2.21 ns 2.59 ns
bm_lexicographical_compare<int>/4 2.74 ns 2.83 ns
bm_lexicographical_compare<int>/5 3.26 ns 3.06 ns
bm_lexicographical_compare<int>/6 3.81 ns 4.53 ns
bm_lexicographical_compare<int>/7 4.41 ns 4.72 ns
bm_lexicographical_compare<int>/8 5.08 ns 2.36 ns
bm_lexicographical_compare<int>/16 9.54 ns 3.08 ns
bm_lexicographical_compare<int>/64 37.8 ns 4.71 ns
bm_lexicographical_compare<int>/512 309 ns 24.6 ns
bm_lexicographical_compare<int>/4096 2422 ns 204 ns
bm_lexicographical_compare<int>/32768 19362 ns 1947 ns
bm_lexicographical_compare<int>/262144 155727 ns 19793 ns
bm_lexicographical_compare<int>/1048576 623614 ns 80180 ns
bm_ranges_lexicographical_compare<unsigned char>/1 1.07 ns 2.35 ns
bm_ranges_lexicographical_compare<unsigned char>/2 1.72 ns 2.13 ns
bm_ranges_lexicographical_compare<unsigned char>/3 2.46 ns 2.12 ns
bm_ranges_lexicographical_compare<unsigned char>/4 3.17 ns 2.12 ns
bm_ranges_lexicographical_compare<unsigned char>/5 3.86 ns 2.12 ns
bm_ranges_lexicographical_compare<unsigned char>/6 4.55 ns 2.12 ns
bm_ranges_lexicographical_compare<unsigned char>/7 5.25 ns 2.12 ns
bm_ranges_lexicographical_compare<unsigned char>/8 5.95 ns 2.13 ns
bm_ranges_lexicographical_compare<unsigned char>/16 11.7 ns 2.13 ns
bm_ranges_lexicographical_compare<unsigned char>/64 45.5 ns 2.36 ns
bm_ranges_lexicographical_compare<unsigned char>/512 366 ns 6.35 ns
bm_ranges_lexicographical_compare<unsigned char>/4096 2886 ns 40.9 ns
bm_ranges_lexicographical_compare<unsigned char>/32768 23054 ns 489 ns
bm_ranges_lexicographical_compare<unsigned char>/262144 185302 ns 4339 ns
bm_ranges_lexicographical_compare<unsigned char>/1048576 741576 ns 19430 ns
bm_ranges_lexicographical_compare<signed char>/1 1.10 ns 2.12 ns
bm_ranges_lexicographical_compare<signed char>/2 1.66 ns 2.35 ns
bm_ranges_lexicographical_compare<signed char>/3 2.23 ns 2.58 ns
bm_ranges_lexicographical_compare<signed char>/4 2.82 ns 2.82 ns
bm_ranges_lexicographical_compare<signed char>/5 3.34 ns 3.06 ns
bm_ranges_lexicographical_compare<signed char>/6 3.92 ns 3.99 ns
bm_ranges_lexicographical_compare<signed char>/7 4.64 ns 4.10 ns
bm_ranges_lexicographical_compare<signed char>/8 5.21 ns 4.61 ns
bm_ranges_lexicographical_compare<signed char>/16 9.79 ns 7.42 ns
bm_ranges_lexicographical_compare<signed char>/64 38.9 ns 2.93 ns
bm_ranges_lexicographical_compare<signed char>/512 317 ns 7.31 ns
bm_ranges_lexicographical_compare<signed char>/4096 2500 ns 47.5 ns
bm_ranges_lexicographical_compare<signed char>/32768 19940 ns 496 ns
bm_ranges_lexicographical_compare<signed char>/262144 159166 ns 4393 ns
bm_ranges_lexicographical_compare<signed char>/1048576 638206 ns 19786 ns
bm_ranges_lexicographical_compare<int>/1 1.10 ns 2.12 ns
bm_ranges_lexicographical_compare<int>/2 1.64 ns 3.04 ns
bm_ranges_lexicographical_compare<int>/3 2.23 ns 2.58 ns
bm_ranges_lexicographical_compare<int>/4 2.81 ns 2.81 ns
bm_ranges_lexicographical_compare<int>/5 3.35 ns 3.05 ns
bm_ranges_lexicographical_compare<int>/6 3.94 ns 4.60 ns
bm_ranges_lexicographical_compare<int>/7 4.60 ns 4.81 ns
bm_ranges_lexicographical_compare<int>/8 5.19 ns 2.35 ns
bm_ranges_lexicographical_compare<int>/16 9.85 ns 2.87 ns
bm_ranges_lexicographical_compare<int>/64 38.9 ns 4.70 ns
bm_ranges_lexicographical_compare<int>/512 318 ns 24.5 ns
bm_ranges_lexicographical_compare<int>/4096 2494 ns 202 ns
bm_ranges_lexicographical_compare<int>/32768 20000 ns 1939 ns
bm_ranges_lexicographical_compare<int>/262144 160433 ns 19730 ns
bm_ranges_lexicographical_compare<int>/1048576 642636 ns 80760 ns
```
Added:
libcxx/test/benchmarks/algorithms/lexicographical_compare.bench.cpp
Modified:
libcxx/docs/ReleaseNotes/20.rst
libcxx/include/__algorithm/comp.h
libcxx/include/__algorithm/lexicographical_compare.h
libcxx/include/__algorithm/ranges_lexicographical_compare.h
libcxx/include/__algorithm/ranges_minmax.h
libcxx/include/__functional/operations.h
libcxx/include/__functional/ranges_operations.h
libcxx/include/__string/constexpr_c_functions.h
libcxx/include/__type_traits/desugars_to.h
libcxx/include/__type_traits/is_trivially_lexicographically_comparable.h
libcxx/test/benchmarks/CMakeLists.txt
libcxx/test/libcxx/transitive_includes/cxx03.csv
libcxx/test/libcxx/transitive_includes/cxx11.csv
libcxx/test/libcxx/transitive_includes/cxx14.csv
libcxx/test/libcxx/transitive_includes/cxx17.csv
libcxx/test/libcxx/transitive_includes/cxx20.csv
libcxx/test/libcxx/transitive_includes/cxx23.csv
libcxx/test/libcxx/transitive_includes/cxx26.csv
libcxx/test/std/algorithms/alg.sorting/alg.lex.comparison/lexicographical_compare.pass.cpp
libcxx/test/std/containers/sequences/array/compare.verify.cpp
Removed:
################################################################################
diff --git a/libcxx/docs/ReleaseNotes/20.rst b/libcxx/docs/ReleaseNotes/20.rst
index 960fdd7ce0562..b319067394099 100644
--- a/libcxx/docs/ReleaseNotes/20.rst
+++ b/libcxx/docs/ReleaseNotes/20.rst
@@ -44,7 +44,8 @@ Implemented Papers
Improvements and New Features
-----------------------------
-- TODO
+- The ``lexicographical_compare`` and ``ranges::lexicographical_compare`` algorithms have been optimized for trivially
+ equality comparable types, resulting in a performance improvement of up to 40x.
Deprecations and Removals
diff --git a/libcxx/include/__algorithm/comp.h b/libcxx/include/__algorithm/comp.h
index a0fa88d6d2acd..1f38f5d2d99b4 100644
--- a/libcxx/include/__algorithm/comp.h
+++ b/libcxx/include/__algorithm/comp.h
@@ -11,6 +11,7 @@
#include <__config>
#include <__type_traits/desugars_to.h>
+#include <__type_traits/is_integral.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -42,7 +43,7 @@ struct __less<void, void> {
};
template <class _Tp>
-inline const bool __desugars_to_v<__less_tag, __less<>, _Tp, _Tp> = true;
+inline const bool __desugars_to_v<__totally_ordered_less_tag, __less<>, _Tp, _Tp> = is_integral<_Tp>::value;
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__algorithm/lexicographical_compare.h b/libcxx/include/__algorithm/lexicographical_compare.h
index edc29e269c88c..df23e6a612c1a 100644
--- a/libcxx/include/__algorithm/lexicographical_compare.h
+++ b/libcxx/include/__algorithm/lexicographical_compare.h
@@ -10,32 +10,93 @@
#define _LIBCPP___ALGORITHM_LEXICOGRAPHICAL_COMPARE_H
#include <__algorithm/comp.h>
-#include <__algorithm/comp_ref_type.h>
+#include <__algorithm/min.h>
+#include <__algorithm/mismatch.h>
+#include <__algorithm/simd_utils.h>
+#include <__algorithm/unwrap_iter.h>
#include <__config>
+#include <__functional/identity.h>
#include <__iterator/iterator_traits.h>
+#include <__string/constexpr_c_functions.h>
+#include <__type_traits/desugars_to.h>
+#include <__type_traits/invoke.h>
+#include <__type_traits/is_equality_comparable.h>
+#include <__type_traits/is_integral.h>
+#include <__type_traits/is_trivially_lexicographically_comparable.h>
+#include <__type_traits/is_volatile.h>
+
+#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+# include <cwchar>
+#endif
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
_LIBCPP_BEGIN_NAMESPACE_STD
-template <class _Compare, class _InputIterator1, class _InputIterator2>
+template <class _Iter1, class _Sent1, class _Iter2, class _Sent2, class _Proj1, class _Proj2, class _Comp>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __lexicographical_compare(
- _InputIterator1 __first1,
- _InputIterator1 __last1,
- _InputIterator2 __first2,
- _InputIterator2 __last2,
- _Compare __comp) {
- for (; __first2 != __last2; ++__first1, (void)++__first2) {
- if (__first1 == __last1 || __comp(*__first1, *__first2))
+ _Iter1 __first1, _Sent1 __last1, _Iter2 __first2, _Sent2 __last2, _Comp& __comp, _Proj1& __proj1, _Proj2& __proj2) {
+ while (__first2 != __last2) {
+ if (__first1 == __last1 ||
+ std::__invoke(__comp, std::__invoke(__proj1, *__first1), std::__invoke(__proj2, *__first2)))
return true;
- if (__comp(*__first2, *__first1))
+ if (std::__invoke(__comp, std::__invoke(__proj2, *__first2), std::__invoke(__proj1, *__first1)))
return false;
+ ++__first1;
+ ++__first2;
}
return false;
}
+#if _LIBCPP_STD_VER >= 14
+
+// If the comparison operation is equivalent to < and that is a total order, we know that we can use equality comparison
+// on that type instead to extract some information. Furthermore, if equality comparison on that type is trivial, the
+// user can't observe that we're calling it. So instead of using the user-provided total order, we use std::mismatch,
+// which uses equality comparison (and is vertorized). Additionally, if the type is trivially lexicographically
+// comparable, we can go one step further and use std::memcmp directly instead of calling std::mismatch.
+template <class _Tp,
+ class _Proj1,
+ class _Proj2,
+ class _Comp,
+ __enable_if_t<__desugars_to_v<__totally_ordered_less_tag, _Comp, _Tp, _Tp> && !is_volatile<_Tp>::value &&
+ __libcpp_is_trivially_equality_comparable<_Tp, _Tp>::value &&
+ __is_identity<_Proj1>::value && __is_identity<_Proj2>::value,
+ int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
+__lexicographical_compare(_Tp* __first1, _Tp* __last1, _Tp* __first2, _Tp* __last2, _Comp&, _Proj1&, _Proj2&) {
+ if constexpr (__is_trivially_lexicographically_comparable_v<_Tp, _Tp>) {
+ auto __res =
+ std::__constexpr_memcmp(__first1, __first2, __element_count(std::min(__last1 - __first1, __last2 - __first2)));
+ if (__res == 0)
+ return __last1 - __first1 < __last2 - __first2;
+ return __res < 0;
+ }
+# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+ else if constexpr (is_same<__remove_cv_t<_Tp>, wchar_t>::value) {
+ auto __res = std::__constexpr_wmemcmp(__first1, __first2, std::min(__last1 - __first1, __last2 - __first2));
+ if (__res == 0)
+ return __last1 - __first1 < __last2 - __first2;
+ return __res < 0;
+ }
+# endif // _LIBCPP_HAS_NO_WIDE_CHARACTERS
+ else {
+ auto __res = std::mismatch(__first1, __last1, __first2, __last2);
+ if (__res.second == __last2)
+ return false;
+ if (__res.first == __last1)
+ return true;
+ return *__res.first < *__res.second;
+ }
+}
+
+#endif // _LIBCPP_STD_VER >= 14
+
template <class _InputIterator1, class _InputIterator2, class _Compare>
_LIBCPP_NODISCARD inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool lexicographical_compare(
_InputIterator1 __first1,
@@ -43,7 +104,15 @@ _LIBCPP_NODISCARD inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 boo
_InputIterator2 __first2,
_InputIterator2 __last2,
_Compare __comp) {
- return std::__lexicographical_compare<__comp_ref_type<_Compare> >(__first1, __last1, __first2, __last2, __comp);
+ __identity __proj;
+ return std::__lexicographical_compare(
+ std::__unwrap_iter(__first1),
+ std::__unwrap_iter(__last1),
+ std::__unwrap_iter(__first2),
+ std::__unwrap_iter(__last2),
+ __comp,
+ __proj,
+ __proj);
}
template <class _InputIterator1, class _InputIterator2>
@@ -54,4 +123,6 @@ _LIBCPP_NODISCARD inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 boo
_LIBCPP_END_NAMESPACE_STD
+_LIBCPP_POP_MACROS
+
#endif // _LIBCPP___ALGORITHM_LEXICOGRAPHICAL_COMPARE_H
diff --git a/libcxx/include/__algorithm/ranges_lexicographical_compare.h b/libcxx/include/__algorithm/ranges_lexicographical_compare.h
index 024cc6b707cab..ec12b0cc29ace 100644
--- a/libcxx/include/__algorithm/ranges_lexicographical_compare.h
+++ b/libcxx/include/__algorithm/ranges_lexicographical_compare.h
@@ -9,6 +9,8 @@
#ifndef _LIBCPP___ALGORITHM_RANGES_LEXICOGRAPHICAL_COMPARE_H
#define _LIBCPP___ALGORITHM_RANGES_LEXICOGRAPHICAL_COMPARE_H
+#include <__algorithm/lexicographical_compare.h>
+#include <__algorithm/unwrap_range.h>
#include <__config>
#include <__functional/identity.h>
#include <__functional/invoke.h>
@@ -33,7 +35,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
namespace ranges {
struct __lexicographical_compare {
template <class _Iter1, class _Sent1, class _Iter2, class _Sent2, class _Proj1, class _Proj2, class _Comp>
- _LIBCPP_HIDE_FROM_ABI constexpr static bool __lexicographical_compare_impl(
+ static _LIBCPP_HIDE_FROM_ABI constexpr bool __lexicographical_compare_unwrap(
_Iter1 __first1,
_Sent1 __last1,
_Iter2 __first2,
@@ -41,15 +43,16 @@ struct __lexicographical_compare {
_Comp& __comp,
_Proj1& __proj1,
_Proj2& __proj2) {
- while (__first2 != __last2) {
- if (__first1 == __last1 || std::invoke(__comp, std::invoke(__proj1, *__first1), std::invoke(__proj2, *__first2)))
- return true;
- if (std::invoke(__comp, std::invoke(__proj2, *__first2), std::invoke(__proj1, *__first1)))
- return false;
- ++__first1;
- ++__first2;
- }
- return false;
+ auto [__first1_un, __last1_un] = std::__unwrap_range(std::move(__first1), std::move(__last1));
+ auto [__first2_un, __last2_un] = std::__unwrap_range(std::move(__first2), std::move(__last2));
+ return std::__lexicographical_compare(
+ std::move(__first1_un),
+ std::move(__last1_un),
+ std::move(__first2_un),
+ std::move(__last2_un),
+ __comp,
+ __proj1,
+ __proj2);
}
template <input_iterator _Iter1,
@@ -67,7 +70,7 @@ struct __lexicographical_compare {
_Comp __comp = {},
_Proj1 __proj1 = {},
_Proj2 __proj2 = {}) const {
- return __lexicographical_compare_impl(
+ return __lexicographical_compare_unwrap(
std::move(__first1), std::move(__last1), std::move(__first2), std::move(__last2), __comp, __proj1, __proj2);
}
@@ -79,7 +82,7 @@ struct __lexicographical_compare {
_Comp = ranges::less>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool operator()(
_Range1&& __range1, _Range2&& __range2, _Comp __comp = {}, _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const {
- return __lexicographical_compare_impl(
+ return __lexicographical_compare_unwrap(
ranges::begin(__range1),
ranges::end(__range1),
ranges::begin(__range2),
diff --git a/libcxx/include/__algorithm/ranges_minmax.h b/libcxx/include/__algorithm/ranges_minmax.h
index 1b43b1e19cdec..9b8551d221340 100644
--- a/libcxx/include/__algorithm/ranges_minmax.h
+++ b/libcxx/include/__algorithm/ranges_minmax.h
@@ -88,7 +88,7 @@ struct __minmax {
// vectorize the code.
if constexpr (contiguous_range<_Range> && is_integral_v<_ValueT> &&
__is_cheap_to_copy<_ValueT> & __is_identity<_Proj>::value &&
- __desugars_to_v<__less_tag, _Comp, _ValueT, _ValueT>) {
+ __desugars_to_v<__totally_ordered_less_tag, _Comp, _ValueT, _ValueT>) {
minmax_result<_ValueT> __result = {__r[0], __r[0]};
for (auto __e : __r) {
if (__e < __result.min)
diff --git a/libcxx/include/__functional/operations.h b/libcxx/include/__functional/operations.h
index 0a6320f19de3f..6022bd679ed3e 100644
--- a/libcxx/include/__functional/operations.h
+++ b/libcxx/include/__functional/operations.h
@@ -14,6 +14,7 @@
#include <__functional/binary_function.h>
#include <__functional/unary_function.h>
#include <__type_traits/desugars_to.h>
+#include <__type_traits/is_integral.h>
#include <__utility/forward.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -362,7 +363,7 @@ struct _LIBCPP_TEMPLATE_VIS less : __binary_function<_Tp, _Tp, bool> {
_LIBCPP_CTAD_SUPPORTED_FOR_TYPE(less);
template <class _Tp>
-inline const bool __desugars_to_v<__less_tag, less<_Tp>, _Tp, _Tp> = true;
+inline const bool __desugars_to_v<__totally_ordered_less_tag, less<_Tp>, _Tp, _Tp> = is_integral<_Tp>::value;
#if _LIBCPP_STD_VER >= 14
template <>
@@ -377,7 +378,7 @@ struct _LIBCPP_TEMPLATE_VIS less<void> {
};
template <class _Tp>
-inline const bool __desugars_to_v<__less_tag, less<>, _Tp, _Tp> = true;
+inline const bool __desugars_to_v<__totally_ordered_less_tag, less<>, _Tp, _Tp> = is_integral<_Tp>::value;
#endif
#if _LIBCPP_STD_VER >= 14
diff --git a/libcxx/include/__functional/ranges_operations.h b/libcxx/include/__functional/ranges_operations.h
index 27f06eadd0eb1..f023d765a6c8a 100644
--- a/libcxx/include/__functional/ranges_operations.h
+++ b/libcxx/include/__functional/ranges_operations.h
@@ -100,7 +100,7 @@ template <class _Tp, class _Up>
inline const bool __desugars_to_v<__equal_tag, ranges::equal_to, _Tp, _Up> = true;
template <class _Tp, class _Up>
-inline const bool __desugars_to_v<__less_tag, ranges::less, _Tp, _Up> = true;
+inline const bool __desugars_to_v<__totally_ordered_less_tag, ranges::less, _Tp, _Up> = true;
#endif // _LIBCPP_STD_VER >= 20
diff --git a/libcxx/include/__string/constexpr_c_functions.h b/libcxx/include/__string/constexpr_c_functions.h
index a978f816f1897..32fc06e121b36 100644
--- a/libcxx/include/__string/constexpr_c_functions.h
+++ b/libcxx/include/__string/constexpr_c_functions.h
@@ -64,13 +64,13 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 size_t __constexpr_st
return __builtin_strlen(reinterpret_cast<const char*>(__str));
}
-// Because of __libcpp_is_trivially_lexicographically_comparable we know that comparing the object representations is
+// Because of __is_trivially_lexicographically_comparable_v we know that comparing the object representations is
// equivalent to a std::memcmp. Since we have multiple objects contiguously in memory, we can call memcmp once instead
// of invoking it on every object individually.
template <class _Tp, class _Up>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 int
__constexpr_memcmp(const _Tp* __lhs, const _Up* __rhs, __element_count __n) {
- static_assert(__libcpp_is_trivially_lexicographically_comparable<_Tp, _Up>::value,
+ static_assert(__is_trivially_lexicographically_comparable_v<_Tp, _Up>,
"_Tp and _Up have to be trivially lexicographically comparable");
auto __count = static_cast<size_t>(__n);
diff --git a/libcxx/include/__type_traits/desugars_to.h b/libcxx/include/__type_traits/desugars_to.h
index 97a2ee5448f20..b0ce7c414e5d7 100644
--- a/libcxx/include/__type_traits/desugars_to.h
+++ b/libcxx/include/__type_traits/desugars_to.h
@@ -17,10 +17,21 @@
_LIBCPP_BEGIN_NAMESPACE_STD
-// Tags to represent the canonical operations
+// Tags to represent the canonical operations.
+
+// syntactically, the operation is equivalent to calling `a == b`
struct __equal_tag {};
+
+// syntactically, the operation is equivalent to calling `a + b`
struct __plus_tag {};
-struct __less_tag {};
+
+// syntactically, the operation is equivalent to calling `a < b`, and these expressions
+// have to be true for any `a` and `b`:
+// - `(a < b) == (b > a)`
+// - `(!(a < b) && !(b < a)) == (a == b)`
+// For example, this is satisfied for std::less on integral types, but also for ranges::less on all types due to
+// additional semantic requirements on that operation.
+struct __totally_ordered_less_tag {};
// This class template is used to determine whether an operation "desugars"
// (or boils down) to a given canonical operation.
diff --git a/libcxx/include/__type_traits/is_trivially_lexicographically_comparable.h b/libcxx/include/__type_traits/is_trivially_lexicographically_comparable.h
index a310ea1b87e30..337f878fea5c1 100644
--- a/libcxx/include/__type_traits/is_trivially_lexicographically_comparable.h
+++ b/libcxx/include/__type_traits/is_trivially_lexicographically_comparable.h
@@ -16,6 +16,7 @@
#include <__type_traits/remove_cv.h>
#include <__type_traits/void_t.h>
#include <__utility/declval.h>
+#include <cstddef>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -40,13 +41,22 @@ _LIBCPP_BEGIN_NAMESPACE_STD
// unsigned integer types with sizeof(T) > 1: depending on the endianness, the LSB might be the first byte to be
// compared. This means that when comparing unsigned(129) and unsigned(2)
// using memcmp(), the result would be that 2 > 129.
-// TODO: Do we want to enable this on big-endian systems?
+
+template <class _Tp>
+inline const bool __is_std_byte_v = false;
+
+#if _LIBCPP_STD_VER >= 17
+template <>
+inline const bool __is_std_byte_v<byte> = true;
+#endif
template <class _Tp, class _Up>
-struct __libcpp_is_trivially_lexicographically_comparable
- : integral_constant<bool,
- is_same<__remove_cv_t<_Tp>, __remove_cv_t<_Up> >::value && sizeof(_Tp) == 1 &&
- is_unsigned<_Tp>::value> {};
+inline const bool __is_trivially_lexicographically_comparable_v =
+ is_same<__remove_cv_t<_Tp>, __remove_cv_t<_Up> >::value &&
+#ifdef _LIBCPP_LITTLE_ENDIAN
+ sizeof(_Tp) == 1 &&
+#endif
+ (is_unsigned<_Tp>::value || __is_std_byte_v<_Tp>);
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/test/benchmarks/CMakeLists.txt b/libcxx/test/benchmarks/CMakeLists.txt
index d61367a367738..616cf0ff8d237 100644
--- a/libcxx/test/benchmarks/CMakeLists.txt
+++ b/libcxx/test/benchmarks/CMakeLists.txt
@@ -114,6 +114,7 @@ set(BENCHMARK_TESTS
algorithms/find.bench.cpp
algorithms/fill.bench.cpp
algorithms/for_each.bench.cpp
+ algorithms/lexicographical_compare.bench.cpp
algorithms/lower_bound.bench.cpp
algorithms/make_heap.bench.cpp
algorithms/make_heap_then_sort_heap.bench.cpp
diff --git a/libcxx/test/benchmarks/algorithms/lexicographical_compare.bench.cpp b/libcxx/test/benchmarks/algorithms/lexicographical_compare.bench.cpp
new file mode 100755
index 0000000000000..0c545263109d2
--- /dev/null
+++ b/libcxx/test/benchmarks/algorithms/lexicographical_compare.bench.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <algorithm>
+#include <benchmark/benchmark.h>
+#include <vector>
+
+// Benchmarks the worst case: check the whole range just to find out that they compare equal
+template <class T>
+static void bm_lexicographical_compare(benchmark::State& state) {
+ std::vector<T> vec1(state.range(), '1');
+ std::vector<T> vec2(state.range(), '1');
+
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(vec1);
+ benchmark::DoNotOptimize(vec2);
+ benchmark::DoNotOptimize(std::lexicographical_compare(vec1.begin(), vec1.end(), vec2.begin(), vec2.end()));
+ }
+}
+BENCHMARK(bm_lexicographical_compare<unsigned char>)->DenseRange(1, 8)->Range(16, 1 << 20);
+BENCHMARK(bm_lexicographical_compare<signed char>)->DenseRange(1, 8)->Range(16, 1 << 20);
+BENCHMARK(bm_lexicographical_compare<int>)->DenseRange(1, 8)->Range(16, 1 << 20);
+
+template <class T>
+static void bm_ranges_lexicographical_compare(benchmark::State& state) {
+ std::vector<T> vec1(state.range(), '1');
+ std::vector<T> vec2(state.range(), '1');
+
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(vec1);
+ benchmark::DoNotOptimize(vec2);
+ benchmark::DoNotOptimize(std::ranges::lexicographical_compare(vec1.begin(), vec1.end(), vec2.begin(), vec2.end()));
+ }
+}
+BENCHMARK(bm_ranges_lexicographical_compare<unsigned char>)->DenseRange(1, 8)->Range(16, 1 << 20);
+BENCHMARK(bm_ranges_lexicographical_compare<signed char>)->DenseRange(1, 8)->Range(16, 1 << 20);
+BENCHMARK(bm_ranges_lexicographical_compare<int>)->DenseRange(1, 8)->Range(16, 1 << 20);
+
+BENCHMARK_MAIN();
diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index 51e659f52000b..3bf39ea17c912 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -40,7 +40,9 @@ array algorithm
array compare
array concepts
array cstddef
+array cstdint
array cstdlib
+array cwchar
array initializer_list
array iterator
array limits
@@ -291,6 +293,7 @@ forward_list cstddef
forward_list cstdint
forward_list cstdlib
forward_list cstring
+forward_list cwchar
forward_list functional
forward_list initializer_list
forward_list iosfwd
@@ -449,6 +452,7 @@ list cstddef
list cstdint
list cstdlib
list cstring
+list cwchar
list functional
list initializer_list
list iosfwd
@@ -489,7 +493,9 @@ locale version
map compare
map concepts
map cstddef
+map cstdint
map cstdlib
+map cwchar
map functional
map initializer_list
map iterator
@@ -723,7 +729,9 @@ semaphore version
set compare
set concepts
set cstddef
+set cstdint
set cstdlib
+set cwchar
set functional
set initializer_list
set iterator
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index 17e85e982729c..49125486cfcf6 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -40,7 +40,9 @@ array algorithm
array compare
array concepts
array cstddef
+array cstdint
array cstdlib
+array cwchar
array initializer_list
array iterator
array limits
@@ -292,6 +294,7 @@ forward_list cstddef
forward_list cstdint
forward_list cstdlib
forward_list cstring
+forward_list cwchar
forward_list functional
forward_list initializer_list
forward_list iosfwd
@@ -452,6 +455,7 @@ list cstddef
list cstdint
list cstdlib
list cstring
+list cwchar
list functional
list initializer_list
list iosfwd
@@ -493,7 +497,9 @@ locale version
map compare
map concepts
map cstddef
+map cstdint
map cstdlib
+map cwchar
map functional
map initializer_list
map iterator
@@ -729,7 +735,9 @@ semaphore version
set compare
set concepts
set cstddef
+set cstdint
set cstdlib
+set cwchar
set functional
set initializer_list
set iterator
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index 8aed93da9e6cc..28dfb320fe06c 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -41,7 +41,9 @@ array algorithm
array compare
array concepts
array cstddef
+array cstdint
array cstdlib
+array cwchar
array initializer_list
array iterator
array limits
@@ -295,6 +297,7 @@ forward_list cstddef
forward_list cstdint
forward_list cstdlib
forward_list cstring
+forward_list cwchar
forward_list functional
forward_list initializer_list
forward_list iosfwd
@@ -455,6 +458,7 @@ list cstddef
list cstdint
list cstdlib
list cstring
+list cwchar
list functional
list initializer_list
list iosfwd
@@ -496,7 +500,9 @@ locale version
map compare
map concepts
map cstddef
+map cstdint
map cstdlib
+map cwchar
map functional
map initializer_list
map iterator
@@ -732,7 +738,9 @@ semaphore version
set compare
set concepts
set cstddef
+set cstdint
set cstdlib
+set cwchar
set functional
set initializer_list
set iterator
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 2c028462144ee..5b7b6cecf73f8 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -41,7 +41,9 @@ array algorithm
array compare
array concepts
array cstddef
+array cstdint
array cstdlib
+array cwchar
array initializer_list
array iterator
array limits
@@ -295,6 +297,7 @@ forward_list cstddef
forward_list cstdint
forward_list cstdlib
forward_list cstring
+forward_list cwchar
forward_list functional
forward_list initializer_list
forward_list iosfwd
@@ -455,6 +458,7 @@ list cstddef
list cstdint
list cstdlib
list cstring
+list cwchar
list functional
list initializer_list
list iosfwd
@@ -496,7 +500,9 @@ locale version
map compare
map concepts
map cstddef
+map cstdint
map cstdlib
+map cwchar
map functional
map initializer_list
map iterator
@@ -733,7 +739,9 @@ semaphore version
set compare
set concepts
set cstddef
+set cstdint
set cstdlib
+set cwchar
set functional
set initializer_list
set iterator
diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index 982c2013e3417..84ea6433fb12d 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -40,7 +40,9 @@ array algorithm
array compare
array concepts
array cstddef
+array cstdint
array cstdlib
+array cwchar
array initializer_list
array iterator
array limits
@@ -303,6 +305,7 @@ forward_list cstddef
forward_list cstdint
forward_list cstdlib
forward_list cstring
+forward_list cwchar
forward_list functional
forward_list initializer_list
forward_list iosfwd
@@ -462,6 +465,7 @@ list cstddef
list cstdint
list cstdlib
list cstring
+list cwchar
list functional
list initializer_list
list iosfwd
@@ -503,7 +507,9 @@ locale version
map compare
map concepts
map cstddef
+map cstdint
map cstdlib
+map cwchar
map functional
map initializer_list
map iterator
@@ -741,7 +747,9 @@ semaphore version
set compare
set concepts
set cstddef
+set cstdint
set cstdlib
+set cwchar
set functional
set initializer_list
set iterator
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 8ffb71d8b566b..f341fb2c29d33 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -23,6 +23,7 @@ any version
array compare
array cstddef
array cstdint
+array cwchar
array initializer_list
array limits
array new
@@ -199,6 +200,8 @@ format typeinfo
format version
forward_list compare
forward_list cstddef
+forward_list cstdint
+forward_list cwchar
forward_list initializer_list
forward_list limits
forward_list new
@@ -312,6 +315,7 @@ list compare
list cstddef
list cstdint
list cstring
+list cwchar
list initializer_list
list limits
list new
@@ -340,6 +344,7 @@ locale version
map compare
map cstddef
map cstdint
+map cwchar
map initializer_list
map limits
map new
@@ -498,6 +503,7 @@ semaphore version
set compare
set cstddef
set cstdint
+set cwchar
set initializer_list
set limits
set new
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index 8ffb71d8b566b..f341fb2c29d33 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -23,6 +23,7 @@ any version
array compare
array cstddef
array cstdint
+array cwchar
array initializer_list
array limits
array new
@@ -199,6 +200,8 @@ format typeinfo
format version
forward_list compare
forward_list cstddef
+forward_list cstdint
+forward_list cwchar
forward_list initializer_list
forward_list limits
forward_list new
@@ -312,6 +315,7 @@ list compare
list cstddef
list cstdint
list cstring
+list cwchar
list initializer_list
list limits
list new
@@ -340,6 +344,7 @@ locale version
map compare
map cstddef
map cstdint
+map cwchar
map initializer_list
map limits
map new
@@ -498,6 +503,7 @@ semaphore version
set compare
set cstddef
set cstdint
+set cwchar
set initializer_list
set limits
set new
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.lex.comparison/lexicographical_compare.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.lex.comparison/lexicographical_compare.pass.cpp
index 10e45df7cf9aa..2cf675476026c 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.lex.comparison/lexicographical_compare.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.lex.comparison/lexicographical_compare.pass.cpp
@@ -20,66 +20,48 @@
#include "test_macros.h"
#include "test_iterators.h"
-#if TEST_STD_VER > 17
-TEST_CONSTEXPR bool test_constexpr() {
- int ia[] = {1, 2, 3};
- int ib[] = {1, 3, 5, 2, 4, 6};
+template <class T, class Iter1>
+struct Test {
+ template <class Iter2>
+ TEST_CONSTEXPR_CXX20 void operator()() {
+ T ia[] = {1, 2, 3, 4};
+ const unsigned sa = sizeof(ia) / sizeof(ia[0]);
+ T ib[] = {1, 2, 3};
+ assert(!std::lexicographical_compare(Iter1(ia), Iter1(ia + sa), Iter2(ib), Iter2(ib + 2)));
+ assert(std::lexicographical_compare(Iter1(ib), Iter1(ib + 2), Iter2(ia), Iter2(ia + sa)));
+ assert(!std::lexicographical_compare(Iter1(ia), Iter1(ia + sa), Iter2(ib), Iter2(ib + 3)));
+ assert(std::lexicographical_compare(Iter1(ib), Iter1(ib + 3), Iter2(ia), Iter2(ia + sa)));
+ assert(std::lexicographical_compare(Iter1(ia), Iter1(ia + sa), Iter2(ib + 1), Iter2(ib + 3)));
+ assert(!std::lexicographical_compare(Iter1(ib + 1), Iter1(ib + 3), Iter2(ia), Iter2(ia + sa)));
+ }
+};
- return std::lexicographical_compare(std::begin(ia), std::end(ia), std::begin(ib), std::end(ib))
- && !std::lexicographical_compare(std::begin(ib), std::end(ib), std::begin(ia), std::end(ia))
- ;
- }
-#endif
-
-template <class Iter1, class Iter2>
-void
-test()
-{
- int ia[] = {1, 2, 3, 4};
- const unsigned sa = sizeof(ia)/sizeof(ia[0]);
- int ib[] = {1, 2, 3};
- assert(!std::lexicographical_compare(Iter1(ia), Iter1(ia+sa), Iter2(ib), Iter2(ib+2)));
- assert( std::lexicographical_compare(Iter1(ib), Iter1(ib+2), Iter2(ia), Iter2(ia+sa)));
- assert(!std::lexicographical_compare(Iter1(ia), Iter1(ia+sa), Iter2(ib), Iter2(ib+3)));
- assert( std::lexicographical_compare(Iter1(ib), Iter1(ib+3), Iter2(ia), Iter2(ia+sa)));
- assert( std::lexicographical_compare(Iter1(ia), Iter1(ia+sa), Iter2(ib+1), Iter2(ib+3)));
- assert(!std::lexicographical_compare(Iter1(ib+1), Iter1(ib+3), Iter2(ia), Iter2(ia+sa)));
-}
+template <class T>
+struct TestIter {
+ template <class Iter1>
+ TEST_CONSTEXPR_CXX20 bool operator()() {
+ types::for_each(types::cpp17_input_iterator_list<T*>(), Test<T, Iter1>());
-int main(int, char**)
-{
- test<cpp17_input_iterator<const int*>, cpp17_input_iterator<const int*> >();
- test<cpp17_input_iterator<const int*>, forward_iterator<const int*> >();
- test<cpp17_input_iterator<const int*>, bidirectional_iterator<const int*> >();
- test<cpp17_input_iterator<const int*>, random_access_iterator<const int*> >();
- test<cpp17_input_iterator<const int*>, const int*>();
+ return true;
+ }
+};
- test<forward_iterator<const int*>, cpp17_input_iterator<const int*> >();
- test<forward_iterator<const int*>, forward_iterator<const int*> >();
- test<forward_iterator<const int*>, bidirectional_iterator<const int*> >();
- test<forward_iterator<const int*>, random_access_iterator<const int*> >();
- test<forward_iterator<const int*>, const int*>();
-
- test<bidirectional_iterator<const int*>, cpp17_input_iterator<const int*> >();
- test<bidirectional_iterator<const int*>, forward_iterator<const int*> >();
- test<bidirectional_iterator<const int*>, bidirectional_iterator<const int*> >();
- test<bidirectional_iterator<const int*>, random_access_iterator<const int*> >();
- test<bidirectional_iterator<const int*>, const int*>();
+TEST_CONSTEXPR_CXX20 bool test() {
+ types::for_each(types::cpp17_input_iterator_list<const int*>(), TestIter<const int>());
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+ types::for_each(types::cpp17_input_iterator_list<const wchar_t*>(), TestIter<const wchar_t>());
+#endif
+ types::for_each(types::cpp17_input_iterator_list<const char*>(), TestIter<const char>());
+ types::for_each(types::cpp17_input_iterator_list<unsigned char*>(), TestIter<unsigned char>());
- test<random_access_iterator<const int*>, cpp17_input_iterator<const int*> >();
- test<random_access_iterator<const int*>, forward_iterator<const int*> >();
- test<random_access_iterator<const int*>, bidirectional_iterator<const int*> >();
- test<random_access_iterator<const int*>, random_access_iterator<const int*> >();
- test<random_access_iterator<const int*>, const int*>();
+ return true;
+}
- test<const int*, cpp17_input_iterator<const int*> >();
- test<const int*, forward_iterator<const int*> >();
- test<const int*, bidirectional_iterator<const int*> >();
- test<const int*, random_access_iterator<const int*> >();
- test<const int*, const int*>();
+int main(int, char**) {
+ test();
-#if TEST_STD_VER > 17
- static_assert(test_constexpr());
+#if TEST_STD_VER >= 20
+ static_assert(test());
#endif
return 0;
diff --git a/libcxx/test/std/containers/sequences/array/compare.verify.cpp b/libcxx/test/std/containers/sequences/array/compare.verify.cpp
index e03f001469344..4b001601a4fe2 100644
--- a/libcxx/test/std/containers/sequences/array/compare.verify.cpp
+++ b/libcxx/test/std/containers/sequences/array/compare.verify.cpp
@@ -28,10 +28,6 @@
template <int>
struct NoCompare {};
-#if TEST_STD_VER >= 14 && TEST_STD_VER <= 17
-// expected-error@*:* 3 {{no matching function for call to object of type 'std::__less<void, void>'}}
-#endif
-
int main(int, char**) {
{
typedef NoCompare<0> T;
More information about the libcxx-commits
mailing list