Skip to content

Commit 6d84599

Browse files
committed
Optimize ranges::move{,_backward} for vector<bool>::iterator
1 parent d33e33f commit 6d84599

File tree

9 files changed

+362
-110
lines changed

9 files changed

+362
-110
lines changed

libcxx/include/__algorithm/move.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@
99
#ifndef _LIBCPP___ALGORITHM_MOVE_H
1010
#define _LIBCPP___ALGORITHM_MOVE_H
1111

12+
#include <__algorithm/copy.h>
1213
#include <__algorithm/copy_move_common.h>
1314
#include <__algorithm/for_each_segment.h>
1415
#include <__algorithm/iterator_operations.h>
1516
#include <__algorithm/min.h>
1617
#include <__config>
18+
#include <__fwd/bit_reference.h>
1719
#include <__iterator/iterator_traits.h>
1820
#include <__iterator/segmented_iterator.h>
1921
#include <__type_traits/common_type.h>
@@ -98,6 +100,14 @@ struct __move_impl {
98100
}
99101
}
100102

103+
template <class _Cp, bool _IsConst>
104+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair<__bit_iterator<_Cp, _IsConst>, __bit_iterator<_Cp, false> >
105+
operator()(__bit_iterator<_Cp, _IsConst> __first,
106+
__bit_iterator<_Cp, _IsConst> __last,
107+
__bit_iterator<_Cp, false> __result) {
108+
return std::__copy(__first, __last, __result);
109+
}
110+
101111
// At this point, the iterators have been unwrapped so any `contiguous_iterator` has been unwrapped to a pointer.
102112
template <class _In, class _Out, __enable_if_t<__can_lower_move_assignment_to_memmove<_In, _Out>::value, int> = 0>
103113
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 pair<_In*, _Out*>

libcxx/include/__algorithm/move_backward.h

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

12+
#include <__algorithm/copy_backward.h>
1213
#include <__algorithm/copy_move_common.h>
1314
#include <__algorithm/iterator_operations.h>
1415
#include <__algorithm/min.h>
1516
#include <__config>
17+
#include <__fwd/bit_reference.h>
1618
#include <__iterator/iterator_traits.h>
1719
#include <__iterator/segmented_iterator.h>
1820
#include <__type_traits/common_type.h>
@@ -107,6 +109,14 @@ struct __move_backward_impl {
107109
}
108110
}
109111

112+
template <class _Cp, bool _IsConst>
113+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair<__bit_iterator<_Cp, _IsConst>, __bit_iterator<_Cp, false> >
114+
operator()(__bit_iterator<_Cp, _IsConst> __first,
115+
__bit_iterator<_Cp, _IsConst> __last,
116+
__bit_iterator<_Cp, false> __result) {
117+
return std::__copy_backward<_ClassicAlgPolicy>(__first, __last, __result);
118+
}
119+
110120
// At this point, the iterators have been unwrapped so any `contiguous_iterator` has been unwrapped to a pointer.
111121
template <class _In, class _Out, __enable_if_t<__can_lower_move_assignment_to_memmove<_In, _Out>::value, int> = 0>
112122
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 pair<_In*, _Out*>

libcxx/include/__bit_reference

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -435,22 +435,6 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __bit_iterator<_Cp, f
435435
return std::__copy_backward_unaligned(__first, __last, __result);
436436
}
437437

438-
// move
439-
440-
template <class _Cp, bool _IsConst>
441-
inline _LIBCPP_HIDE_FROM_ABI __bit_iterator<_Cp, false>
442-
move(__bit_iterator<_Cp, _IsConst> __first, __bit_iterator<_Cp, _IsConst> __last, __bit_iterator<_Cp, false> __result) {
443-
return std::copy(__first, __last, __result);
444-
}
445-
446-
// move_backward
447-
448-
template <class _Cp, bool _IsConst>
449-
inline _LIBCPP_HIDE_FROM_ABI __bit_iterator<_Cp, false> move_backward(
450-
__bit_iterator<_Cp, _IsConst> __first, __bit_iterator<_Cp, _IsConst> __last, __bit_iterator<_Cp, false> __result) {
451-
return std::copy_backward(__first, __last, __result);
452-
}
453-
454438
// swap_ranges
455439

