Skip to content

Commit 32307b6

Browse files
committed
[libc++] Optimize make_heap
1 parent 250d251 commit 32307b6

File tree

5 files changed

+38
-25
lines changed

5 files changed

+38
-25
lines changed

libcxx/include/__algorithm/make_heap.h

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@
1212
#include <__algorithm/comp.h>
1313
#include <__algorithm/comp_ref_type.h>
1414
#include <__algorithm/iterator_operations.h>
15+
#include <__algorithm/push_heap.h>
1516
#include <__algorithm/sift_down.h>
1617
#include <__config>
1718
#include <__iterator/iterator_traits.h>
19+
#include <__type_traits/is_arithmetic.h>
1820
#include <__utility/move.h>
1921

2022
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -31,13 +33,23 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void
3133
__make_heap(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare&& __comp) {
3234
__comp_ref_type<_Compare> __comp_ref = __comp;
3335

34-
using difference_type = typename iterator_traits<_RandomAccessIterator>::difference_type;
35-
difference_type __n = __last - __first;
36+
using __diff_t = __iter_diff_t<_RandomAccessIterator>;
37+
const __diff_t __n = __last - __first;
38+
39+
static const bool __assume_both_children = is_arithmetic<__iter_value_type<_RandomAccessIterator> >::value;
40+
41+
// While it would be correct to always assume we have both children, in practice we observed this to be a performance
42+
// improvement only for arithmetic types.
43+
const __diff_t __sift_down_n = __assume_both_children ? ((__n & 1) ? __n : __n - 1) : __n;
44+
3645
if (__n > 1) {
3746
// start from the first parent, there is no need to consider children
38-
for (difference_type __start = (__n - 2) / 2; __start >= 0; --__start) {
39-
std::__sift_down<_AlgPolicy>(__first, __comp_ref, __n, __first + __start);
47+
48+
for (__diff_t __start = (__sift_down_n - 2) / 2; __start >= 0; --__start) {
49+
std::__sift_down<_AlgPolicy, __assume_both_children>(__first, __comp_ref, __sift_down_n, __start);
4050
}
51+
if _LIBCPP_CONSTEXPR (__assume_both_children)
52+
std::__sift_up<_AlgPolicy>(__first, __last, __comp, __n);
4153
}
4254
}
4355

libcxx/include/__algorithm/partial_sort.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _RandomAccessIterator __part
4545
for (; __i != __last; ++__i) {
4646
if (__comp(*__i, *__first)) {
4747
_IterOps<_AlgPolicy>::iter_swap(__i, __first);
48-
std::__sift_down<_AlgPolicy>(__first, __comp, __len, __first);
48+
std::__sift_down<_AlgPolicy, false>(__first, __comp, __len, 0);
4949
}
5050
}
5151
std::__sort_heap<_AlgPolicy>(std::move(__first), std::move(__middle), __comp);

libcxx/include/__algorithm/partial_sort_copy.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair<_InputIterator, _Random
6060
for (; __first != __last; ++__first)
6161
if (std::__invoke(__comp, std::__invoke(__proj1, *__first), std::__invoke(__proj2, *__result_first))) {
6262
*__result_first = *__first;
63-
std::__sift_down<_AlgPolicy>(__result_first, __projected_comp, __len, __result_first);
63+
std::__sift_down<_AlgPolicy, false>(__result_first, __projected_comp, __len, 0);
6464
}
6565
std::__sort_heap<_AlgPolicy>(__result_first, __r, __projected_comp);
6666
}

libcxx/include/__algorithm/sift_down.h

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,59 +24,60 @@ _LIBCPP_PUSH_MACROS
2424

2525
_LIBCPP_BEGIN_NAMESPACE_STD
2626

