Skip to content

Commit 9540950

Browse files
authored
[libc++] Overhaul the PSTL dispatching mechanism (llvm#88131)
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: - There is a single level of backend dispatching, however partial backends can be stacked to provide a full implementation of the PSTL. The two-level dispatching that was used for CPU-based backends is handled by providing CPU-based basis operations as simple helpers that can easily be reused when defining any PSTL backend. - The default definitions for algorithms are separated from their dispatching logic. - 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 llvm#70718
1 parent 1139902 commit 9540950

29 files changed

+1891
-2174
lines changed

libcxx/benchmarks/algorithms/pstl.stable_sort.bench.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//===----------------------------------------------------------------------===//
88

99
#include <algorithm>
10+
#include <execution>
1011

1112
#include "common.h"
1213

libcxx/include/CMakeLists.txt

Lines changed: 5 additions & 3 deletions
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
@@ -576,11 +575,12 @@ set(files
576575
__numeric/transform_reduce.h
577576
__ostream/basic_ostream.h
578577
__ostream/print.h
578+
__pstl/backend.h
579+
__pstl/backend_fwd.h
580+
__pstl/backends/default.h
579581
__pstl/backends/libdispatch.h
580582
__pstl/backends/serial.h
581583
__pstl/backends/std_thread.h
582-
__pstl/configuration.h
583-
__pstl/configuration_fwd.h
584584
__pstl/cpu_algos/any_of.h
585585
__pstl/cpu_algos/cpu_traits.h
586586
__pstl/cpu_algos/fill.h
@@ -590,6 +590,8 @@ set(files
590590
__pstl/cpu_algos/stable_sort.h
591591
__pstl/cpu_algos/transform.h
592592
__pstl/cpu_algos/transform_reduce.h
593+
__pstl/dispatch.h
594+
__pstl/handle_exception.h
593595
__random/bernoulli_distribution.h
594596
__random/binomial_distribution.h
595597
__random/cauchy_distribution.h

libcxx/include/__algorithm/pstl.h

Lines changed: 219 additions & 923 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>
1716
#include <__iterator/iterator_traits.h>
18-
#include <__numeric/transform_reduce.h>
19-
#include <__pstl/configuration.h>
17+
#include <__pstl/backend.h>
18+
#include <__pstl/dispatch.h>
19+
#include <__pstl/handle_exception.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::__handle_exception<_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::__handle_exception<_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::__handle_exception<_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::__handle_exception<_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::__handle_exception<_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::__handle_exception<_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

libcxx/include/__pstl/configuration.h renamed to libcxx/include/__pstl/backend.h

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,30 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9-
#ifndef _LIBCPP___PSTL_CONFIGURATION_H
10-
#define _LIBCPP___PSTL_CONFIGURATION_H
9+
#ifndef _LIBCPP___PSTL_BACKEND_H
10+
#define _LIBCPP___PSTL_BACKEND_H
1111

1212
#include <__config>
13-
#include <__pstl/configuration_fwd.h>
13+
#include <__pstl/backend_fwd.h>
1414

1515
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
1616
# pragma GCC system_header
1717
#endif
1818

19+
_LIBCPP_PUSH_MACROS
20+
#include <__undef_macros>
21+
1922
#if defined(_LIBCPP_PSTL_BACKEND_SERIAL)
23+
# include <__pstl/backends/default.h>
2024
# include <__pstl/backends/serial.h>
2125
#elif defined(_LIBCPP_PSTL_BACKEND_STD_THREAD)
26+
# include <__pstl/backends/default.h>
2227
# include <__pstl/backends/std_thread.h>
2328
#elif defined(_LIBCPP_PSTL_BACKEND_LIBDISPATCH)
29+
# include <__pstl/backends/default.h>
2430
# include <__pstl/backends/libdispatch.h>
2531
#endif
2632

27-
#endif // _LIBCPP___PSTL_CONFIGURATION_H
33+
_LIBCPP_POP_MACROS
34+
35+
#endif // _LIBCPP___PSTL_BACKEND_H

0 commit comments

Comments
 (0)