Skip to content

Commit e1bd88f

Browse files
committed
Optimize ranges::rotate for vector<bool>::iterator
1 parent 4a92c27 commit e1bd88f

File tree

7 files changed

+560
-460
lines changed

7 files changed

+560
-460
lines changed

libcxx/include/__algorithm/rotate.h

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,19 @@
99
#ifndef _LIBCPP___ALGORITHM_ROTATE_H
1010
#define _LIBCPP___ALGORITHM_ROTATE_H
1111

12+
#include <__algorithm/copy.h>
13+
#include <__algorithm/copy_backward.h>
1214
#include <__algorithm/iterator_operations.h>
1315
#include <__algorithm/move.h>
1416
#include <__algorithm/move_backward.h>
1517
#include <__algorithm/swap_ranges.h>
1618
#include <__config>
19+
#include <__cstddef/size_t.h>
20+
#include <__fwd/bit_reference.h>
1721
#include <__iterator/iterator_traits.h>
22+
#include <__memory/construct_at.h>
23+
#include <__memory/pointer_traits.h>
24+
#include <__type_traits/is_constant_evaluated.h>
1825
#include <__type_traits/is_trivially_assignable.h>
1926
#include <__utility/move.h>
2027
#include <__utility/pair.h>
@@ -185,6 +192,44 @@ __rotate(_Iterator __first, _Iterator __middle, _Sentinel __last) {
185192
return _Ret(std::move(__result), std::move(__last_iter));
186193
}
187194

