Skip to content

Commit d28be44

Browse files
committed
[libc++] Optimize lexicographical_compare
1 parent b6d2460 commit d28be44

File tree

10 files changed

+162
-93
lines changed

10 files changed

+162
-93
lines changed

libcxx/benchmarks/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ set(BENCHMARK_TESTS
155155
algorithms.partition_point.bench.cpp
156156
algorithms/equal.bench.cpp
157157
algorithms/find.bench.cpp
158+
algorithms/lexicographical_compare.bench.cpp
158159
algorithms/lower_bound.bench.cpp
159160
algorithms/make_heap.bench.cpp
160161
algorithms/make_heap_then_sort_heap.bench.cpp
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include <algorithm>
10+
#include <benchmark/benchmark.h>
11+
#include <vector>
12+
13+
template <class T>
14+
static void bm_lexicographical_compare(benchmark::State& state) {
15+
std::vector<T> vec1(state.range(), '1');
16+
std::vector<T> vec2(state.range(), '1');
17+
18+
for (auto _ : state) {
19+
benchmark::DoNotOptimize(vec1);
20+
benchmark::DoNotOptimize(vec2);
21+
benchmark::DoNotOptimize(std::lexicographical_compare(vec1.begin(), vec1.end(), vec2.begin(), vec2.end()));
22+
}
23+
}
24+
BENCHMARK(bm_lexicographical_compare<unsigned char>)->DenseRange(1, 8)->Range(16, 1 << 20);
25+
BENCHMARK(bm_lexicographical_compare<int>)->DenseRange(1, 8)->Range(16, 1 << 20);
26+
27+
template <class T>
28+
static void bm_ranges_lexicographical_compare(benchmark::State& state) {
29+
std::vector<T> vec1(state.range(), '1');
30+
std::vector<T> vec2(state.range(), '1');
31+
32+
for (auto _ : state) {
33+
benchmark::DoNotOptimize(vec1);
34+
benchmark::DoNotOptimize(vec2);
35+
benchmark::DoNotOptimize(std::ranges::lexicographical_compare(vec1.begin(), vec1.end(), vec2.begin(), vec2.end()));
36+
}
37+
}
38+
BENCHMARK(bm_ranges_lexicographical_compare<unsigned char>)->DenseRange(1, 8)->Range(16, 1 << 20);
39+
BENCHMARK(bm_ranges_lexicographical_compare<int>)->DenseRange(1, 8)->Range(16, 1 << 20);
40+
41+
BENCHMARK_MAIN();

libcxx/include/__algorithm/comp.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ struct __less<void, void> {
4242
}
4343
};
4444

45+
template <class _Lhs, class _Rhs>
46+
struct __is_trivial_less_than_predicate<__less<>, _Lhs, _Rhs> : true_type {};
47+
4548
_LIBCPP_END_NAMESPACE_STD
4649

4750
#endif // _LIBCPP___ALGORITHM_COMP_H

libcxx/include/__algorithm/lexicographical_compare.h

Lines changed: 55 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,50 +11,79 @@
1111

1212
#include <__algorithm/comp.h>
1313
#include <__algorithm/comp_ref_type.h>
14+
#include <__algorithm/min.h>
15+
#include <__algorithm/unwrap_iter.h>
1416
#include <__config>
17+
#include <__functional/identity.h>
1518
#include <__iterator/iterator_traits.h>
19+
#include <__string/constexpr_c_functions.h>
20+
#include <__type_traits/invoke.h>
21+
#include <__type_traits/is_trivially_lexicographically_comparable.h>
22+
#include <__type_traits/predicate_traits.h>
1623

1724
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
1825
# pragma GCC system_header
1926
#endif
2027

28+
_LIBCPP_PUSH_MACROS
29+
#include <__undef_macros>
30+
2131
_LIBCPP_BEGIN_NAMESPACE_STD
2232

