Skip to content

Commit 62a191c

Browse files
committed
[libc++] Optimize ranges::minmax
1 parent 2684a09 commit 62a191c

File tree

8 files changed

+99
-1
lines changed

8 files changed

+99
-1
lines changed

libcxx/benchmarks/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ set(BENCHMARK_TESTS
182182
algorithms/make_heap.bench.cpp
183183
algorithms/make_heap_then_sort_heap.bench.cpp
184184
algorithms/min.bench.cpp
185+
algorithms/minmax.bench.cpp
185186
algorithms/min_max_element.bench.cpp
186187
algorithms/mismatch.bench.cpp
187188
algorithms/pop_heap.bench.cpp
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#include <algorithm>
2+
#include <cassert>
3+
4+
#include <benchmark/benchmark.h>
5+
6+
void run_sizes(auto benchmark) {
7+
benchmark->Arg(1)
8+
->Arg(2)
9+
->Arg(3)
10+
->Arg(4)
11+
->Arg(5)
12+
->Arg(6)
13+
->Arg(7)
14+
->Arg(8)
15+
->Arg(9)
16+
->Arg(10)
17+
->Arg(11)
18+
->Arg(12)
19+
->Arg(13)
20+
->Arg(14)
21+
->Arg(15)
22+
->Arg(16)
23+
->Arg(17)
24+
->Arg(18)
25+
->Arg(19)
26+
->Arg(20)
27+
->Arg(21)
28+
->Arg(22)
29+
->Arg(23)
30+
->Arg(24)
31+
->Arg(25)
32+
->Arg(26)
33+
->Arg(27)
34+
->Arg(28)
35+
->Arg(29)
36+
->Arg(30)
37+
->Arg(31)
38+
->Arg(32)
39+
->Arg(64)
40+
->Arg(512)
41+
->Arg(1024)
42+
->Arg(4000)
43+
->Arg(4096)
44+
->Arg(5500)
45+
->Arg(64000)
46+
->Arg(65536)
47+
->Arg(70000);
48+
}
49+
50+
template <class T>
51+
static void BM_std_minmax(benchmark::State& state) {
52+
std::vector<T> vec(state.range(), 3);
53+
54+
for (auto _ : state) {
55+
benchmark::DoNotOptimize(vec);
56+
benchmark::DoNotOptimize(std::ranges::minmax(vec));
57+
}
58+
}
59+
BENCHMARK(BM_std_minmax<char>)->Apply(run_sizes);
60+
BENCHMARK(BM_std_minmax<short>)->Apply(run_sizes);
61+
BENCHMARK(BM_std_minmax<int>)->Apply(run_sizes);
62+
BENCHMARK(BM_std_minmax<long long>)->Apply(run_sizes);
63+
BENCHMARK(BM_std_minmax<unsigned char>)->Apply(run_sizes);
64+
BENCHMARK(BM_std_minmax<unsigned short>)->Apply(run_sizes);
65+
BENCHMARK(BM_std_minmax<unsigned int>)->Apply(run_sizes);
66+
BENCHMARK(BM_std_minmax<unsigned long long>)->Apply(run_sizes);
67+
68+
BENCHMARK_MAIN();

libcxx/docs/ReleaseNotes/19.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ Improvements and New Features
5353
resulting in a performance increase of up to 1400x.
5454
- The ``std::mismatch`` algorithm has been optimized for integral types, which can lead up to 40x performance
5555
improvements.
56+
- The ``std::ranges::minmax`` algorithm has been optimized for integral types, resulting in a performance increase of
57+
up to 100x.
5658

5759
Deprecations and Removals
5860
-------------------------

libcxx/include/__algorithm/comp.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include <__config>
1313
#include <__type_traits/integral_constant.h>
14+
#include <__type_traits/is_integral.h>
1415
#include <__type_traits/operation_traits.h>
1516

1617
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -42,6 +43,9 @@ struct __less<void, void> {
4243
}
4344
};
4445

