Skip to content

Commit 438e43d

Browse files
committed
Fix review comments
1 parent 64dcb8c commit 438e43d

File tree

9 files changed

+124
-186
lines changed

9 files changed

+124
-186
lines changed

libcxx/docs/ReleaseNotes/21.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,14 @@ Improvements and New Features
7272
in C++23 and later.
7373
=======
7474
- The ``std::ranges::for_each`` and ``std::ranges::for_each_n`` algorithms have been optimized for segmented iterators,
75+
<<<<<<< HEAD
7576
resulting in performance improvements of up to 21.3x for ``std::deque::iterator`` segmented inputs and 24.9x for
7677
``join_view`` of ``vector<vector<T>>``.
7778
>>>>>>> 50ac206d4a13 (Apply optimization for join_view segmented iterators)
79+
=======
80+
resulting in performance improvements of up to 21.3x for ``std::deque::iterator`` and 24.9x for ``join_view`` of
81+
``vector<vector<char>>``.
82+
>>>>>>> 590136ba0d9f (Fix review comments)
7883

7984
- The ``std::for_each_n`` algorithm has been optimized for segmented iterators, resulting in a performance improvement of
8085
up to 17.7x for ``std::deque<short>`` iterators, and up to 13.9x for ``std::join_view<vector<vector<short>>>`` iterators.

libcxx/include/__algorithm/for_each.h

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
#include <__algorithm/for_each_segment.h>
1414
#include <__config>
15+
#include <__functional/identity.h>
16+
#include <__functional/invoke.h>
1517
#include <__iterator/segmented_iterator.h>
1618
#include <__type_traits/enable_if.h>
1719

@@ -21,29 +23,33 @@
2123

2224
_LIBCPP_BEGIN_NAMESPACE_STD
2325