456440
template <class _Cl, class _Cr>
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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_move_vb(benchmark::State& state, bool aligned) {
16+
auto n = state.range();
17+
std::vector<bool> in(n, true);
18+
std::vector<bool> out(aligned ? n : n + 8);
19+
benchmark::DoNotOptimize(&in);
20+
auto dst = aligned ? out.begin() : out.begin() + 4;
21+
for (auto _ : state) {
22+
benchmark::DoNotOptimize(std::ranges::move(in, dst));
23+
benchmark::DoNotOptimize(&out);
24+
}
25+
}
26+
27+
static void bm_move_vb(benchmark::State& state, bool aligned) {
28+
auto n = state.range();
29+
std::vector<bool> in(n, true);
30+
std::vector<bool> out(aligned ? n : n + 8);
31+
benchmark::DoNotOptimize(&in);
32+
auto beg = in.begin();
33+
auto end = in.end();
34+
auto dst = aligned ? out.begin() : out.begin() + 4;
35+
for (auto _ : state) {
36+
benchmark::DoNotOptimize(std::move(beg, end, dst));
37+
benchmark::DoNotOptimize(&out);
38+
}
39+
}
40+
41+
static void bm_ranges_move_vb_aligned(benchmark::State& state) { bm_ranges_move_vb(state, true); }
42+
static void bm_ranges_move_vb_unaligned(benchmark::State& state) { bm_ranges_move_vb(state, false); }
43+
44+
static void bm_move_vb_aligned(benchmark::State& state) { bm_move_vb(state, true); }
45+
static void bm_move_vb_unaligned(benchmark::State& state) { bm_move_vb(state, false); }
46+
47+
// Test std::ranges::move for vector<bool>::iterator
48+
BENCHMARK(bm_ranges_move_vb_aligned)->Range(8, 1 << 20);
49+
BENCHMARK(bm_ranges_move_vb_unaligned)->Range(8, 1 << 20);
50+
51+
// Test std::move for vector<bool>::iterator
52+
BENCHMARK(bm_move_vb_aligned)->Range(8, 1 << 20);
53+
BENCHMARK(bm_move_vb_unaligned)->Range(8, 1 << 20);
54+
55+
BENCHMARK_MAIN();
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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_move_backward_vb(benchmark::State& state, bool aligned) {
16+
auto n = state.range();
17+
std::vector<bool> in(n, true);
18+
std::vector<bool> out(aligned ? n : n + 8);
19+
benchmark::DoNotOptimize(&in);
20+
auto dst = aligned ? out.end() : out.end() - 4;
21+
for (auto _ : state) {
22+
benchmark::DoNotOptimize(std::ranges::move_backward(in, dst));
23+
benchmark::DoNotOptimize(&out);
24+
}
25+
}
26+
27+
static void bm_move_backward(benchmark::State& state, bool aligned) {
28+
auto n = state.range();
29+
std::vector<bool> in(n, true);
30+
std::vector<bool> out(aligned ? n : n + 8);
31+
benchmark::DoNotOptimize(&in);
32+
auto beg = in.begin();
33+
auto end = in.end();
34+
auto dst = aligned ? out.end() : out.end() - 4;
35+
for (auto _ : state) {
36+
benchmark::DoNotOptimize(std::move_backward(beg, end, dst));
37+
benchmark::DoNotOptimize(&out);
38+
}
39+
}
40+
41+
static void bm_ranges_move_backward_vb_aligned(benchmark::State& state) { bm_ranges_move_backward_vb(state, true); }
42+
static void bm_ranges_move_backward_vb_unaligned(benchmark::State& state) { bm_ranges_move_backward_vb(state, false); }
43+
44+
static void bm_move_backward_vb_aligned(benchmark::State& state) { bm_move_backward(state, true); }
45+
static void bm_move_backward_vb_unaligned(benchmark::State& state) { bm_move_backward(state, false); }
46+
47+
// Test std::ranges::move_backward for vector<bool>::iterator
48+
BENCHMARK(bm_ranges_move_backward_vb_aligned)->Range(8, 1 << 20);
49+
BENCHMARK(bm_ranges_move_backward_vb_unaligned)->Range(8, 1 << 20);
50+
51+
// Test std::move_backward for vector<bool>::iterator
52+
BENCHMARK(bm_move_backward_vb_aligned)->Range(8, 1 << 20);
53+
BENCHMARK(bm_move_backward_vb_unaligned)->Range(8, 1 << 20);
54+
55+
BENCHMARK_MAIN();