23-
template <class _Compare, class _InputIterator1, class _InputIterator2>
33+
template <class _Iter1, class _Sent1, class _Iter2, class _Sent2, class _Proj1, class _Proj2, class _Comp>
34+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __lexicographical_compare(
35+
_Iter1 __first1, _Sent1 __last1, _Iter2 __first2, _Sent2 __last2, _Comp& __comp, _Proj1& __proj1, _Proj2& __proj2) {
36+
while (__first2 != __last2) {
37+
if (__first1 == __last1 ||
38+
std::__invoke(__comp, std::__invoke(__proj1, *__first1), std::__invoke(__proj2, *__first2)))
39+
return true;
40+
if (std::__invoke(__comp, std::__invoke(__proj2, *__first2), std::__invoke(__proj1, *__first1)))
41+
return false;
42+
++__first1;
43+
++__first2;
44+
}
45+
return false;
46+
}
47+
48+
#if _LIBCPP_STD_VER >= 20
49+
template <class _Tp, class _Up, class _Proj1, class _Proj2, class _Comp>
50+
requires(__libcpp_is_trivially_lexicographically_comparable<_Tp, _Up>::value && __is_identity<_Proj1>::value &&
51+
__is_identity<_Proj2>::value && __is_trivial_less_than_predicate<_Comp, _Tp, _Up>::value)
2452
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
25-
__lexicographical_compare(_InputIterator1 __first1, _InputIterator1 __last1,
26-
_InputIterator2 __first2, _InputIterator2 __last2, _Compare __comp)
27-
{
28-
for (; __first2 != __last2; ++__first1, (void) ++__first2)
29-
{
30-
if (__first1 == __last1 || __comp(*__first1, *__first2))
31-
return true;
32-
if (__comp(*__first2, *__first1))
33-
return false;
34-
}
35-
return false;
53+
__lexicographical_compare(_Tp* __first1, _Tp* __last1, _Up* __first2, _Up* __last2, _Comp&, _Proj1&, _Proj2&) {
54+
if (auto __res = std::__constexpr_memcmp(
55+
__first1, __first2, __element_count(std::min(__last1 - __first1, __last2 - __first2))))
56+
return __res < 0;
57+
return __last1 - __first1 < __last2 - __first2;
3658
}
59+
#endif
3760

3861
template <class _InputIterator1, class _InputIterator2, class _Compare>
39-
_LIBCPP_NODISCARD_EXT inline
40-
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20
41-
bool
42-
lexicographical_compare(_InputIterator1 __first1, _InputIterator1 __last1,
43-
_InputIterator2 __first2, _InputIterator2 __last2, _Compare __comp)
44-
{
45-
return _VSTD::__lexicographical_compare<__comp_ref_type<_Compare> >(__first1, __last1, __first2, __last2, __comp);
62+
_LIBCPP_NODISCARD_EXT inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool lexicographical_compare(
63+
_InputIterator1 __first1,
64+
_InputIterator1 __last1,
65+
_InputIterator2 __first2,
66+
_InputIterator2 __last2,
67+
_Compare __comp) {
68+
__identity __proj;
69+
return std::__lexicographical_compare(
70+
std::__unwrap_iter(__first1),
71+
std::__unwrap_iter(__last1),
72+
std::__unwrap_iter(__first2),
73+
std::__unwrap_iter(__last2),
74+
__comp,
75+
__proj,
76+
__proj);
4677
}
4778

4879
template <class _InputIterator1, class _InputIterator2>
49-
_LIBCPP_NODISCARD_EXT inline
50-
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20
51-
bool
52-
lexicographical_compare(_InputIterator1 __first1, _InputIterator1 __last1,
53-
_InputIterator2 __first2, _InputIterator2 __last2)
54-
{
55-
return _VSTD::lexicographical_compare(__first1, __last1, __first2, __last2, __less<>());
80+
_LIBCPP_NODISCARD_EXT inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20 bool lexicographical_compare(
81+
_InputIterator1 __first1, _InputIterator1 __last1, _InputIterator2 __first2, _InputIterator2 __last2) {
82+
return std::lexicographical_compare(__first1, __last1, __first2, __last2, __less<>());
5683
}
5784

5885
_LIBCPP_END_NAMESPACE_STD
5986

87+
_LIBCPP_POP_MACROS
88+
6089
#endif // _LIBCPP___ALGORITHM_LEXICOGRAPHICAL_COMPARE_H

libcxx/include/__algorithm/ranges_lexicographical_compare.h

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
#ifndef _LIBCPP___ALGORITHM_RANGES_LEXICOGRAPHICAL_COMPARE_H
1010
#define _LIBCPP___ALGORITHM_RANGES_LEXICOGRAPHICAL_COMPARE_H
1111

12+
#include <__algorithm/lexicographical_compare.h>
13+
#include <__algorithm/unwrap_range.h>
1214
#include <__config>
1315
#include <__functional/identity.h>
1416
#include <__functional/invoke.h>
@@ -31,23 +33,24 @@ namespace ranges {
3133
namespace __lexicographical_compare {
3234
struct __fn {
3335
template <class _Iter1, class _Sent1, class _Iter2, class _Sent2, class _Proj1, class _Proj2, class _Comp>
34-
_LIBCPP_HIDE_FROM_ABI constexpr static bool __lexicographical_compare_impl(
36+
static _LIBCPP_HIDE_FROM_ABI constexpr bool __lexicographical_compare_unwrap(
3537
_Iter1 __first1,
3638
_Sent1 __last1,
3739
_Iter2 __first2,
3840
_Sent2 __last2,
3941
_Comp& __comp,
4042
_Proj1& __proj1,
4143
_Proj2& __proj2) {
42-
while (__first2 != __last2) {
43-
if (__first1 == __last1 || std::invoke(__comp, std::invoke(__proj1, *__first1), std::invoke(__proj2, *__first2)))
44-
return true;
45-
if (std::invoke(__comp, std::invoke(__proj2, *__first2), std::invoke(__proj1, *__first1)))
46-
return false;
47-
++__first1;
48-
++__first2;
49-
}
50-
return false;
44+
auto [__first1_un, __last1_un] = std::__unwrap_range(std::move(__first1), std::move(__last1));
45+
auto [__first2_un, __last2_un] = std::__unwrap_range(std::move(__first2), std::move(__last2));
46+
return std::__lexicographical_compare(
47+
std::move(__first1_un),
48+
std::move(__last1_un),
49+
std::move(__first2_un),
50+
std::move(__last2_un),
51+
__comp,
52+
__proj1,
53+
__proj2);
5154
}
5255

5356
template <input_iterator _Iter1,
@@ -65,7 +68,7 @@ struct __fn {
6568
_Comp __comp = {},
6669
_Proj1 __proj1 = {},
6770
_Proj2 __proj2 = {}) const {
68-
return __lexicographical_compare_impl(
71+
return __lexicographical_compare_unwrap(
6972
std::move(__first1), std::move(__last1), std::move(__first2), std::move(__last2), __comp, __proj1, __proj2);
7073
}
7174

@@ -77,7 +80,7 @@ struct __fn {
7780
_Comp = ranges::less>
7881
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr bool operator()(
7982
_Range1&& __range1, _Range2&& __range2, _Comp __comp = {}, _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const {
80-
return __lexicographical_compare_impl(
83+
return __lexicographical_compare_unrwap(
8184
ranges::begin(__range1),
8285
ranges::end(__range1),
8386
ranges::begin(__range2),

libcxx/include/__functional/operations.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,14 @@ struct _LIBCPP_TEMPLATE_VIS less<void>
418418
};
419419
#endif
420420

421+
template <class _Tp>
422+
struct __is_trivial_less_than_predicate<less<_Tp>, _Tp, _Tp> : true_type {};
423+
424+
#if _LIBCPP_STD_VER >= 14
425+
template <class _Tp, class _Up>
426+
struct __is_trivial_less_than_predicate<less<>, _Tp, _Up> : true_type {};
427+
#endif
428+
421429
#if _LIBCPP_STD_VER >= 14
422430
template <class _Tp = void>
423431
#else

libcxx/include/__functional/ranges_operations.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ struct greater_equal {
9898
template <class _Lhs, class _Rhs>
9999
struct __is_trivial_equality_predicate<ranges::equal_to, _Lhs, _Rhs> : true_type {};
100100

101+
template <class _Lhs, class _Rhs>
102+
struct __is_trivial_less_than_predicate<ranges::less, _Lhs, _Rhs> : true_type {};
103+
101104
#endif // _LIBCPP_STD_VER >= 20
102105

103106
_LIBCPP_END_NAMESPACE_STD

libcxx/include/__type_traits/predicate_traits.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ _LIBCPP_BEGIN_NAMESPACE_STD
2121
template <class _Pred, class _Lhs, class _Rhs>
2222
struct __is_trivial_equality_predicate : false_type {};
2323

24+
template <class _Pred, class _Lhs, class _Rhs>
25+
struct __is_trivial_less_than_predicate : false_type {};
26+
2427
_LIBCPP_END_NAMESPACE_STD
2528

2629
#endif // _LIBCPP___TYPE_TRAITS_PREDICATE_TRAITS

libcxx/test/std/algorithms/alg.sorting/alg.lex.comparison/lexicographical_compare.pass.cpp

Lines changed: 33 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -20,66 +20,45 @@
2020
#include "test_macros.h"
2121
#include "test_iterators.h"
2222

23-
#if TEST_STD_VER > 17
24-
TEST_CONSTEXPR bool test_constexpr() {
25-
int ia[] = {1, 2, 3};
26-
int ib[] = {1, 3, 5, 2, 4, 6};
23+
template <class T, class Iter1>
24+
struct Test {
25+
template <class Iter2>
26+
TEST_CONSTEXPR_CXX20 void operator()() {
27+
T ia[] = {1, 2, 3, 4};
28+
const unsigned sa = sizeof(ia) / sizeof(ia[0]);
29+
T ib[] = {1, 2, 3};
30+
assert(!std::lexicographical_compare(Iter1(ia), Iter1(ia + sa), Iter2(ib), Iter2(ib + 2)));
31+
assert(std::lexicographical_compare(Iter1(ib), Iter1(ib + 2), Iter2(ia), Iter2(ia + sa)));
32+
assert(!std::lexicographical_compare(Iter1(ia), Iter1(ia + sa), Iter2(ib), Iter2(ib + 3)));
33+
assert(std::lexicographical_compare(Iter1(ib), Iter1(ib + 3), Iter2(ia), Iter2(ia + sa)));
34+
assert(std::lexicographical_compare(Iter1(ia), Iter1(ia + sa), Iter2(ib + 1), Iter2(ib + 3)));
35+
assert(!std::lexicographical_compare(Iter1(ib + 1), Iter1(ib + 3), Iter2(ia), Iter2(ia + sa)));
36+
}
37+
};
2738

28-
return std::lexicographical_compare(std::begin(ia), std::end(ia), std::begin(ib), std::end(ib))
29-
&& !std::lexicographical_compare(std::begin(ib), std::end(ib), std::begin(ia), std::end(ia))
30-
;
31-
}
32-
#endif
33-
34-
template <class Iter1, class Iter2>
35-
void
36-
test()
37-
{
38-
int ia[] = {1, 2, 3, 4};
39-
const unsigned sa = sizeof(ia)/sizeof(ia[0]);
40-
int ib[] = {1, 2, 3};
41-
assert(!std::lexicographical_compare(Iter1(ia), Iter1(ia+sa), Iter2(ib), Iter2(ib+2)));
42-
assert( std::lexicographical_compare(Iter1(ib), Iter1(ib+2), Iter2(ia), Iter2(ia+sa)));
43-
assert(!std::lexicographical_compare(Iter1(ia), Iter1(ia+sa), Iter2(ib), Iter2(ib+3)));
44-
assert( std::lexicographical_compare(Iter1(ib), Iter1(ib+3), Iter2(ia), Iter2(ia+sa)));
45-
assert( std::lexicographical_compare(Iter1(ia), Iter1(ia+sa), Iter2(ib+1), Iter2(ib+3)));
46-
assert(!std::lexicographical_compare(Iter1(ib+1), Iter1(ib+3), Iter2(ia), Iter2(ia+sa)));
47-
}
39+
template <class T>
40+
struct TestIter {
41+
template <class Iter1>
42+
TEST_CONSTEXPR_CXX20 bool operator()() {
43+
types::for_each(types::cpp17_input_iterator_list<T*>(), Test<T, Iter1>());
4844

49-
int main(int, char**)
50-
{
51-
test<cpp17_input_iterator<const int*>, cpp17_input_iterator<const int*> >();
52-
test<cpp17_input_iterator<const int*>, forward_iterator<const int*> >();
53-
test<cpp17_input_iterator<const int*>, bidirectional_iterator<const int*> >();
54-
test<cpp17_input_iterator<const int*>, random_access_iterator<const int*> >();
55-
test<cpp17_input_iterator<const int*>, const int*>();
45+
return true;
46+
}
47+
};
5648

57-
test<forward_iterator<const int*>, cpp17_input_iterator<const int*> >();
58-
test<forward_iterator<const int*>, forward_iterator<const int*> >();
59-
test<forward_iterator<const int*>, bidirectional_iterator<const int*> >();
60-
test<forward_iterator<const int*>, random_access_iterator<const int*> >();
61-
test<forward_iterator<const int*>, const int*>();
49+
TEST_CONSTEXPR_CXX20 bool test() {
50+
types::for_each(types::cpp17_input_iterator_list<const int*>(), TestIter<const int>());
51+
types::for_each(types::cpp17_input_iterator_list<const char*>(), TestIter<const char>());
52+
types::for_each(types::cpp17_input_iterator_list<unsigned char*>(), TestIter<unsigned char>());
6253

63-
test<bidirectional_iterator<const int*>, cpp17_input_iterator<const int*> >();
64-
test<bidirectional_iterator<const int*>, forward_iterator<const int*> >();
65-
test<bidirectional_iterator<const int*>, bidirectional_iterator<const int*> >();
66-
test<bidirectional_iterator<const int*>, random_access_iterator<const int*> >();
67-
test<bidirectional_iterator<const int*>, const int*>();
68-
69-
test<random_access_iterator<const int*>, cpp17_input_iterator<const int*> >();
70-
test<random_access_iterator<const int*>, forward_iterator<const int*> >();
71-
test<random_access_iterator<const int*>, bidirectional_iterator<const int*> >();
72-
test<random_access_iterator<const int*>, random_access_iterator<const int*> >();
73-
test<random_access_iterator<const int*>, const int*>();
54+
return true;
55+
}
7456

75-
test<const int*, cpp17_input_iterator<const int*> >();
76-
test<const int*, forward_iterator<const int*> >();
77-
test<const int*, bidirectional_iterator<const int*> >();
78-
test<const int*, random_access_iterator<const int*> >();
79-
test<const int*, const int*>();
57+
int main(int, char**) {
58+
test();
8059

81-
#if TEST_STD_VER > 17
82-
static_assert(test_constexpr());
60+
#if TEST_STD_VER >= 20
61+
static_assert(test());
8362
#endif
8463

8564
return 0;

libcxx/utils/data/ignore_format.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ libcxx/include/__algorithm/is_sorted.h
3131
libcxx/include/__algorithm/is_sorted_until.h
3232
libcxx/include/__algorithm/iterator_operations.h
3333
libcxx/include/__algorithm/iter_swap.h
34-
libcxx/include/__algorithm/lexicographical_compare.h
3534
libcxx/include/__algorithm/lower_bound.h
3635
libcxx/include/__algorithm/make_heap.h
3736
libcxx/include/__algorithm/make_projected.h

0 commit comments

Comments
 (0)