Skip to content

Commit 1de13f4

Browse files
committed
[libc++] PSTL dispatching mechanism overhaul
The experimental PSTL's current dispatching mechanism was designed with flexibility in mind. However, while reviewing the in-progress OpenMP backend, I realized that the dispatching mechanism based on ADL and default definitions in the frontend had several downsides. To name a few: 1. The dispatching of an algorithm to the back-end and its default implementation is bundled together via `_LIBCPP_PSTL_CUSTOMIZATION_POINT`. This makes the dispatching really confusing and leads to annoyances such as variable shadowing and weird lambda captures in the front-end. 2. The distinction between back-end functions and front-end algorithms is not as clear as it could be, which led us to call one where we meant the other in a few cases. This is bad due to the exception requirements of the PSTL: calling a front-end algorithm inside the implementation of a back-end is incorrect for exception-safety. 3. There are two levels of back-end dispatching in the PSTL, which treat CPU backends as a special case. This was confusing and not as flexible as we'd like. For example, there was no straightforward way to dispatch all uses of `unseq` to a specific back-end from the OpenMP backend, or for CPU backends to fall back on each other. This patch rewrites the backend dispatching mechanism to solve these problems, but doesn't touch any of the actual implementation of algorithms. Specifically, this rewrite has the following characteristics: - All back-ends are full top-level backends defining all the basis operations required by the PSTL. This is made realistic for CPU backends by providing the CPU-based basis operations as simple helpers that can easily be reused when defining the PSTL basis operations. - The default definitions for algorithms are separated from their dispatching logic and grouped in families instead, based on the basis operation they require for their default implementation. - The front-end is thus simplified a whole lot and made very consistent for all algorithms, which makes it easier to audit the front-end for things like exception-correctness, appropriate forwarding, etc. Fixes #70718
1 parent ea20647 commit 1de13f4

27 files changed

+1874
-2121
lines changed

libcxx/include/CMakeLists.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ set(files
7373
__algorithm/pop_heap.h
7474
__algorithm/prev_permutation.h
7575
__algorithm/pstl.h
76-
__algorithm/pstl_frontend_dispatch.h
7776
__algorithm/push_heap.h
7877
__algorithm/ranges_adjacent_find.h
7978
__algorithm/ranges_all_of.h
@@ -570,6 +569,8 @@ set(files
570569
__numeric/transform_reduce.h
571570
__ostream/basic_ostream.h
572571
__ostream/print.h
572+
__pstl/backend_fwd.h
573+
__pstl/backends/default.h
573574
__pstl/backends/libdispatch.h
574575
__pstl/backends/serial.h
575576
__pstl/backends/std_thread.h
@@ -584,6 +585,8 @@ set(files
584585
__pstl/cpu_algos/stable_sort.h
585586
__pstl/cpu_algos/transform.h
586587
__pstl/cpu_algos/transform_reduce.h
588+
__pstl/dispatch.h
589+
__pstl/run_backend.h
587590
__random/bernoulli_distribution.h
588591
__random/binomial_distribution.h
589592
__random/cauchy_distribution.h

libcxx/include/__algorithm/pstl.h

Lines changed: 219 additions & 922 deletions
Large diffs are not rendered by default.

libcxx/include/__algorithm/pstl_frontend_dispatch.h

Lines changed: 0 additions & 44 deletions
This file was deleted.

libcxx/include/__numeric/pstl.h

Lines changed: 66 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,20 @@
99
#ifndef _LIBCPP___NUMERIC_PSTL_H
1010
#define _LIBCPP___NUMERIC_PSTL_H
1111

12-
#include <__algorithm/pstl_frontend_dispatch.h>
1312
#include <__config>
1413
#include <__functional/identity.h>
1514
#include <__functional/operations.h>
1615
#include <__iterator/cpp17_iterator_concepts.h>
1716
#include <__iterator/iterator_traits.h>
18-
#include <__numeric/transform_reduce.h>
17+
#include <__pstl/backend_fwd.h>
1918
#include <__pstl/configuration.h>
19+
#include <__pstl/dispatch.h>
20+
#include <__pstl/run_backend.h>
21+
#include <__type_traits/enable_if.h>
2022
#include <__type_traits/is_execution_policy.h>
23+
#include <__type_traits/remove_cvref.h>
24+
#include <__utility/forward.h>
2125
#include <__utility/move.h>
22-
#include <optional>
2326

2427
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
2528
# pragma GCC system_header
@@ -33,30 +36,50 @@ _LIBCPP_PUSH_MACROS
3336
_LIBCPP_BEGIN_NAMESPACE_STD
3437

3538
template <class _ExecutionPolicy,
36-
class _ForwardIterator1,
37-
class _ForwardIterator2,
39+
class _ForwardIterator,
3840
class _Tp,
39-
class _BinaryOperation1,
40-
class _BinaryOperation2,
41+
class _BinaryOperation,
4142
class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
4243
enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
43-
_LIBCPP_HIDE_FROM_ABI optional<_Tp> __transform_reduce(
44-
_ExecutionPolicy&&,
45-
_ForwardIterator1&& __first1,
46-
_ForwardIterator1&& __last1,
47-
_ForwardIterator2&& __first2,
48-
_Tp&& __init,
49-
_BinaryOperation1&& __reduce,
50-
_BinaryOperation2&& __transform) noexcept {
51-
using _Backend = typename __select_backend<_RawPolicy>::type;
52-
return std::__pstl_transform_reduce<_RawPolicy>(
53-
_Backend{},
54-
std::move(__first1),
55-
std::move(__last1),
56-
std::move(__first2),
44+
_LIBCPP_HIDE_FROM_ABI _Tp reduce(
45+
_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Tp __init, _BinaryOperation __op) {
46+
_LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "reduce requires ForwardIterators");
47+
using _Implementation = __pstl::__dispatch<__pstl::__reduce, __pstl::__current_configuration, _RawPolicy>;
48+
return __pstl::__run_backend<_Implementation>(
49+
std::forward<_ExecutionPolicy>(__policy),
50+
std::move(__first),
51+
std::move(__last),
5752
std::move(__init),
58-
std::move(__reduce),
59-
std::move(__transform));
53+
std::move(__op));
54+
}
55+
56+
template <class _ExecutionPolicy,
57+
class _ForwardIterator,
58+
class _Tp,
59+
class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
60+
enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
61+
_LIBCPP_HIDE_FROM_ABI _Tp
62+
reduce(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Tp __init) {
63+
_LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "reduce requires ForwardIterators");
64+
using _Implementation = __pstl::__dispatch<__pstl::__reduce, __pstl::__current_configuration, _RawPolicy>;
65+
return __pstl::__run_backend<_Implementation>(
66+
std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), std::move(__init), plus{});
67+
}
68+
69+
template <class _ExecutionPolicy,
70+
class _ForwardIterator,
71+
class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
72+
enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
73+
_LIBCPP_HIDE_FROM_ABI __iter_value_type<_ForwardIterator>
74+
reduce(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last) {
75+
_LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "reduce requires ForwardIterators");
76+
using _Implementation = __pstl::__dispatch<__pstl::__reduce, __pstl::__current_configuration, _RawPolicy>;
77+
return __pstl::__run_backend<_Implementation>(
78+
std::forward<_ExecutionPolicy>(__policy),
79+
std::move(__first),
80+
std::move(__last),
81+
__iter_value_type<_ForwardIterator>(),
82+
plus{});
6083
}
6184

