Skip to content

Commit 2fa896a

Browse files
committed
[libc++] Optimize ranges::minmax
1 parent f5960c1 commit 2fa896a

File tree

8 files changed

+100
-1
lines changed

8 files changed

+100
-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
@@ -54,6 +54,8 @@ Improvements and New Features
5454
resulting in a performance increase of up to 1400x.
5555
- The ``std::mismatch`` algorithm has been optimized for integral types, which can lead up to 40x performance
5656
improvements.
57+
- The ``std::ranges::minmax`` algorithm has been optimized for integral types, resulting in a performance increase of
58+
up to 100x.
5759

5860
- The ``_LIBCPP_ENABLE_CXX26_REMOVED_STRSTREAM`` macro has been added to make the declarations in ``<strstream>`` available.
5961

libcxx/include/__algorithm/comp.h

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

44+
template <class _Tp>
45+
inline const bool __desugars_to_v<__less_tag, __less<>, _Tp, _Tp> = true;
46+
4447
_LIBCPP_END_NAMESPACE_STD
4548

4649
#endif // _LIBCPP___ALGORITHM_COMP_H

libcxx/include/__algorithm/ranges_minmax.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
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>
28+
#include <__type_traits/operation_traits.h>
2729
#include <__type_traits/remove_cvref.h>
2830
#include <__utility/forward.h>
2931
#include <__utility/move.h>
@@ -83,7 +85,20 @@ struct __fn {
8385

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

86-
if constexpr (forward_range<_Range>) {
88+
// This optimiation is not in minmax_element because clang doesn't see through the pointers and as a result doesn't
89+
// vectorize the code.
90+
if constexpr (contiguous_range<_Range> && is_integral_v<_ValueT> &&
91+
__is_cheap_to_copy<_ValueT> & __is_identity<_Proj>::value &&
92+
__desugars_to_v<__less_tag, _Comp, _ValueT, _ValueT>) {
93+
minmax_result<_ValueT> __result = {__r[0], __r[0]};
94+
for (auto __e : __r) {
95+
if (__e < __result.min)
96+
__result.min = __e;
97+
if (__result.max < __e)
98+
__result.max = __e;
99+
}
100+
return __result;
101+
} else if constexpr (forward_range<_Range>) {
87102
// Special-case the one element case. Avoid repeatedly initializing objects from the result of an iterator
88103
// dereference when doing so might not be idempotent. The `if constexpr` avoids the extra branch in cases where
89104
// it's not needed.

libcxx/include/__functional/operations.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,9 @@ struct _LIBCPP_TEMPLATE_VIS less : __binary_function<_Tp, _Tp, bool> {
359359
};
360360
_LIBCPP_CTAD_SUPPORTED_FOR_TYPE(less);
361361

362+
template <class _Tp>
363+
inline const bool __desugars_to_v<__less_tag, less<_Tp>, _Tp, _Tp> = true;
364+
362365
#if _LIBCPP_STD_VER >= 14
363366
template <>
364367
struct _LIBCPP_TEMPLATE_VIS less<void> {
@@ -370,6 +373,9 @@ struct _LIBCPP_TEMPLATE_VIS less<void> {
370373
}
371374
typedef void is_transparent;
372375
};
376+
377+
template <class _Tp>
378+
inline const bool __desugars_to_v<__less_tag, less<>, _Tp, _Tp> = true;
373379
#endif
374380

375381
#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
@@ -99,6 +99,9 @@ struct greater_equal {
9999
template <class _Tp, class _Up>
100100
inline const bool __desugars_to_v<__equal_tag, ranges::equal_to, _Tp, _Up> = true;
101101

102+
template <class _Tp, class _Up>
103+
inline const bool __desugars_to_v<__less_tag, ranges::less, _Tp, _Up> = true;
104+
102105
#endif // _LIBCPP_STD_VER >= 20
103106

104107
_LIBCPP_END_NAMESPACE_STD

libcxx/include/__type_traits/desugars_to.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
2020
// Tags to represent the canonical operations
2121
struct __equal_tag {};
2222
struct __plus_tag {};
23+
struct __less_tag {};
2324

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

0 commit comments

Comments
 (0)