27-
template <class _AlgPolicy, class _Compare, class _RandomAccessIterator>
27+
template <class _AlgPolicy, bool __assume_both_children, class _Compare, class _RandomAccessIterator>
2828
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void
2929
__sift_down(_RandomAccessIterator __first,
3030
_Compare&& __comp,
31-
typename iterator_traits<_RandomAccessIterator>::difference_type __len,
32-
_RandomAccessIterator __start) {
31+
__iter_diff_t<_RandomAccessIterator> __len,
32+
__iter_diff_t<_RandomAccessIterator> __start) {
3333
using _Ops = _IterOps<_AlgPolicy>;
3434

3535
typedef typename iterator_traits<_RandomAccessIterator>::difference_type difference_type;
3636
typedef typename iterator_traits<_RandomAccessIterator>::value_type value_type;
3737
// left-child of __start is at 2 * __start + 1
3838
// right-child of __start is at 2 * __start + 2
39-
difference_type __child = __start - __first;
39+
difference_type __child = __start;
4040

4141
if (__len < 2 || (__len - 2) / 2 < __child)
4242
return;
4343

44-
__child = 2 * __child + 1;
45-
_RandomAccessIterator __child_i = __first + __child;
44+
__child = 2 * __child + 1;
4645

47-
if ((__child + 1) < __len && __comp(*__child_i, *(__child_i + difference_type(1)))) {
46+
if _LIBCPP_CONSTEXPR (__assume_both_children) {
47+
// right-child exists and is greater than left-child
48+
__child += __comp(__first[__child], __first[__child + 1]);
49+
} else if ((__child + 1) < __len && __comp(__first[__child], __first[__child + 1])) {
4850
// right-child exists and is greater than left-child
49-
++__child_i;
5051
++__child;
5152
}
5253

5354
// check if we are in heap-order
54-
if (__comp(*__child_i, *__start))
55+
if (__comp(__first[__child], __first[__start]))
5556
// we are, __start is larger than its largest child
5657
return;
5758

58-
value_type __top(_Ops::__iter_move(__start));
59+
value_type __top(_Ops::__iter_move(__first + __start));
5960
do {
6061
// we are not in heap-order, swap the parent with its largest child
61-
*__start = _Ops::__iter_move(__child_i);
62-
__start = __child_i;
62+
__first[__start] = _Ops::__iter_move(__first + __child);
63+
__start = __child;
6364

6465
if ((__len - 2) / 2 < __child)
6566
break;
6667

6768
// recompute the child based off of the updated parent
68-
__child = 2 * __child + 1;
69-
__child_i = __first + __child;
69+
__child = 2 * __child + 1;
7070

71-
if ((__child + 1) < __len && __comp(*__child_i, *(__child_i + difference_type(1)))) {
71+
if _LIBCPP_CONSTEXPR (__assume_both_children) {
72+
__child += __comp(__first[__child], __first[__child + 1]);
73+
} else if ((__child + 1) < __len && __comp(__first[__child], __first[__child + 1])) {
7274
// right-child exists and is greater than left-child
73-
++__child_i;
7475
++__child;
7576
}
7677

7778
// check if we are in heap-order
78-
} while (!__comp(*__child_i, __top));
79-
*__start = std::move(__top);
79+
} while (!__comp(__first[__child], __top));
80+
__first[__start] = std::move(__top);
8081
}
8182

8283
template <class _AlgPolicy, class _Compare, class _RandomAccessIterator>

libcxx/test/libcxx/algorithms/bad_iterator_traits.verify.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,5 +57,5 @@ struct BadIter {
5757
// behavior when these iterators are passed to standard algorithms.
5858
void test() {
5959
std::sort(BadIter(), BadIter());
60-
//expected-error-re@*:* {{static assertion failed {{.*}}It looks like your iterator's `iterator_traits<It>::reference` does not match the return type of dereferencing the iterator}}
60+
//expected-error-re@*:* 2 {{static assertion failed {{.*}}It looks like your iterator's `iterator_traits<It>::reference` does not match the return type of dereferencing the iterator}}
6161
}

0 commit comments

Comments
 (0)