6285
template <class _ExecutionPolicy,
@@ -77,18 +100,16 @@ _LIBCPP_HIDE_FROM_ABI _Tp transform_reduce(
77100
_BinaryOperation2 __transform) {
78101
_LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "transform_reduce requires ForwardIterators");
79102
_LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "transform_reduce requires ForwardIterators");
80-
auto __res = std::__transform_reduce(
81-
__policy,
103+
using _Implementation =
104+
__pstl::__dispatch<__pstl::__transform_reduce_binary, __pstl::__current_configuration, _RawPolicy>;
105+
return __pstl::__run_backend<_Implementation>(
106+
std::forward<_ExecutionPolicy>(__policy),
82107
std::move(__first1),
83108
std::move(__last1),
84109
std::move(__first2),
85110
std::move(__init),
86111
std::move(__reduce),
87112
std::move(__transform));
88-
89-
if (!__res)
90-
std::__throw_bad_alloc();
91-
return *std::move(__res);
92113
}
93114

94115
// This overload doesn't get a customization point because it's trivial to detect (through e.g.
@@ -97,7 +118,8 @@ template <class _ExecutionPolicy,
97118
class _ForwardIterator1,
98119
class _ForwardIterator2,
99120
class _Tp,
100-
enable_if_t<is_execution_policy_v<__remove_cvref_t<_ExecutionPolicy>>, int> = 0>
121+
class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
122+
enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
101123
_LIBCPP_HIDE_FROM_ABI _Tp transform_reduce(
102124
_ExecutionPolicy&& __policy,
103125
_ForwardIterator1 __first1,
@@ -106,31 +128,16 @@ _LIBCPP_HIDE_FROM_ABI _Tp transform_reduce(
106128
_Tp __init) {
107129
_LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "transform_reduce requires ForwardIterators");
108130
_LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "transform_reduce requires ForwardIterators");
109-
return std::transform_reduce(__policy, __first1, __last1, __first2, __init, plus{}, multiplies{});
110-
}
111-
112-
template <class _ExecutionPolicy,
113-
class _ForwardIterator,
114-
class _Tp,
115-
class _BinaryOperation,
116-
class _UnaryOperation,
117-
class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
118-
enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
119-
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<__remove_cvref_t<_Tp>> __transform_reduce(
120-
_ExecutionPolicy&&,
121-
_ForwardIterator&& __first,
122-
_ForwardIterator&& __last,
123-
_Tp&& __init,
124-
_BinaryOperation&& __reduce,
125-
_UnaryOperation&& __transform) noexcept {
126-
using _Backend = typename __select_backend<_RawPolicy>::type;
127-
return std::__pstl_transform_reduce<_RawPolicy>(
128-
_Backend{},
129-
std::move(__first),
130-
std::move(__last),
131+
using _Implementation =
132+
__pstl::__dispatch<__pstl::__transform_reduce_binary, __pstl::__current_configuration, _RawPolicy>;
133+
return __pstl::__run_backend<_Implementation>(
134+
std::forward<_ExecutionPolicy>(__policy),
135+
std::move(__first1),
136+
std::move(__last1),
137+
std::move(__first2),
131138
std::move(__init),
132-
std::move(__reduce),
133-
std::move(__transform));
139+
plus{},
140+
multiplies{});
134141
}
135142

