Skip to content

Commit 6399aec

Browse files
committed
[libc++] Optimize rotate
1 parent fd78472 commit 6399aec

File tree

3 files changed

+67
-36
lines changed

3 files changed

+67
-36
lines changed

libcxx/docs/ReleaseNotes/20.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ Improvements and New Features
7373
optimized, resulting in a performance improvement of up to 2x for trivial element types (e.g., `std::vector<int>`),
7474
and up to 3.4x for non-trivial element types (e.g., `std::vector<std::vector<int>>`).
7575

76+
- ``rotate`` has been optimized, resulting in a performance improvement of up to 2.2x for trivially move assignable
77+
types.
78+
7679
Deprecations and Removals
7780
-------------------------
7881

libcxx/include/__algorithm/rotate.h

Lines changed: 29 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -82,46 +82,39 @@ __rotate_forward(_ForwardIterator __first, _ForwardIterator __middle, _ForwardIt
8282
return __r;
8383
}
8484

85-
template <typename _Integral>
86-
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 _Integral __algo_gcd(_Integral __x, _Integral __y) {
87-
do {
88-
_Integral __t = __x % __y;
89-
__x = __y;
90-
__y = __t;
91-
} while (__y);
92-
return __x;
93-
}
85+
template <class _AlgPolicy, class _Iter, class _Sent>
86+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 _Iter __rotate_random(_Iter __first, _Iter __middle, _Sent __last) {
87+
auto __left = _IterOps<_AlgPolicy>::distance(__first, __middle);
88+
auto __right = _IterOps<_AlgPolicy>::distance(__middle, __last);
89+
auto __end = __first + __right;
9490

95-
template <class _AlgPolicy, typename _RandomAccessIterator>
96-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 _RandomAccessIterator
97-
__rotate_gcd(_RandomAccessIterator __first, _RandomAccessIterator __middle, _RandomAccessIterator __last) {
98-
typedef typename iterator_traits<_RandomAccessIterator>::difference_type difference_type;
99-
typedef typename iterator_traits<_RandomAccessIterator>::value_type value_type;
100-
using _Ops = _IterOps<_AlgPolicy>;
91+
if (__left == 0 || __first == __last)
92+
return __first;
10193

102-
const difference_type __m1 = __middle - __first;
103-
const difference_type __m2 = _Ops::distance(__middle, __last);
104-
if (__m1 == __m2) {
105-
std::__swap_ranges<_AlgPolicy>(__first, __middle, __middle, __last);
94+
if (__left == __right) {
95+
std::__swap_ranges<_AlgPolicy>(__first, __middle, __middle);
10696
return __middle;
10797
}
108-
const difference_type __g = std::__algo_gcd(__m1, __m2);
109-
for (_RandomAccessIterator __p = __first + __g; __p != __first;) {
110-
value_type __t(_Ops::__iter_move(--__p));
111-
_RandomAccessIterator __p1 = __p;
112-
_RandomAccessIterator __p2 = __p1 + __m1;
113-
do {
114-
*__p1 = _Ops::__iter_move(__p2);
115-
__p1 = __p2;
116-
const difference_type __d = _Ops::distance(__p2, __last);
117-
if (__m1 < __d)
118-
__p2 += __m1;
119-
else
120-
__p2 = __first + (__m1 - __d);
121-
} while (__p2 != __p);
122-
*__p1 = std::move(__t);
98+
99+
auto __min_len = std::min(__left, __right);
100+
101+
while (__min_len > 0) {
102+
if (__left <= __right) {
103+
do {
104+
std::__swap_ranges<_AlgPolicy>(__first, __first + __left, __first + __left);
105+
__first += __left;
106+
__right -= __left;
107+
} while (__left <= __right);
108+
__min_len = __right;
109+
} else {
110+
do {
111+
std::__swap_ranges<_AlgPolicy>(__first + (__left - __right), __first + __left, __first + __left);
112+
__left -= __right;
113+
} while (__left > __right);
114+
__min_len = __left;
115+
}
123116
}
124-
return __first + __m2;
117+
return __end;
125118
}
126119

127120
template <class _AlgPolicy, class _ForwardIterator>
@@ -163,7 +156,7 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _RandomAccessIterator
163156
return std::__rotate_left<_AlgPolicy>(__first, __last);
164157
if (_IterOps<_AlgPolicy>::next(__middle) == __last)
165158
return std::__rotate_right<_AlgPolicy>(__first, __last);
166-
return std::__rotate_gcd<_AlgPolicy>(__first, __middle, __last);
159+
return std::__rotate_random<_AlgPolicy>(__first, __middle, __last);
167160
}
168161
return std::__rotate_forward<_AlgPolicy>(__first, __middle, __last);
169162
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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+
// UNSUPPORTED: c++03, c++11, c++14, c++17
10+
11+
#include <algorithm>
12+
#include <cassert>
13+
14+
#include <benchmark/benchmark.h>
15+
16+
void run_sizes(auto benchmark) {
17+
benchmark->Arg(1)->Arg(2)->Arg(3)->Arg(4)->Arg(32)->Arg(64)->Arg(512)->Arg(4096)->Arg(65536);
18+
}
19+
20+
template <class T>
21+
static void BM_std_rotate(benchmark::State& state) {
22+
std::vector<T> vec(state.range(), T());
23+
24+
for (auto _ : state) {
25+
benchmark::DoNotOptimize(vec);
26+
benchmark::DoNotOptimize(std::rotate(vec.begin(), vec.begin() + vec.size() / 3, vec.end()));
27+
}
28+
}
29+
BENCHMARK(BM_std_rotate<int>)->Apply(run_sizes);
30+
#ifndef TEST_HAS_NO_INT128
31+
BENCHMARK(BM_std_rotate<__int128>)->Apply(run_sizes);
32+
#endif
33+
BENCHMARK(BM_std_rotate<std::string>)->Apply(run_sizes);
34+
35+
BENCHMARK_MAIN();

0 commit comments

Comments
 (0)