Skip to content

Commit 6351b51

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 6351b51

25 files changed

+1812
-2129
lines changed

libcxx/include/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,8 @@ set(files
570570
__numeric/transform_reduce.h
571571
__ostream/basic_ostream.h
572572
__ostream/print.h
573+
__pstl/backend_fwd.h
574+
__pstl/backends/default.h
573575
__pstl/backends/libdispatch.h
574576
__pstl/backends/serial.h
575577
__pstl/backends/std_thread.h
@@ -584,6 +586,8 @@ set(files
584586
__pstl/cpu_algos/stable_sort.h
585587
__pstl/cpu_algos/transform.h
586588
__pstl/cpu_algos/transform_reduce.h
589+
__pstl/dispatch.h
590+
__pstl/run_backend.h
587591
__random/bernoulli_distribution.h
588592
__random/binomial_distribution.h
589593
__random/cauchy_distribution.h

libcxx/include/__algorithm/pstl.h

Lines changed: 210 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 & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,19 @@
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>
17-
#include <__iterator/iterator_traits.h>
18-
#include <__numeric/transform_reduce.h>
16+
#include <__pstl/backend_fwd.h>
1917
#include <__pstl/configuration.h>
18+
#include <__pstl/dispatch.h>
19+
#include <__pstl/run_backend.h>
20+
#include <__type_traits/enable_if.h>
2021
#include <__type_traits/is_execution_policy.h>
22+
#include <__type_traits/remove_cvref.h>
23+
#include <__utility/forward.h>
2124
#include <__utility/move.h>
22-
#include <optional>
2325

2426
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
2527
# pragma GCC system_header
@@ -33,30 +35,50 @@ _LIBCPP_PUSH_MACROS
3335
_LIBCPP_BEGIN_NAMESPACE_STD
3436

3537
template <class _ExecutionPolicy,
36-
class _ForwardIterator1,
37-
class _ForwardIterator2,
38+
class _ForwardIterator,
3839
class _Tp,
39-
class _BinaryOperation1,
40-
class _BinaryOperation2,
40+
class _BinaryOperation,
4141
class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
4242
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),
43+
_LIBCPP_HIDE_FROM_ABI _Tp reduce(
44+
_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Tp __init, _BinaryOperation __op) {
45+
_LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "reduce requires ForwardIterators");
46+
using _Implementation = __pstl::__dispatch<__pstl::__reduce, __pstl::__current_configuration, _RawPolicy>;
47+
return __pstl::__run_backend<_Implementation>(
48+
std::forward<_ExecutionPolicy>(__policy),
49+
std::move(__first),
50+
std::move(__last),
5751
std::move(__init),
58-
std::move(__reduce),
59-
std::move(__transform));
52+
std::move(__op));
53+
}
54+
55+
template <class _ExecutionPolicy,
56+
class _ForwardIterator,
57+
class _Tp,
58+
class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
59+
enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
60+
_LIBCPP_HIDE_FROM_ABI _Tp
61+
reduce(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Tp __init) {
62+
_LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "reduce requires ForwardIterators");
63+
using _Implementation = __pstl::__dispatch<__pstl::__reduce, __pstl::__current_configuration, _RawPolicy>;
64+
return __pstl::__run_backend<_Implementation>(
65+
std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), std::move(__init), plus{});
66+
}
67+
68+
template <class _ExecutionPolicy,
69+
class _ForwardIterator,
70+
class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
71+
enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
72+
_LIBCPP_HIDE_FROM_ABI __iter_value_type<_ForwardIterator>
73+
reduce(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last) {
74+
_LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "reduce requires ForwardIterators");
75+
using _Implementation = __pstl::__dispatch<__pstl::__reduce, __pstl::__current_configuration, _RawPolicy>;
76+
return __pstl::__run_backend<_Implementation>(
77+
std::forward<_ExecutionPolicy>(__policy),
78+
std::move(__first),
79+
std::move(__last),
80+
__iter_value_type<_ForwardIterator>(),
81+
plus{});
6082
}
6183

6284
template <class _ExecutionPolicy,
@@ -77,18 +99,16 @@ _LIBCPP_HIDE_FROM_ABI _Tp transform_reduce(
7799
_BinaryOperation2 __transform) {
78100
_LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "transform_reduce requires ForwardIterators");
79101
_LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "transform_reduce requires ForwardIterators");
80-
auto __res = std::__transform_reduce(
81-
__policy,
102+
using _Implementation =
103+
__pstl::__dispatch<__pstl::__transform_reduce_binary, __pstl::__current_configuration, _RawPolicy>;
104+
return __pstl::__run_backend<_Implementation>(
105+
std::forward<_ExecutionPolicy>(__policy),
82106
std::move(__first1),
83107
std::move(__last1),
84108
std::move(__first2),
85109
std::move(__init),
86110
std::move(__reduce),
87111
std::move(__transform));
88-
89-
if (!__res)
90-
std::__throw_bad_alloc();
91-
return *std::move(__res);
92112
}
93113

94114
// This overload doesn't get a customization point because it's trivial to detect (through e.g.
@@ -97,7 +117,8 @@ template <class _ExecutionPolicy,
97117
class _ForwardIterator1,
98118
class _ForwardIterator2,
99119
class _Tp,
100-
enable_if_t<is_execution_policy_v<__remove_cvref_t<_ExecutionPolicy>>, int> = 0>
120+
class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
121+
enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
101122
_LIBCPP_HIDE_FROM_ABI _Tp transform_reduce(
102123
_ExecutionPolicy&& __policy,
103124
_ForwardIterator1 __first1,
@@ -106,31 +127,16 @@ _LIBCPP_HIDE_FROM_ABI _Tp transform_reduce(
106127
_Tp __init) {
107128
_LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "transform_reduce requires ForwardIterators");
108129
_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),
130+
using _Implementation =
131+
__pstl::__dispatch<__pstl::__transform_reduce_binary, __pstl::__current_configuration, _RawPolicy>;
132+
return __pstl::__run_backend<_Implementation>(
133+
std::forward<_ExecutionPolicy>(__policy),
134+
std::move(__first1),
135+
std::move(__last1),
136+
std::move(__first2),
131137
std::move(__init),
132-
std::move(__reduce),
133-
std::move(__transform));
138+
plus{},
139+
multiplies{});
134140
}
135141

136142
template <class _ExecutionPolicy,
@@ -148,86 +154,14 @@ _LIBCPP_HIDE_FROM_ABI _Tp transform_reduce(
148154
_BinaryOperation __reduce,
149155
_UnaryOperation __transform) {
150156
_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-
},
157+
using _Implementation = __pstl::__dispatch<__pstl::__transform_reduce, __pstl::__current_configuration, _RawPolicy>;
158+
return __pstl::__run_backend<_Implementation>(
159+
std::forward<_ExecutionPolicy>(__policy),
179160
std::move(__first),
180161
std::move(__last),
181162
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);
163+
std::move(__reduce),
164+
std::move(__transform));
231165
}
232166

233167
_LIBCPP_END_NAMESPACE_STD

0 commit comments

Comments
 (0)