195+
template <class, class _Cp>
196+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair<__bit_iterator<_Cp, false>, __bit_iterator<_Cp, false> >
197+
__rotate(__bit_iterator<_Cp, false> __first, __bit_iterator<_Cp, false> __middle, __bit_iterator<_Cp, false> __last) {
198+
using _I1 = __bit_iterator<_Cp, false>;
199+
using difference_type = typename _I1::difference_type;
200+
difference_type __d1 = __middle - __first;
201+
difference_type __d2 = __last - __middle;
202+
_I1 __r = __first + __d2;
203+
while (__d1 != 0 && __d2 != 0) {
204+
if (__d1 <= __d2) {
205+
if (__d1 <= __bit_array<_Cp>::capacity()) {
206+
__bit_array<_Cp> __b(__d1);
207+
std::copy(__first, __middle, __b.begin());
208+
std::copy(__b.begin(), __b.end(), std::copy(__middle, __last, __first));
209+
break;
210+
} else {
211+
__bit_iterator<_Cp, false> __mp = std::swap_ranges(__first, __middle, __middle);
212+
__first = __middle;
213+
__middle = __mp;
214+
__d2 -= __d1;
215+
}
216+
} else {
217+
if (__d2 <= __bit_array<_Cp>::capacity()) {
218+
__bit_array<_Cp> __b(__d2);
219+
std::copy(__middle, __last, __b.begin());
220+
std::copy_backward(__b.begin(), __b.end(), std::copy_backward(__first, __middle, __last));
221+
break;
222+
} else {
223+
__bit_iterator<_Cp, false> __mp = __first + __d2;
224+
std::swap_ranges(__first, __mp, __middle);
225+
__first = __mp;
226+
__d1 -= __d2;
227+
}
228+
}
229+
}
230+
return std::make_pair(__r, __last);
231+
}
232+
188233
template <class _ForwardIterator>
189234
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _ForwardIterator
190235
rotate(_ForwardIterator __first, _ForwardIterator __middle, _ForwardIterator __last) {

libcxx/include/__bit_reference

Lines changed: 5 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include <__algorithm/copy_n.h>
1414
#include <__algorithm/min.h>
15+
#include <__algorithm/rotate.h>
1516
#include <__bit/countr.h>
1617
#include <__compare/ordering.h>
1718
#include <__config>
@@ -22,6 +23,7 @@
2223
#include <__memory/pointer_traits.h>
2324
#include <__type_traits/conditional.h>
2425
#include <__type_traits/is_constant_evaluated.h>
26+
#include <__utility/pair.h>
2527
#include <__utility/swap.h>
2628

2729
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -583,8 +585,6 @@ inline _LIBCPP_HIDE_FROM_ABI __bit_iterator<_Cr, false> swap_ranges(
583585
return std::__swap_ranges_unaligned(__first1, __last1, __first2);
584586
}
585587

586-
// rotate
587-
588588
template <class _Cp>
589589
struct __bit_array {
590590
using difference_type = typename _Cp::difference_type;
@@ -616,45 +616,6 @@ struct __bit_array {
616616
}
617617
};
618618

619-
template <class _Cp>
620-
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI __bit_iterator<_Cp, false>
621-
rotate(__bit_iterator<_Cp, false> __first, __bit_iterator<_Cp, false> __middle, __bit_iterator<_Cp, false> __last) {
622-
using _I1 = __bit_iterator<_Cp, false>;
623-
using difference_type = typename _I1::difference_type;
624-
625-
difference_type __d1 = __middle - __first;
626-
difference_type __d2 = __last - __middle;
627-
_I1 __r = __first + __d2;
628-
while (__d1 != 0 && __d2 != 0) {
629-
if (__d1 <= __d2) {
630-
if (__d1 <= __bit_array<_Cp>::capacity()) {
631-
__bit_array<_Cp> __b(__d1);
632-
std::copy(__first, __middle, __b.begin());
633-
std::copy(__b.begin(), __b.end(), std::copy(__middle, __last, __first));
634-
break;
635-
} else {
636-
__bit_iterator<_Cp, false> __mp = std::swap_ranges(__first, __middle, __middle);
637-
__first = __middle;
638-
__middle = __mp;
639-
__d2 -= __d1;
640-
}
641-
} else {
642-
if (__d2 <= __bit_array<_Cp>::capacity()) {
643-
__bit_array<_Cp> __b(__d2);
644-
std::copy(__middle, __last, __b.begin());
645-
std::copy_backward(__b.begin(), __b.end(), std::copy_backward(__first, __middle, __last));
646-
break;
647-
} else {
648-
__bit_iterator<_Cp, false> __mp = __first + __d2;
649-
std::swap_ranges(__first, __mp, __middle);
650-
__first = __mp;
651-
__d1 -= __d2;
652-
}
653-
}
654-
}
655-
return __r;
656-
}
657-
658619
// equal
659620

660621
template <class _Cp, bool _IC1, bool _IC2>
@@ -995,9 +956,9 @@ private:
995956
template <class _Cl, class _Cr>
996957
friend __bit_iterator<_Cr, false>
997958
swap_ranges(__bit_iterator<_Cl, false>, __bit_iterator<_Cl, false>, __bit_iterator<_Cr, false>);
998-
template <class _Dp>
999-
_LIBCPP_CONSTEXPR_SINCE_CXX20 friend __bit_iterator<_Dp, false>
1000-
rotate(__bit_iterator<_Dp, false>, __bit_iterator<_Dp, false>, __bit_iterator<_Dp, false>);
959+
template <class, class _Dp>
960+
_LIBCPP_CONSTEXPR_SINCE_CXX20 friend pair<__bit_iterator<_Dp, false>, __bit_iterator<_Dp, false> >
961+
__rotate(__bit_iterator<_Dp, false>, __bit_iterator<_Dp, false>, __bit_iterator<_Dp, false>);
1001962
template <class _Dp, bool _IC1, bool _IC2>
1002963
_LIBCPP_CONSTEXPR_SINCE_CXX20 friend bool
1003964
__equal_aligned(__bit_iterator<_Dp, _IC1>, __bit_iterator<_Dp, _IC1>, __bit_iterator<_Dp, _IC2>);

libcxx/include/__fwd/bit_reference.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ _LIBCPP_BEGIN_NAMESPACE_STD
2020
template <class _Cp, bool _IsConst, typename _Cp::__storage_type = 0>
2121
class __bit_iterator;
2222

23+
template <class _Cp>
24+
struct __bit_array;
25+
2326
_LIBCPP_END_NAMESPACE_STD
2427

2528
#endif // _LIBCPP___FWD_BIT_REFERENCE_H

libcxx/include/__vector/vector_bool.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <__algorithm/fill_n.h>
1414
#include <__algorithm/iterator_operations.h>
1515
#include <__algorithm/max.h>
16+
#include <__algorithm/rotate.h>
1617
#include <__assert>
1718
#include <__bit_reference>
1819
#include <__config>
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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, c++20
10+
11+
#include <algorithm>
12+
#include <benchmark/benchmark.h>
13+
#include <vector>
14+
15+
static void bm_ranges_rotate(benchmark::State& state) {
16+
auto n = state.range();
17+
std::vector<bool> v(n);
18+
auto mid = std::ranges::begin(v) + n / 2;
19+
for (auto _ : state) {
20+
benchmark::DoNotOptimize(std::ranges::rotate(v, mid));
21+
benchmark::DoNotOptimize(&v);
22+
}
23+
}
24+
25+
static void bm_rotate(benchmark::State& state) {
26+
auto n = state.range();
27+
std::vector<bool> v(n);
28+
auto beg = v.begin();
29+
auto mid = v.begin() + n / 2;
30+
auto end = v.end();
31+
for (auto _ : state) {
32+
benchmark::DoNotOptimize(std::rotate(beg, mid, end));
33+
benchmark::DoNotOptimize(&v);
34+
}
35+
}
36+
37+
BENCHMARK(bm_ranges_rotate)->Range(8, 1 << 20);
38+
BENCHMARK(bm_rotate)->Range(8, 1 << 20);
39+
40+
BENCHMARK_MAIN();

libcxx/test/std/algorithms/alg.modifying.operations/alg.rotate/ranges_rotate.pass.cpp

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,19 @@
2121
#include <array>
2222
#include <cassert>
2323
#include <ranges>
24+
#include <vector>
2425

2526
#include "almost_satisfies_types.h"
27+
#include "test_macros.h"
2628
#include "test_iterators.h"
2729

2830
// Test constraints of the (iterator, sentinel) overload.
2931
// ======================================================
3032

3133
template <class Iter = int*, class Sent = int*>
32-
concept HasRotateIter =
33-
requires(Iter&& iter, Sent&& sent) {
34-
std::ranges::rotate(std::forward<Iter>(iter), std::forward<Iter>(iter), std::forward<Sent>(sent));
35-
};
34+
concept HasRotateIter = requires(Iter&& iter, Sent&& sent) {
35+
std::ranges::rotate(std::forward<Iter>(iter), std::forward<Iter>(iter), std::forward<Sent>(sent));
36+
};
3637

3738
static_assert(HasRotateIter<int*, int*>);
3839

@@ -48,10 +49,9 @@ static_assert(!HasRotateIter<int*, SentinelForNotWeaklyEqualityComparableWith>);
4849
// =========================================
4950

5051
template <class Range>
51-
concept HasRotateRange =
52-
requires(Range&& range, std::ranges::iterator_t<Range> iter) {
53-
std::ranges::rotate(std::forward<Range>(range), iter);
54-
};
52+
concept HasRotateRange = requires(Range&& range, std::ranges::iterator_t<Range> iter) {
53+
std::ranges::rotate(std::forward<Range>(range), iter);
54+
};
5555

5656
template <class T>
5757
using R = UncheckedRange<T>;
@@ -73,10 +73,10 @@ constexpr void test_one(const std::array<int, N> input, std::size_t mid_index, s
7373
assert(mid_index <= N);
7474

7575
{ // (iterator, sentinel) overload.
76-
auto in = input;
76+
auto in = input;
7777
auto begin = Iter(in.data());
78-
auto mid = Iter(in.data() + mid_index);
79-
auto end = Sent(Iter(in.data() + in.size()));
78+
auto mid = Iter(in.data() + mid_index);
79+
auto end = Sent(Iter(in.data() + in.size()));
8080

8181
std::same_as<std::ranges::subrange<Iter>> decltype(auto) result = std::ranges::rotate(begin, mid, end);
8282
assert(base(result.begin()) == in.data() + in.size() - mid_index);
@@ -85,10 +85,10 @@ constexpr void test_one(const std::array<int, N> input, std::size_t mid_index, s
8585
}
8686

8787
{ // (range) overload.
88-
auto in = input;
88+
auto in = input;
8989
auto begin = Iter(in.data());
90-
auto mid = Iter(in.data() + mid_index);
91-
auto end = Sent(Iter(in.data() + in.size()));
90+
auto mid = Iter(in.data() + mid_index);
91+
auto end = Sent(Iter(in.data() + in.size()));
9292
auto range = std::ranges::subrange(std::move(begin), std::move(end));
9393

9494
std::same_as<std::ranges::subrange<Iter>> decltype(auto) result = std::ranges::rotate(range, mid);
@@ -146,18 +146,41 @@ constexpr void test_iterators() {
146146
test_iter<int*>();
147147
}
148148

149+
#if TEST_STD_VER >= 23
150+
template <std::size_t N>
151+
TEST_CONSTEXPR_CXX20 bool test_bit_iterator() {
152+
for (int offset = -1; offset <= 1; ++offset) {
153+
std::vector<bool> a(N, false);
154+
std::size_t mid = N / 2 + offset;
155+
for (std::size_t i = mid; i < N; ++i)
156+
a[i] = true;
157+
158+
// (iterator, sentinel)-overload
159+
std::ranges::rotate(std::ranges::begin(a), std::ranges::begin(a) + mid, std::ranges::end(a));
160+
for (std::size_t i = 0; i < N; ++i)
161+
assert(a[i] == (i < N - mid));
162+
163+
// range-overload
164+
std::ranges::rotate(a, std::ranges::begin(a) + (N - mid));
165+
for (std::size_t i = 0; i < N; ++i)
166+
assert(a[i] == (i >= mid));
167+
}
168+
return true;
169+
};
170+
#endif
171+
149172
constexpr bool test() {
150173
test_iterators();
151174

152175
{ // Complexity: at most `last - first` swaps.
153176
const std::array input = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
154-
auto expected = static_cast<int>(input.size());
177+
auto expected = static_cast<int>(input.size());
155178

156179
{
157-
auto in = input;
158-
int swaps = 0;
180+
auto in = input;
181+
int swaps = 0;
159182
auto begin = adl::Iterator::TrackSwaps(in.data(), swaps);
160-
auto end = adl::Iterator::TrackSwaps(in.data() + in.size(), swaps);
183+
auto end = adl::Iterator::TrackSwaps(in.data() + in.size(), swaps);
161184

162185
for (std::size_t mid = 0; mid != input.size(); ++mid) {
163186
std::ranges::rotate(begin, begin + mid, end);
@@ -166,10 +189,10 @@ constexpr bool test() {
166189
}
167190

168191
{
169-
auto in = input;
170-
int swaps = 0;
192+
auto in = input;
193+
int swaps = 0;
171194
auto begin = adl::Iterator::TrackSwaps(in.data(), swaps);
172-
auto end = adl::Iterator::TrackSwaps(in.data() + in.size(), swaps);
195+
auto end = adl::Iterator::TrackSwaps(in.data() + in.size(), swaps);
173196
auto range = std::ranges::subrange(begin, end);
174197

175198
for (std::size_t mid = 0; mid != input.size(); ++mid) {
@@ -179,6 +202,13 @@ constexpr bool test() {
179202
}
180203
}
181204

205+
#if TEST_STD_VER >= 23
206+
test_bit_iterator<8>();
207+
test_bit_iterator<16>();
208+
test_bit_iterator<64>();
209+
test_bit_iterator<100>();
210+
#endif
211+
182212
return true;
183213
}
184214

0 commit comments

Comments
 (0)