46+
template <class _Tp>
47+
struct __desugars_to<__totally_ordered_less_tag, __less<>, _Tp, _Tp> : is_integral<_Tp> {};
48+
4549
_LIBCPP_END_NAMESPACE_STD
4650

4751
#endif // _LIBCPP___ALGORITHM_COMP_H

libcxx/include/__algorithm/ranges_minmax.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <__ranges/access.h>
2525
#include <__ranges/concepts.h>
2626
#include <__type_traits/is_reference.h>
27+
#include <__type_traits/is_trivially_copyable.h>
2728
#include <__type_traits/remove_cvref.h>
2829
#include <__utility/forward.h>
2930
#include <__utility/move.h>
@@ -83,7 +84,18 @@ struct __fn {
8384

8485
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__first != __last, "range has to contain at least one element");
8586

86-
if constexpr (forward_range<_Range>) {
87+
if constexpr (contiguous_range<_Range> && is_integral_v<_ValueT> &&
88+
__is_cheap_to_copy<_ValueT> & __is_identity<_Proj>::value &&
89+
__desugars_to<__totally_ordered_less_tag, _Comp, _ValueT, _ValueT>::value) {
90+
minmax_result<_ValueT> __result = {__r[0], __r[0]};
91+
for (auto __e : __r) {
92+
if (__e < __result.min)
93+
__result.min = __e;
94+
if (__result.max < __e)
95+
__result.max = __e;
96+
}
97+
return __result;
98+
} else if constexpr (forward_range<_Range>) {
8799
// Special-case the one element case. Avoid repeatedly initializing objects from the result of an iterator
88100
// dereference when doing so might not be idempotent. The `if constexpr` avoids the extra branch in cases where
89101
// it's not needed.

libcxx/include/__functional/operations.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <__functional/binary_function.h>
1515
#include <__functional/unary_function.h>
1616
#include <__type_traits/integral_constant.h>
17+
#include <__type_traits/is_integral.h>
1718
#include <__type_traits/operation_traits.h>
1819
#include <__utility/forward.h>
1920

@@ -360,6 +361,9 @@ struct _LIBCPP_TEMPLATE_VIS less : __binary_function<_Tp, _Tp, bool> {
360361
};
361362
_LIBCPP_CTAD_SUPPORTED_FOR_TYPE(less);
362363

364+
template <class _Tp>
365+
struct __desugars_to<__totally_ordered_less_tag, less<_Tp>, _Tp, _Tp> : is_integral<_Tp> {};
366+
363367
#if _LIBCPP_STD_VER >= 14
364368
template <>
365369
struct _LIBCPP_TEMPLATE_VIS less<void> {
@@ -371,6 +375,9 @@ struct _LIBCPP_TEMPLATE_VIS less<void> {
371375
}
372376
typedef void is_transparent;
373377
};
378+
379+
template <class _Tp>
380+
struct __desugars_to<__totally_ordered_less_tag, less<>, _Tp, _Tp> : is_integral<_Tp> {};
374381
#endif
375382

376383
#if _LIBCPP_STD_VER >= 14

libcxx/include/__functional/ranges_operations.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ struct greater_equal {
100100
template <class _Tp, class _Up>
101101
struct __desugars_to<__equal_tag, ranges::equal_to, _Tp, _Up> : true_type {};
102102

103+
template <class _Tp, class _Up>
104+
struct __desugars_to<__totally_ordered_less_tag, ranges::less, _Tp, _Up> : true_type {};
105+
103106
#endif // _LIBCPP_STD_VER >= 20
104107

105108
_LIBCPP_END_NAMESPACE_STD

libcxx/include/__type_traits/operation_traits.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
2121
// Tags to represent the canonical operations
2222
struct __equal_tag {};
2323
struct __plus_tag {};
24+
struct __totally_ordered_less_tag {};
2425

2526
// This class template is used to determine whether an operation "desugars"
2627
// (or boils down) to a given canonical operation.

0 commit comments

Comments
 (0)