24-
template <class _InputIterator, class _Sent, class _Func>
25-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __for_each(_InputIterator __first, _Sent __last, _Func& __f) {
26+
template <class _InputIterator, class _Sent, class _Func, class _Proj>
27+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _InputIterator
28+
__for_each(_InputIterator __first, _Sent __last, _Func& __f, _Proj& __proj) {
2629
for (; __first != __last; ++__first)
27-
__f(*__first);
30+
std::invoke(__f, std::invoke(__proj, *__first));
31+
return __first;
2832
}
2933

3034
#ifndef _LIBCPP_CXX03_LANG
3135
template <class _SegmentedIterator,
3236
class _Function,
37+
class _Proj,
3338
__enable_if_t<__is_segmented_iterator<_SegmentedIterator>::value, int> = 0>
3439
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
35-
__for_each(_SegmentedIterator __first, _SegmentedIterator __last, _Function& __func) {
40+
__for_each(_SegmentedIterator __first, _SegmentedIterator __last, _Function& __func, _Proj& __proj) {
3641
using __local_iterator_t = typename __segmented_iterator_traits<_SegmentedIterator>::__local_iterator;
3742
std::__for_each_segment(__first, __last, [&](__local_iterator_t __lfirst, __local_iterator_t __llast) {
38-
std::__for_each(__lfirst, __llast, __func);
43+
std::__for_each(__lfirst, __llast, __func, __proj);
3944
});
4045
}
4146
#endif // !_LIBCPP_CXX03_LANG
4247

4348
template <class _InputIterator, class _Function>
4449
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Function
4550
for_each(_InputIterator __first, _InputIterator __last, _Function __f) {
46-
std::__for_each(__first, __last, __f);
51+
__identity __proj;
52+
std::__for_each(__first, __last, __f, __proj);
4753
return __f;
4854
}
4955

libcxx/include/__algorithm/for_each_n.h

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@
1313
#include <__algorithm/for_each.h>
1414
#include <__algorithm/for_each_n_segment.h>
1515
#include <__config>
16+
#include <__functional/identity.h>
17+
#include <__functional/invoke.h>
1618
#include <__iterator/iterator_traits.h>
17-
#include <__iterator/next.h>
1819
#include <__iterator/segmented_iterator.h>
1920
#include <__type_traits/disjunction.h>
2021
#include <__type_traits/enable_if.h>
@@ -34,16 +35,17 @@ _LIBCPP_BEGIN_NAMESPACE_STD
3435
template <class _InputIterator,
3536
class _Size,
3637
class _Func,
38+
class _Proj,
3739
__enable_if_t<!__has_random_access_iterator_category<_InputIterator>::value &&
3840
_Or< _Not<__is_segmented_iterator<_InputIterator> >,
3941
_Not<__has_random_access_local_iterator<_InputIterator> > >::value,
4042
int> = 0>
4143
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _InputIterator
42-
__for_each_n(_InputIterator __first, _Size __orig_n, _Func& __f) {
44+
__for_each_n(_InputIterator __first, _Size __orig_n, _Func& __f, _Proj& __proj) {
4345
typedef decltype(std::__convert_to_integral(__orig_n)) _IntegralSize;
4446
_IntegralSize __n = __orig_n;
4547
while (__n > 0) {
46-
__f(*__first);
48+
std::invoke(__f, std::invoke(__proj, *__first));
4749
++__first;
4850
--__n;
4951
}
@@ -53,29 +55,31 @@ __for_each_n(_InputIterator __first, _Size __orig_n, _Func& __f) {
5355
template <class _RandIter,
5456
class _Size,
5557
class _Func,
58+
class _Proj,
5659
__enable_if_t<__has_random_access_iterator_category<_RandIter>::value, int> = 0>
5760
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _RandIter
5861
__for_each_n(_RandIter __first, _Size __orig_n, _Func& __f) {
5962
typename std::iterator_traits<_RandIter>::difference_type __n = __orig_n;
6063
auto __last = __first + __n;
61-
std::__for_each(__first, __last, __f);
64+
std::__for_each(__first, __last, __f, __proj);
6265
return std::move(__last);
6366
}
6467

6568
#ifndef _LIBCPP_CXX03_LANG
6669
template <class _SegmentedIterator,
6770
class _Size,
6871
class _Func,
72+
class _Proj,
6973
__enable_if_t<!__has_random_access_iterator_category<_SegmentedIterator>::value &&
7074
__is_segmented_iterator<_SegmentedIterator>::value &&
7175
__has_random_access_iterator_category<
7276
typename __segmented_iterator_traits<_SegmentedIterator>::__local_iterator>::value,
7377
int> = 0>
7478
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _SegmentedIterator
75-
__for_each_n(_SegmentedIterator __first, _Size __orig_n, _Func& __f) {
79+
__for_each_n(_SegmentedIterator __first, _Size __orig_n, _Func& __f, _Proj& __proj) {
7680
using __local_iterator_t = typename __segmented_iterator_traits<_SegmentedIterator>::__local_iterator;
7781
return std::__for_each_n_segment(__first, __orig_n, [&](__local_iterator_t __lfirst, __local_iterator_t __llast) {
78-
std::__for_each(__lfirst, __llast, __f);
82+
std::__for_each(__lfirst, __llast, __f, __proj);
7983
});
8084
}
8185
#endif // !_LIBCPP_CXX03_LANG
@@ -85,7 +89,8 @@ __for_each_n(_SegmentedIterator __first, _Size __orig_n, _Func& __f) {
8589
template <class _InputIterator, class _Size, class _Function>
8690
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _InputIterator
8791
for_each_n(_InputIterator __first, _Size __orig_n, _Function __f) {
88-
return std::__for_each_n(__first, __orig_n, __f);
92+
__identity __proj;
93+
return std::__for_each_n(__first, __orig_n, __f, __proj);
8994
}
9095

9196
#endif // _LIBCPP_STD_VER >= 17

libcxx/include/__algorithm/for_each_n_segment.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,13 @@
1010
#define _LIBCPP___ALGORITHM_FOR_EACH_N_SEGMENT_H
1111

1212
#include <__config>
13+
<<<<<<< HEAD
1314
#include <__iterator/iterator_traits.h>
15+
=======
16+
#include <__iterator/distance.h>
17+
#include <__iterator/iterator_traits.h>
18+
#include <__iterator/next.h>
19+
>>>>>>> 4a86118918e8 (Fix review comments)
1420
#include <__iterator/segmented_iterator.h>
1521

1622
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)

libcxx/include/__algorithm/ranges_for_each.h

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,19 +44,13 @@ struct __for_each {
4444
template <class _Iter, class _Sent, class _Proj, class _Func>
4545
_LIBCPP_HIDE_FROM_ABI constexpr static for_each_result<_Iter, _Func>
4646
__for_each_impl(_Iter __first, _Sent __last, _Func& __func, _Proj& __proj) {
47-
if constexpr (std::assignable_from<_Iter&, _Sent>) {
48-
_Iter __end = std::move(__last);
49-
std::for_each(__first, __end, [&](auto&& __val) { std::invoke(__func, std::invoke(__proj, __val)); });
50-
return {std::move(__end), std::move(__func)};
51-
} else if constexpr (sized_sentinel_for<_Sent, _Iter>) {
52-
auto __end = std::for_each_n(__first, __last - __first, [&](auto&& __val) {
53-
std::invoke(__func, std::invoke(__proj, __val));
54-
});
47+
if constexpr (!std::assignable_from<_Iter&, _Sent> && sized_sentinel_for<_Sent, _Iter>) {
48+
auto __n = __last - __first;
49+
auto __end = std::__for_each_n(std::move(__first), __n, __func, __proj);
5550
return {std::move(__end), std::move(__func)};
5651
} else {
57-
for (; __first != __last; ++__first)
58-
std::invoke(__func, std::invoke(__proj, *__first));
59-
return {std::move(__first), std::move(__func)};
52+
auto __end = std::__for_each(std::move(__first), std::move(__last), __func, __proj);
53+
return {std::move(__end), std::move(__func)};
6054
}
6155
}
6256

libcxx/include/__algorithm/ranges_for_each_n.h

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
#include <__iterator/concepts.h>
1818
#include <__iterator/incrementable_traits.h>
1919
#include <__iterator/iterator_traits.h>
20-
#include <__iterator/next.h>
2120
#include <__iterator/projected.h>
2221
#include <__ranges/concepts.h>
2322
#include <__utility/move.h>
@@ -42,17 +41,8 @@ struct __for_each_n {
4241
template <input_iterator _Iter, class _Proj = identity, indirectly_unary_invocable<projected<_Iter, _Proj>> _Func>
4342
_LIBCPP_HIDE_FROM_ABI constexpr for_each_n_result<_Iter, _Func>
4443
operator()(_Iter __first, iter_difference_t<_Iter> __count, _Func __func, _Proj __proj = {}) const {
45-
if constexpr (forward_iterator<_Iter>) {
46-
auto __f = [&](auto&& __val) { std::invoke(__func, std::invoke(__proj, __val)); };
47-
auto __last = std::for_each_n(__first, __count, __f);
48-
return {std::move(__last), std::move(__func)};
49-
} else {
50-
while (__count-- > 0) {
51-
std::invoke(__func, std::invoke(__proj, *__first));
52-
++__first;
53-
}
54-
return {std::move(__first), std::move(__func)};
55-
}
44+
auto __last = std::__for_each_n(std::move(__first), __count, __func, __proj);
45+
return {std::move(__last), std::move(__func)};
5646
}
5747
};
5848

libcxx/test/benchmarks/algorithms/nonmodifying/for_each.bench.cpp

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <cstddef>
1313
#include <deque>
1414
#include <list>
15+
#include <ranges>
1516
#include <string>
1617
#include <vector>
1718

@@ -48,20 +49,6 @@ int main(int argc, char** argv) {
4849
->Arg(1 << 16)
4950
->Arg(1 << 18);
5051
};
51-
bm.operator()<std::vector<char>>("std::for_each(vector<char>)", std_for_each);
52-
bm.operator()<std::deque<char>>("std::for_each(deque<char>)", std_for_each);
53-
bm.operator()<std::list<char>>("std::for_each(list<char>)", std_for_each);
54-
bm.operator()<std::vector<char>>("rng::for_each(vector<char>)", std::ranges::for_each);
55-
bm.operator()<std::deque<char>>("rng::for_each(deque<char>)", std::ranges::for_each);
56-
bm.operator()<std::list<char>>("rng::for_each(list<char>)", std::ranges::for_each);
57-
58-
bm.operator()<std::vector<short>>("std::for_each(vector<short>)", std_for_each);
59-
bm.operator()<std::deque<short>>("std::for_each(deque<short>)", std_for_each);
60-
bm.operator()<std::list<short>>("std::for_each(list<short>)", std_for_each);
61-
bm.operator()<std::vector<short>>("rng::for_each(vector<short>)", std::ranges::for_each);
62-
bm.operator()<std::deque<short>>("rng::for_each(deque<short>)", std::ranges::for_each);
63-
bm.operator()<std::list<short>>("rng::for_each(list<short>)", std::ranges::for_each);
64-
6552
bm.operator()<std::vector<int>>("std::for_each(vector<int>)", std_for_each);
6653
bm.operator()<std::deque<int>>("std::for_each(deque<int>)", std_for_each);
6754
bm.operator()<std::list<int>>("std::for_each(list<int>)", std_for_each);
@@ -70,6 +57,47 @@ int main(int argc, char** argv) {
7057
bm.operator()<std::list<int>>("rng::for_each(list<int>)", std::ranges::for_each);
7158
}
7259

60+
// {std,ranges}::for_each for join_view
61+
{
62+
auto bm = []<class Container>(std::string name, auto for_each) {
63+
using C1 = typename Container::value_type;
64+
using ElemType = typename C1::value_type;
65+
66+
benchmark::RegisterBenchmark(
67+
name,
68+
[for_each](auto& st) {
69+
std::size_t const size = st.range(0);
70+
std::size_t const seg_size = 256;
71+
std::size_t const segments = (size + seg_size - 1) / seg_size;
72+
Container c(segments);
73+
for (std::size_t i = 0, n = size; i < segments; ++i, n -= seg_size) {
74+
c[i].resize(std::min(seg_size, n), ElemType(1));
75+
}
76+
77+
auto view = c | std::views::join;
78+
auto first = view.begin();
79+
auto last = view.end();
80+
81+
for ([[maybe_unused]] auto _ : st) {
82+
benchmark::DoNotOptimize(c);
83+
auto result = for_each(first, last, [](ElemType& x) { x = std::clamp<ElemType>(x, 10, 100); });
84+
benchmark::DoNotOptimize(result);
85+
}
86+
})
87+
->Arg(8)
88+
->Arg(32)
89+
->Arg(50) // non power-of-two
90+
->Arg(1024)
91+
->Arg(4096)
92+
->Arg(8192)
93+
->Arg(1 << 14)
94+
->Arg(1 << 16)
95+
->Arg(1 << 18);
96+
};
97+
bm.operator()<std::vector<std::vector<int>>>("std::for_each(join_view(vector<vector<int>>))", std_for_each);
98+
bm.operator()<std::vector<std::vector<int>>>("rng::for_each(join_view(vector<vector<int>>)", std::ranges::for_each);
99+
}
100+
73101
benchmark::Initialize(&argc, argv);
74102
benchmark::RunSpecifiedBenchmarks();
75103
benchmark::Shutdown();

0 commit comments

Comments
 (0)