Skip to content

Commit 3466873

Browse files
committed
[libc++] Optimize rotate
1 parent 8ad5a21 commit 3466873

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
@@ -122,6 +122,9 @@ Improvements and New Features
122122

123123
- The performance of ``std::getline`` has been improved, resulting in a performance uplift of up to 10x.
124124

125+
- ``rotate`` has been optimized, resulting in a performance improvement of up to 2.2x for trivially move assignable
126+
types.
127+
125128
Deprecations and Removals
126129
-------------------------
127130

libcxx/include/__algorithm/rotate.h

Lines changed: 29 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -89,46 +89,39 @@ __rotate_forward(_ForwardIterator __first, _ForwardIterator __middle, _ForwardIt
8989
return __r;
9090
}
9191

92-
template <typename _Integral>
93-
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 _Integral __algo_gcd(_Integral __x, _Integral __y) {
94-
do {
95-
_Integral __t = __x % __y;
96-
__x = __y;
97-
__y = __t;
98-
} while (__y);
99-
return __x;
100-
}
92+
template <class _AlgPolicy, class _Iter, class _Sent>
93+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 _Iter __rotate_random(_Iter __first, _Iter __middle, _Sent __last) {
94+
auto __left = _IterOps<_AlgPolicy>::distance(__first, __middle);
95+
auto __right = _IterOps<_AlgPolicy>::distance(__middle, __last);
96+
auto __end = __first + __right;
10197

102-
template <class _AlgPolicy, typename _RandomAccessIterator>
103-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 _RandomAccessIterator
104-
__rotate_gcd(_RandomAccessIterator __first, _RandomAccessIterator __middle, _RandomAccessIterator __last) {
105-
typedef typename iterator_traits<_RandomAccessIterator>::difference_type difference_type;
106-
typedef typename iterator_traits<_RandomAccessIterator>::value_type value_type;
107-
using _Ops = _IterOps<_AlgPolicy>;
98+
if (__left == 0 || __first == __last)
99+
return __first;
108100

109-
const difference_type __m1 = __middle - __first;
110-
const difference_type __m2 = _Ops::distance(__middle, __last);
111-
if (__m1 == __m2) {
112-
std::__swap_ranges<_AlgPolicy>(__first, __middle, __middle, __last);
101+
if (__left == __right) {
102+
std::__swap_ranges<_AlgPolicy>(__first, __middle, __middle);
113103
return __middle;
114104
}
115-
const difference_type __g = std::__algo_gcd(__m1, __m2);
116-
for (_RandomAccessIterator __p = __first + __g; __p != __first;) {
117-
value_type __t(_Ops::__iter_move(--__p));
118-
_RandomAccessIterator __p1 = __p;
119-
_RandomAccessIterator __p2 = __p1 + __m1;
120-
do {
121-
*__p1 = _Ops::__iter_move(__p2);
122-
__p1 = __p2;
123-
const difference_type __d = _Ops::distance(__p2, __last);
124-
if (__m1 < __d)
125-
__p2 += __m1;
126-
else
127-
__p2 = __first + (__m1 - __d);
128-
} while (__p2 != __p);
129-
*__p1 = std::move(__t);
105+
106+
auto __min_len = std::min(__left, __right);
107+
108+
while (__min_len > 0) {
109+
if (__left <= __right) {
110+
do {
111+
std::__swap_ranges<_AlgPolicy>(__first, __first + __left, __first + __left);
112+
__first += __left;
113+
__right -= __left;
114+
} while (__left <= __right);
115+
__min_len = __right;
116+
} else {
117+
do {
118+
std::__swap_ranges<_AlgPolicy>(__first + (__left - __right), __first + __left, __first + __left);
119+
__left -= __right;
120+
} while (__left > __right);
121+
__min_len = __left;
122+
}
130123
}
131-
return __first + __m2;
124+
return __end;
132125
}
133126

134127
template <class _AlgPolicy, class _ForwardIterator>
@@ -170,7 +163,7 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _RandomAccessIterator
170163
return std::__rotate_left<_AlgPolicy>(__first, __last);
171164
if (_IterOps<_AlgPolicy>::next(__middle) == __last)
172165
return std::__rotate_right<_AlgPolicy>(__first, __last);
173-
return std::__rotate_gcd<_AlgPolicy>(__first, __middle, __last);
166+
return std::__rotate_random<_AlgPolicy>(__first, __middle, __last);
174167
}
175168
return std::__rotate_forward<_AlgPolicy>(__first, __middle, __last);
176169
}
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)