Skip to content

Commit d841abe

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

File tree

10 files changed

+376
-112
lines changed

10 files changed

+376
-112
lines changed

libcxx/docs/ReleaseNotes/21.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ Implemented Papers
4343
Improvements and New Features
4444
-----------------------------
4545

46-
- The ``std::ranges::{copy, copy_n, copy_backward}`` algorithms have been optimized for ``std::vector<bool>::iterator``\s,
47-
resulting in a performance improvement of up to 2000x.
46+
- The ``std::ranges::{copy, copy_n, copy_backward, move, move_backward}`` algorithms have been optimized for
47+
``std::vector<bool>::iterator``, resulting in a performance improvement of up to 2000x.
4848

4949
- Updated formatting library to Unicode 16.0.0.
5050

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
@@ -210,22 +210,6 @@ private:
210210
__mask_(__m) {}
211211
};
212212

213-
// move
214-
215-
template <class _Cp, bool _IsConst>
216-
inline _LIBCPP_HIDE_FROM_ABI __bit_iterator<_Cp, false>
217-
move(__bit_iterator<_Cp, _IsConst> __first, __bit_iterator<_Cp, _IsConst> __last, __bit_iterator<_Cp, false> __result) {
218-
return std::copy(__first, __last, __result);
219-
}
220-
221-
// move_backward
222-
223-
template <class _Cp, bool _IsConst>
224-
inline _LIBCPP_HIDE_FROM_ABI __bit_iterator<_Cp, false> move_backward(
225-
__bit_iterator<_Cp, _IsConst> __first, __bit_iterator<_Cp, _IsConst> __last, __bit_iterator<_Cp, false> __result) {
226-
return std::copy_backward(__first, __last, __result);
227-
}
228-
229213
// swap_ranges
230214

231215
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 << 16)->DenseRange(102400, 204800, 4096);
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 << 16)->DenseRange(102400, 204800, 4096);
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: 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,26 @@ struct Test1OutIters {
9293
}
9394
};
9495

96+
TEST_CONSTEXPR_CXX20 bool test_vector_bool(std::size_t N) {
97+
std::vector<bool> in(N, false);
98+
for (std::size_t i = 0; i < N; i += 2)
99+
in[i] = true;
100+
101+
{ // Test move with aligned bytes
102+
std::vector<bool> out(N);
103+
std::move(in.begin(), in.end(), out.begin());
104+
assert(in == out);
105+
}
106+
{ // Test move with unaligned bytes
107+
std::vector<bool> out(N + 8);
108+
std::move(in.begin(), in.end(), out.begin() + 4);
109+
for (std::size_t i = 0; i < N; ++i)
110+
assert(out[i + 4] == in[i]);
111+
}
112+
113+
return true;
114+
}
115+
95116
TEST_CONSTEXPR_CXX20 bool test() {
96117
types::for_each(types::cpp17_input_iterator_list<int*>(), TestOutIters());
97118
if (TEST_STD_AT_LEAST_23_OR_RUNTIME_EVALUATED)
@@ -118,7 +139,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
118139
// When non-trivial
119140
{
120141
MoveOnly from[3] = {1, 2, 3};
121-
MoveOnly to[3] = {};
142+
MoveOnly to[3] = {};
122143
std::move(std::begin(from), std::end(from), std::begin(to));
123144
assert(to[0] == MoveOnly(1));
124145
assert(to[1] == MoveOnly(2));
@@ -127,14 +148,24 @@ TEST_CONSTEXPR_CXX20 bool test() {
127148
// When trivial
128149
{
129150
TrivialMoveOnly from[3] = {1, 2, 3};
130-
TrivialMoveOnly to[3] = {};
151+
TrivialMoveOnly to[3] = {};
131152
std::move(std::begin(from), std::end(from), std::begin(to));
132153
assert(to[0] == TrivialMoveOnly(1));
133154
assert(to[1] == TrivialMoveOnly(2));
134155
assert(to[2] == TrivialMoveOnly(3));
135156
}
136157
}
137158

159+
{ // Test vector<bool>::iterator optimization
160+
assert(test_vector_bool(8));
161+
assert(test_vector_bool(19));
162+
assert(test_vector_bool(32));
163+
assert(test_vector_bool(49));
164+
assert(test_vector_bool(64));
165+
assert(test_vector_bool(199));
166+
assert(test_vector_bool(256));
167+
}
168+
138169
return true;
139170
}
140171

0 commit comments

Comments
 (0)