Skip to content

Commit cd7a802

Browse files
committed
Optimize ranges::move{,_backward} for vector<bool>::iterator
1 parent 70965ef commit cd7a802

File tree

9 files changed

+374
-110
lines changed

9 files changed

+374
-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
@@ -421,22 +421,6 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __bit_iterator<_Cp, f
421421
return std::__copy_backward_unaligned(__first, __last, __result);
422422
}
423423

424-
// move
425-
426-
template <class _Cp, bool _IsConst>
427-
inline _LIBCPP_HIDE_FROM_ABI __bit_iterator<_Cp, false>
428-
move(__bit_iterator<_Cp, _IsConst> __first, __bit_iterator<_Cp, _IsConst> __last, __bit_iterator<_Cp, false> __result) {
429-
return std::copy(__first, __last, __result);
430-
}
431-
432-
// move_backward
433-
434-
template <class _Cp, bool _IsConst>
435-
inline _LIBCPP_HIDE_FROM_ABI __bit_iterator<_Cp, false> move_backward(
436-
__bit_iterator<_Cp, _IsConst> __first, __bit_iterator<_Cp, _IsConst> __last, __bit_iterator<_Cp, false> __result) {
437-
return std::copy_backward(__first, __last, __result);
438-
}
439-
440424
// swap_ranges
441425

442426
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(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(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_aligned(benchmark::State& state) { bm_ranges_move(state, true); }
42+
static void bm_ranges_move_unaligned(benchmark::State& state) { bm_ranges_move(state, false); }
43+
44+
static void bm_move_aligned(benchmark::State& state) { bm_move(state, true); }
45+
static void bm_move_unaligned(benchmark::State& state) { bm_move(state, false); }
46+
47+
// Test std::ranges::move for vector<bool>::iterator
48+
BENCHMARK(bm_ranges_move_aligned)->Range(8, 1 << 20);
49+
BENCHMARK(bm_ranges_move_unaligned)->Range(8, 1 << 20);
50+
51+
// Test std::move for vector<bool>::iterator
52+
BENCHMARK(bm_move_aligned)->Range(8, 1 << 20);
53+
BENCHMARK(bm_move_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(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_aligned(benchmark::State& state) { bm_ranges_move_backward(state, true); }
42+
static void bm_ranges_move_backward_unaligned(benchmark::State& state) { bm_ranges_move_backward(state, false); }
43+
44+
static void bm_move_backward_aligned(benchmark::State& state) { bm_move_backward(state, true); }
45+
static void bm_move_backward_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_aligned)->Range(8, 1 << 20);
49+
BENCHMARK(bm_ranges_move_backward_unaligned)->Range(8, 1 << 20);
50+
51+
// Test std::move_backward for vector<bool>::iterator
52+
BENCHMARK(bm_move_backward_aligned)->Range(8, 1 << 20);
53+
BENCHMARK(bm_move_backward_unaligned)->Range(8, 1 << 20);
54+
55+
BENCHMARK_MAIN();

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

Lines changed: 42 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,28 @@ struct Test1OutIters {
9293
}
9394
};
9495

96+
template <std::size_t N>
97+
struct TestBitIter {
98+
std::vector<bool> in;
99+
TEST_CONSTEXPR_CXX20 TestBitIter() : in(N, false) {
100+
for (std::size_t i = 0; i < N; i += 2)
101+
in[i] = true;
102+
}
103+
TEST_CONSTEXPR_CXX20 void operator()() {
104+
{ // Test move with aligned bytes
105+
std::vector<bool> out(N);
106+
std::move(in.begin(), in.end(), out.begin());
107+
assert(in == out);
108+
}
109+
{ // Test move with unaligned bytes
110+
std::vector<bool> out(N + 8);
111+
std::move(in.begin(), in.end(), out.begin() + 4);
112+
for (std::size_t i = 0; i < N; ++i)
113+
assert(out[i + 4] == in[i]);
114+
}
115+
}
116+
};
117+
95118
TEST_CONSTEXPR_CXX20 bool test() {
96119
types::for_each(types::cpp17_input_iterator_list<int*>(), TestOutIters());
97120
if (TEST_STD_AT_LEAST_23_OR_RUNTIME_EVALUATED)
@@ -118,7 +141,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
118141
// When non-trivial
119142
{
120143
MoveOnly from[3] = {1, 2, 3};
121-
MoveOnly to[3] = {};
144+
MoveOnly to[3] = {};
122145
std::move(std::begin(from), std::end(from), std::begin(to));
123146
assert(to[0] == MoveOnly(1));
124147
assert(to[1] == MoveOnly(2));
@@ -127,14 +150,22 @@ TEST_CONSTEXPR_CXX20 bool test() {
127150
// When trivial
128151
{
129152
TrivialMoveOnly from[3] = {1, 2, 3};
130-
TrivialMoveOnly to[3] = {};
153+
TrivialMoveOnly to[3] = {};
131154
std::move(std::begin(from), std::end(from), std::begin(to));
132155
assert(to[0] == TrivialMoveOnly(1));
133156
assert(to[1] == TrivialMoveOnly(2));
134157
assert(to[2] == TrivialMoveOnly(3));
135158
}
136159
}
137160

161+
{ // Test vector<bool>::iterator optimization
162+
TestBitIter<8>()();
163+
TestBitIter<16>()();
164+
TestBitIter<32>()();
165+
TestBitIter<64>()();
166+
TestBitIter<1024>()();
167+
}
168+
138169
return true;
139170
}
140171

0 commit comments

Comments
 (0)