Skip to content

Commit 0d08932

Browse files
committed
Optimize ranges::rotate for vector<bool>::iterator
1 parent e8e75e0 commit 0d08932

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>
@@ -24,6 +25,7 @@
2425
#include <__type_traits/conditional.h>
2526
#include <__type_traits/is_constant_evaluated.h>
2627
#include <__type_traits/void_t.h>
28+
#include <__utility/pair.h>
2729
#include <__utility/swap.h>
2830

2931
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -597,8 +599,6 @@ inline _LIBCPP_HIDE_FROM_ABI __bit_iterator<_Cr, false> swap_ranges(
597599
return std::__swap_ranges_unaligned(__first1, __last1, __first2);
598600
}
599601

600-
// rotate
601-
602602
template <class _Cp>
603603
struct __bit_array {
604604
using difference_type _LIBCPP_NODEBUG = typename __size_difference_type_traits<_Cp>::difference_type;
@@ -630,45 +630,6 @@ struct __bit_array {
630630
}
631631
};
632632

633-
template <class _Cp>
634-
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI __bit_iterator<_Cp, false>
635-
rotate(__bit_iterator<_Cp, false> __first, __bit_iterator<_Cp, false> __middle, __bit_iterator<_Cp, false> __last) {
636-
using _I1 = __bit_iterator<_Cp, false>;
637-
using difference_type = typename _I1::difference_type;
638-
639-
difference_type __d1 = __middle - __first;
640-
difference_type __d2 = __last - __middle;
641-
_I1 __r = __first + __d2;
642-
while (__d1 != 0 && __d2 != 0) {
643-
if (__d1 <= __d2) {
644-
if (__d1 <= __bit_array<_Cp>::capacity()) {
645-
__bit_array<_Cp> __b(__d1);
646-
std::copy(__first, __middle, __b.begin());
647-
std::copy(__b.begin(), __b.end(), std::copy(__middle, __last, __first));
648-
break;
649-
} else {
650-
__bit_iterator<_Cp, false> __mp = std::swap_ranges(__first, __middle, __middle);
651-
__first = __middle;
652-
__middle = __mp;
653-
__d2 -= __d1;
654-
}
655-
} else {
656-
if (__d2 <= __bit_array<_Cp>::capacity()) {
657-
__bit_array<_Cp> __b(__d2);
658-
std::copy(__middle, __last, __b.begin());
659-
std::copy_backward(__b.begin(), __b.end(), std::copy_backward(__first, __middle, __last));
660-
break;
661-
} else {
662-
__bit_iterator<_Cp, false> __mp = __first + __d2;
663-
std::swap_ranges(__first, __mp, __middle);
664-
__first = __mp;
665-
__d1 -= __d2;
666-
}
667-
}
668-
}
669-
return __r;
670-
}
671-
672633
// equal
673634

674635
template <class _Cp, bool _IC1, bool _IC2>
@@ -1009,9 +970,9 @@ private:
1009970
template <class _Cl, class _Cr>
1010971
friend __bit_iterator<_Cr, false>
1011972
swap_ranges(__bit_iterator<_Cl, false>, __bit_iterator<_Cl, false>, __bit_iterator<_Cr, false>);
1012-
template <class _Dp>
1013-
_LIBCPP_CONSTEXPR_SINCE_CXX20 friend __bit_iterator<_Dp, false>
1014-
rotate(__bit_iterator<_Dp, false>, __bit_iterator<_Dp, false>, __bit_iterator<_Dp, false>);
973+
template <class, class _Dp>
974+
_LIBCPP_CONSTEXPR_SINCE_CXX20 friend pair<__bit_iterator<_Dp, false>, __bit_iterator<_Dp, false> >
975+
__rotate(__bit_iterator<_Dp, false>, __bit_iterator<_Dp, false>, __bit_iterator<_Dp, false>);
1015976
template <class _Dp, bool _IC1, bool _IC2>
1016977
_LIBCPP_CONSTEXPR_SINCE_CXX20 friend bool
1017978
__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
@@ -23,6 +23,9 @@ class __bit_iterator;
2323
template <class, class = void>
2424
struct __size_difference_type_traits;
2525

26+
template <class _Cp>
27+
struct __bit_array;
28+
2629
_LIBCPP_END_NAMESPACE_STD
2730

2831
#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_vb(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_vb(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_vb)->Range(8, 1 << 20);
38+
BENCHMARK(bm_rotate_vb)->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_vector_bool() {
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_vector_bool<8>();
207+
test_vector_bool<16>();
208+
test_vector_bool<64>();
209+
test_vector_bool<256>();
210+
#endif
211+
182212
return true;
183213
}
184214

0 commit comments

Comments
 (0)