libcxx/test/std/algorithms/alg.modifying.operations/alg.move/move.pass.cpp

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <cassert>
2121
#include <iterator>
2222
#include <memory>
23+
#include <vector>
2324

2425
#include "MoveOnly.h"
2526
#include "test_iterators.h"
@@ -45,15 +46,15 @@ struct Test {
4546
template <class OutIter>
4647
TEST_CONSTEXPR_CXX20 void operator()() {
4748
const unsigned N = 1000;
48-
int ia[N] = {};
49+
int ia[N] = {};
4950
for (unsigned i = 0; i < N; ++i)
50-
ia[i] = i;
51+
ia[i] = i;
5152
int ib[N] = {0};
5253

53-
OutIter r = std::move(InIter(ia), InIter(ia+N), OutIter(ib));
54-
assert(base(r) == ib+N);
54+
OutIter r = std::move(InIter(ia), InIter(ia + N), OutIter(ib));
55+
assert(base(r) == ib + N);
5556
for (unsigned i = 0; i < N; ++i)
56-
assert(ia[i] == ib[i]);
57+
assert(ia[i] == ib[i]);
5758
}
5859
};
5960

@@ -73,13 +74,13 @@ struct Test1 {
7374
const unsigned N = 100;
7475
std::unique_ptr<int> ia[N];
7576
for (unsigned i = 0; i < N; ++i)
76-
ia[i].reset(new int(i));
77+
ia[i].reset(new int(i));
7778
std::unique_ptr<int> ib[N];
7879

79-
OutIter r = std::move(InIter(ia), InIter(ia+N), OutIter(ib));
80-
assert(base(r) == ib+N);
80+
OutIter r = std::move(InIter(ia), InIter(ia + N), OutIter(ib));
81+
assert(base(r) == ib + N);
8182
for (unsigned i = 0; i < N; ++i)
82-
assert(*ib[i] == static_cast<int>(i));
83+
assert(*ib[i] == static_cast<int>(i));
8384
}
8485
};
8586

@@ -92,6 +93,25 @@ struct Test1OutIters {
9293
}
9394
};
9495

96+
template <std::size_t N>
97+
TEST_CONSTEXPR_CXX20 void test_vector_bool() {
98+
std::vector<bool> in(N, false);
99+
for (std::size_t i = 0; i < N; i += 2)
100+
in[i] = true;
101+
102+
{ // Test move with aligned bytes
103+
std::vector<bool> out(N);
104+
std::move(in.begin(), in.end(), out.begin());
105+
assert(in == out);
106+
}
107+
{ // Test move with unaligned bytes
108+
std::vector<bool> out(N + 8);
109+
std::move(in.begin(), in.end(), out.begin() + 4);
110+
for (std::size_t i = 0; i < N; ++i)
111+
assert(out[i + 4] == in[i]);
112+
}
113+
}
114+
95115
TEST_CONSTEXPR_CXX20 bool test() {
96116
types::for_each(types::cpp17_input_iterator_list<int*>(), TestOutIters());
97117
if (TEST_STD_AT_LEAST_23_OR_RUNTIME_EVALUATED)
@@ -118,7 +138,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
118138
// When non-trivial
119139
{
120140
MoveOnly from[3] = {1, 2, 3};
121-
MoveOnly to[3] = {};
141+
MoveOnly to[3] = {};
122142
std::move(std::begin(from), std::end(from), std::begin(to));
123143
assert(to[0] == MoveOnly(1));
124144
assert(to[1] == MoveOnly(2));
@@ -127,14 +147,22 @@ TEST_CONSTEXPR_CXX20 bool test() {
127147
// When trivial
128148
{
129149
TrivialMoveOnly from[3] = {1, 2, 3};
130-
TrivialMoveOnly to[3] = {};
150+
TrivialMoveOnly to[3] = {};
131151
std::move(std::begin(from), std::end(from), std::begin(to));
132152
assert(to[0] == TrivialMoveOnly(1));
133153
assert(to[1] == TrivialMoveOnly(2));
134154
assert(to[2] == TrivialMoveOnly(3));
135155
}
136156
}
137157

158+
{ // Test vector<bool>::iterator optimization
159+
test_vector_bool<8>();
160+
test_vector_bool<16>();
161+
test_vector_bool<32>();
162+
test_vector_bool<64>();
163+
test_vector_bool<256>();
164+
}
165+
138166
return true;
139167
}
140168

0 commit comments

Comments
 (0)