Skip to content

Commit 892e5b2

Browse files
committed
Optimize {std,ranges}::{fill,fill_n} for segmented iterators
1 parent 5d8e8e8 commit 892e5b2

File tree

6 files changed

+157
-14
lines changed

6 files changed

+157
-14
lines changed

libcxx/include/__algorithm/fill.h

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@
1010
#define _LIBCPP___ALGORITHM_FILL_H
1111

1212
#include <__algorithm/fill_n.h>
13+
#include <__algorithm/for_each_segment.h>
1314
#include <__config>
1415
#include <__iterator/iterator_traits.h>
16+
#include <__iterator/segmented_iterator.h>
17+
#include <__type_traits/enable_if.h>
1518

1619
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
1720
# pragma GCC system_header
@@ -21,23 +24,52 @@ _LIBCPP_BEGIN_NAMESPACE_STD
2124

2225
// fill isn't specialized for std::memset, because the compiler already optimizes the loop to a call to std::memset.
2326

24-
template <class _ForwardIterator, class _Tp>
27+
template <class _ForwardIterator,
28+
class _Sentinel,
29+
class _Tp,
30+
__enable_if_t<__has_forward_iterator_category<_ForwardIterator>::value, int> = 0>
2531
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
26-
__fill(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value, forward_iterator_tag) {
32+
__fill(_ForwardIterator __first, _Sentinel __last, const _Tp& __value) {
2733
for (; __first != __last; ++__first)
2834
*__first = __value;
2935
}
3036

31-
template <class _RandomAccessIterator, class _Tp>
37+
template <class _OutIter, class _Tp>
38+
struct _FillSegment {
39+
using _Traits _LIBCPP_NODEBUG = __segmented_iterator_traits<_OutIter>;
40+
41+
const _Tp& __value_;
42+
43+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit _FillSegment(const _Tp& __value) : __value_(__value) {}
44+
45+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void
46+
operator()(typename _Traits::__local_iterator __lfirst, typename _Traits::__local_iterator __llast) {
47+
std::__fill(__lfirst, __llast, __value_);
48+
}
49+
};
50+
51+
template <class _RandomAccessIterator,
52+
class _Tp,
53+
__enable_if_t<__has_random_access_iterator_category<_RandomAccessIterator>::value &&
54+
!__is_segmented_iterator<_RandomAccessIterator>::value,
55+
int> = 0>
3256
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
33-
__fill(_RandomAccessIterator __first, _RandomAccessIterator __last, const _Tp& __value, random_access_iterator_tag) {
57+
__fill(_RandomAccessIterator __first, _RandomAccessIterator __last, const _Tp& __value) {
3458
std::fill_n(__first, __last - __first, __value);
3559
}
3660

61+
template <class _SegmentedIterator,
62+
class _Tp,
63+
__enable_if_t<__is_segmented_iterator<_SegmentedIterator>::value, int> = 0>
64+
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
65+
__fill(_SegmentedIterator __first, _SegmentedIterator __last, const _Tp& __value) {
66+
std::__for_each_segment(__first, __last, _FillSegment<_SegmentedIterator, _Tp>(__value));
67+
}
68+
3769
template <class _ForwardIterator, class _Tp>
3870
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
3971
fill(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value) {
40-
std::__fill(__first, __last, __value, typename iterator_traits<_ForwardIterator>::iterator_category());
72+
std::__fill(__first, __last, __value);
4173
}
4274

4375
_LIBCPP_END_NAMESPACE_STD

libcxx/include/__algorithm/fill_n.h

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@
1212
#include <__algorithm/min.h>
1313
#include <__config>
1414
#include <__fwd/bit_reference.h>
15+
#include <__iterator/iterator_traits.h>
16+
#include <__iterator/next.h>
17+
#include <__iterator/segmented_iterator.h>
1518
#include <__memory/pointer_traits.h>
19+
#include <__type_traits/enable_if.h>
1620
#include <__utility/convert_to_integral.h>
1721

1822
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -26,9 +30,17 @@ _LIBCPP_BEGIN_NAMESPACE_STD
2630

2731
// fill_n isn't specialized for std::memset, because the compiler already optimizes the loop to a call to std::memset.
2832

29-
template <class _OutputIterator, class _Size, class _Tp>
30-
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
31-
__fill_n(_OutputIterator __first, _Size __n, const _Tp& __value);
33+
template <class _ForwardIterator, class _Tp>
34+
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
35+
fill(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value);
36+
37+
template <class _OutIter, class _Size, class _Tp, __enable_if_t<!__is_segmented_iterator<_OutIter>::value, int> = 0>
38+
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutIter
39+
__fill_n(_OutIter __first, _Size __n, const _Tp& __value) {
40+
for (; __n > 0; ++__first, (void)--__n)
41+
*__first = __value;
42+
return __first;
43+
}
3244

3345
template <bool _FillVal, class _Cp>
3446
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void
@@ -68,12 +80,15 @@ __fill_n(__bit_iterator<_Cp, false> __first, _Size __n, const bool& __value) {
6880
return __first + __n;
6981
}
7082

71-
template <class _OutputIterator, class _Size, class _Tp>
72-
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _OutputIterator
73-
__fill_n(_OutputIterator __first, _Size __n, const _Tp& __value) {
74-
for (; __n > 0; ++__first, (void)--__n)
75-
*__first = __value;
76-
return __first;
83+
template < class _OutIter,
84+
class _Size,
85+
class _Tp,
86+
__enable_if_t<__is_segmented_iterator<_OutIter>::value && __has_forward_iterator_category<_OutIter>::value,
87+
int> = 0>
88+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _OutIter __fill_n(_OutIter __first, _Size __n, const _Tp& __value) {
89+
_OutIter __last = std::next(__first, __n);
90+
std::fill(__first, __last, __value);
91+
return __last;
7792
}
7893

7994
template <class _OutputIterator, class _Size, class _Tp>

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
#include <array>
1818
#include <cassert>
1919
#include <cstddef>
20+
#include <deque>
21+
#include <ranges>
2022
#include <vector>
2123

2224
#include "sized_allocator.h"
@@ -93,6 +95,27 @@ TEST_CONSTEXPR_CXX20 bool test_vector_bool(std::size_t N) {
9395
return true;
9496
}
9597

98+
/*TEST_CONSTEXPR_CXX23*/ void
99+
test_segmented_iterator() { // TODO: Mark this test as TEST_CONSTEXPR_CXX23 when std::deque is constexpr
100+
{ // std::deque iterator
101+
std::deque<int> in(20);
102+
std::deque<int> expected(in.size(), 42);
103+
std::fill(in.begin(), in.end(), 42);
104+
assert(in == expected);
105+
}
106+
107+
#if TEST_STD_VER >= 20
108+
{ // join_view iterator
109+
std::vector<std::vector<int>> v{{1, 2}, {1, 2, 3}, {0, 0}, {3, 4, 5}, {6}, {7, 8, 9, 6}, {0, 1, 2, 3, 0, 1, 2}};
110+
auto jv = std::ranges::join_view(v);
111+
std::fill(jv.begin(), jv.end(), 42);
112+
for (const auto& vec : v)
113+
for (auto n : vec)
114+
assert(n == 42);
115+
}
116+
#endif
117+
}
118+
96119
TEST_CONSTEXPR_CXX20 bool test() {
97120
types::for_each(types::forward_iterator_list<char*>(), Test<char>());
98121
types::for_each(types::forward_iterator_list<int*>(), Test<int>());
@@ -138,6 +161,9 @@ TEST_CONSTEXPR_CXX20 bool test() {
138161
}
139162
}
140163

164+
if (!TEST_IS_CONSTANT_EVALUATED) // TODO: Use TEST_STD_AT_LEAST_23_OR_RUNTIME_EVALUATED when std::deque is made constexpr
165+
test_segmented_iterator();
166+
141167
return true;
142168
}
143169

libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
#include <array>
1818
#include <cassert>
1919
#include <cstddef>
20+
#include <deque>
21+
#include <ranges>
2022
#include <vector>
2123

2224
#include "sized_allocator.h"
@@ -176,6 +178,27 @@ TEST_CONSTEXPR_CXX20 void test_struct_array() {
176178
}
177179
}
178180

181+
/*TEST_CONSTEXPR_CXX23*/ void
182+
test_segmented_iterator() { // TODO: Mark this test as TEST_CONSTEXPR_CXX23 when std::deque is constexpr
183+
{ // std::deque iterator
184+
std::deque<int> in(20);
185+
std::deque<int> expected(in.size(), 42);
186+
std::fill_n(in.begin(), in.size(), 42);
187+
assert(in == expected);
188+
}
189+
190+
#if TEST_STD_VER >= 20
191+
{ // join_view iterator
192+
std::vector<std::vector<int>> v{{1, 2}, {1, 2, 3}, {0, 0}, {3, 4, 5}, {6}, {7, 8, 9, 6}, {0, 1, 2, 3, 0, 1, 2}};
193+
auto jv = std::ranges::join_view(v);
194+
std::fill_n(jv.begin(), std::distance(jv.begin(), jv.end()), 42);
195+
for (const auto& vec : v)
196+
for (auto n : vec)
197+
assert(n == 42);
198+
}
199+
#endif
200+
}
201+
179202
TEST_CONSTEXPR_CXX20 bool test() {
180203
types::for_each(types::forward_iterator_list<char*>(), Test<char>());
181204
types::for_each(types::forward_iterator_list<int*>(), Test<int>());
@@ -225,6 +248,9 @@ TEST_CONSTEXPR_CXX20 bool test() {
225248
}
226249
}
227250

251+
if (!TEST_IS_CONSTANT_EVALUATED) // TODO: Use TEST_STD_AT_LEAST_23_OR_RUNTIME_EVALUATED when std::deque is made constexpr
252+
test_segmented_iterator();
253+
228254
return true;
229255
}
230256

libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/ranges.fill.pass.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <algorithm>
1919
#include <array>
2020
#include <cassert>
21+
#include <deque>
2122
#include <ranges>
2223
#include <string>
2324
#include <vector>
@@ -128,6 +129,24 @@ constexpr bool test_vector_bool(std::size_t N) {
128129
}
129130
#endif
130131

132+
/*TEST_CONSTEXPR_CXX23*/ void
133+
test_segmented_range() { // TODO: Mark this test as TEST_CONSTEXPR_CXX23 when std::deque is constexpr
134+
{ // std::deque
135+
std::deque<int> in(20);
136+
std::deque<int> expected(in.size(), 42);
137+
std::ranges::fill(in, 42);
138+
assert(in == expected);
139+
}
140+
{ // join_view
141+
std::vector<std::vector<int>> v{{1, 2}, {1, 2, 3}, {0, 0}, {3, 4, 5}, {6}, {7, 8, 9, 6}, {0, 1, 2, 3, 0, 1, 2}};
142+
auto jv = std::ranges::join_view(v);
143+
std::ranges::fill(jv, 42);
144+
for (const auto& vec : v)
145+
for (auto n : vec)
146+
assert(n == 42);
147+
}
148+
}
149+
131150
constexpr bool test() {
132151
test_iterators<cpp17_output_iterator<int*>, sentinel_wrapper<cpp17_output_iterator<int*>>>();
133152
test_iterators<cpp20_output_iterator<int*>, sentinel_wrapper<cpp20_output_iterator<int*>>>();
@@ -227,6 +246,9 @@ constexpr bool test() {
227246
}
228247
#endif
229248

249+
if (!TEST_IS_CONSTANT_EVALUATED) // TODO: Use TEST_STD_AT_LEAST_23_OR_RUNTIME_EVALUATED when std::deque is made constexpr
250+
test_segmented_range();
251+
230252
return true;
231253
}
232254

libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/ranges.fill_n.pass.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <algorithm>
1717
#include <array>
1818
#include <cassert>
19+
#include <deque>
1920
#include <ranges>
2021
#include <string>
2122
#include <vector>
@@ -101,6 +102,24 @@ constexpr bool test_vector_bool(std::size_t N) {
101102
}
102103
#endif
103104

105+
/*TEST_CONSTEXPR_CXX23*/ void
106+
test_segmented_range() { // TODO: Mark this test as TEST_CONSTEXPR_CXX23 when std::deque is constexpr
107+
{ // std::deque
108+
std::deque<int> in(20);
109+
std::deque<int> expected(in.size(), 42);
110+
std::ranges::fill_n(std::ranges::begin(in), std::ranges::size(in), 42);
111+
assert(in == expected);
112+
}
113+
{ // join_view
114+
std::vector<std::vector<int>> v{{1, 2}, {1, 2, 3}, {0, 0}, {3, 4, 5}, {6}, {7, 8, 9, 6}, {0, 1, 2, 3, 0, 1, 2}};
115+
auto jv = std::ranges::join_view(v);
116+
std::ranges::fill_n(std::ranges::begin(jv), std::ranges::distance(jv), 42);
117+
for (const auto& vec : v)
118+
for (auto n : vec)
119+
assert(n == 42);
120+
}
121+
}
122+
104123
constexpr bool test() {
105124
test_iterators<cpp17_output_iterator<int*>, sentinel_wrapper<cpp17_output_iterator<int*>>>();
106125
test_iterators<cpp20_output_iterator<int*>, sentinel_wrapper<cpp20_output_iterator<int*>>>();
@@ -175,6 +194,9 @@ constexpr bool test() {
175194
}
176195
#endif
177196

197+
if (!TEST_IS_CONSTANT_EVALUATED) // TODO: Use TEST_STD_AT_LEAST_23_OR_RUNTIME_EVALUATED when std::deque is made constexpr
198+
test_segmented_range();
199+
178200
return true;
179201
}
180202

0 commit comments

Comments
 (0)