136143
template <class _ExecutionPolicy,
@@ -148,86 +155,14 @@ _LIBCPP_HIDE_FROM_ABI _Tp transform_reduce(
148155
_BinaryOperation __reduce,
149156
_UnaryOperation __transform) {
150157
_LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "transform_reduce requires ForwardIterators");
151-
auto __res = std::__transform_reduce(
152-
__policy, std::move(__first), std::move(__last), std::move(__init), std::move(__reduce), std::move(__transform));
153-
if (!__res)
154-
std::__throw_bad_alloc();
155-
return *std::move(__res);
156-
}
157-
158-
template <class>
159-
void __pstl_reduce();
160-
161-
template <class _ExecutionPolicy,
162-
class _ForwardIterator,
163-
class _Tp,
164-
class _BinaryOperation = plus<>,
165-
class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
166-
enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
167-
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_Tp>
168-
__reduce(_ExecutionPolicy&& __policy,
169-
_ForwardIterator&& __first,
170-
_ForwardIterator&& __last,
171-
_Tp&& __init,
172-
_BinaryOperation&& __op = {}) noexcept {
173-
return std::__pstl_frontend_dispatch(
174-
_LIBCPP_PSTL_CUSTOMIZATION_POINT(__pstl_reduce, _RawPolicy),
175-
[&__policy](_ForwardIterator __g_first, _ForwardIterator __g_last, _Tp __g_init, _BinaryOperation __g_op) {
176-
return std::__transform_reduce(
177-
__policy, std::move(__g_first), std::move(__g_last), std::move(__g_init), std::move(__g_op), __identity{});
178-
},
158+
using _Implementation = __pstl::__dispatch<__pstl::__transform_reduce, __pstl::__current_configuration, _RawPolicy>;
159+
return __pstl::__run_backend<_Implementation>(
160+
std::forward<_ExecutionPolicy>(__policy),
179161
std::move(__first),
180162
std::move(__last),
181163
std::move(__init),
182-
std::move(__op));
183-
}
184-
185-
template <class _ExecutionPolicy,
186-
class _ForwardIterator,
187-
class _Tp,
188-
class _BinaryOperation = plus<>,
189-
class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
190-
enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
191-
_LIBCPP_HIDE_FROM_ABI _Tp
192-
reduce(_ExecutionPolicy&& __policy,
193-
_ForwardIterator __first,
194-
_ForwardIterator __last,
195-
_Tp __init,
196-
_BinaryOperation __op = {}) {
197-
_LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "reduce requires ForwardIterators");
198-
auto __res = std::__reduce(__policy, std::move(__first), std::move(__last), std::move(__init), std::move(__op));
199-
if (!__res)
200-
std::__throw_bad_alloc();
201-
return *std::move(__res);
202-
}
203-
204-
template <class _ExecutionPolicy,
205-
class _ForwardIterator,
206-
class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
207-
enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
208-
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<__iter_value_type<_ForwardIterator>>
209-
__reduce(_ExecutionPolicy&& __policy, _ForwardIterator&& __first, _ForwardIterator&& __last) noexcept {
210-
return std::__pstl_frontend_dispatch(
211-
_LIBCPP_PSTL_CUSTOMIZATION_POINT(__pstl_reduce, _RawPolicy),
212-
[&__policy](_ForwardIterator __g_first, _ForwardIterator __g_last) {
213-
return std::__reduce(
214-
__policy, std::move(__g_first), std::move(__g_last), __iter_value_type<_ForwardIterator>());
215-
},
216-
std::move(__first),
217-
std::move(__last));
218-
}
219-
220-
template <class _ExecutionPolicy,
221-
class _ForwardIterator,
222-
class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
223-
enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
224-
_LIBCPP_HIDE_FROM_ABI __iter_value_type<_ForwardIterator>
225-
reduce(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last) {
226-
_LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "reduce requires ForwardIterators");
227-
auto __res = std::__reduce(__policy, std::move(__first), std::move(__last));
228-
if (!__res)
229-
std::__throw_bad_alloc();
230-
return *std::move(__res);
164+
std::move(__reduce),
165+
std::move(__transform));
231166
}
232167

233168
_LIBCPP_END_NAMESPACE_STD

0 commit comments

Comments
 (0)