1 // -*- C++ -*-
2 //===----------------------------------------------------------------------===//
3 //
4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 // See https://llvm.org/LICENSE.txt for license information.
6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //
8 //===----------------------------------------------------------------------===//
9
10 #ifndef _PSTL_NUMERIC_IMPL_H
11 #define _PSTL_NUMERIC_IMPL_H
12
13 #include <iterator>
14 #include <type_traits>
15 #include <numeric>
16
17 #include "parallel_backend.h"
18 #include "pstl_config.h"
19 #include "execution_impl.h"
20 #include "unseq_backend_simd.h"
21 #include "algorithm_fwd.h"
22
23 _PSTL_HIDE_FROM_ABI_PUSH
24
25 namespace __pstl
26 {
27 namespace __internal
28 {
29
30 //------------------------------------------------------------------------
31 // transform_reduce (version with two binary functions, according to draft N4659)
32 //------------------------------------------------------------------------
33
34 template <class _ForwardIterator1, class _ForwardIterator2, class _Tp, class _BinaryOperation1, class _BinaryOperation2>
35 _Tp
__brick_transform_reduce(_ForwardIterator1 __first1,_ForwardIterator1 __last1,_ForwardIterator2 __first2,_Tp __init,_BinaryOperation1 __binary_op1,_BinaryOperation2 __binary_op2,std::false_type)36 __brick_transform_reduce(_ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardIterator2 __first2, _Tp __init,
37 _BinaryOperation1 __binary_op1, _BinaryOperation2 __binary_op2,
38 /*is_vector=*/std::false_type) noexcept
39 {
40 return std::inner_product(__first1, __last1, __first2, __init, __binary_op1, __binary_op2);
41 }
42
43 template <class _RandomAccessIterator1, class _RandomAccessIterator2, class _Tp, class _BinaryOperation1,
44 class _BinaryOperation2>
45 _Tp
__brick_transform_reduce(_RandomAccessIterator1 __first1,_RandomAccessIterator1 __last1,_RandomAccessIterator2 __first2,_Tp __init,_BinaryOperation1 __binary_op1,_BinaryOperation2 __binary_op2,std::true_type)46 __brick_transform_reduce(_RandomAccessIterator1 __first1, _RandomAccessIterator1 __last1,
47 _RandomAccessIterator2 __first2, _Tp __init, _BinaryOperation1 __binary_op1,
48 _BinaryOperation2 __binary_op2,
49 /*is_vector=*/std::true_type) noexcept
50 {
51 typedef typename std::iterator_traits<_RandomAccessIterator1>::difference_type _DifferenceType;
52 return __unseq_backend::__simd_transform_reduce(
53 __last1 - __first1, __init, __binary_op1,
54 [=, &__binary_op2](_DifferenceType __i) { return __binary_op2(__first1[__i], __first2[__i]); });
55 }
56
57 template <class _Tag, class _ExecutionPolicy, class _ForwardIterator1, class _ForwardIterator2, class _Tp,
58 class _BinaryOperation1, class _BinaryOperation2>
59 _Tp
__pattern_transform_reduce(_Tag,_ExecutionPolicy &&,_ForwardIterator1 __first1,_ForwardIterator1 __last1,_ForwardIterator2 __first2,_Tp __init,_BinaryOperation1 __binary_op1,_BinaryOperation2 __binary_op2)60 __pattern_transform_reduce(_Tag, _ExecutionPolicy&&, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
61 _ForwardIterator2 __first2, _Tp __init, _BinaryOperation1 __binary_op1,
62 _BinaryOperation2 __binary_op2) noexcept
63 {
64 return __brick_transform_reduce(__first1, __last1, __first2, __init, __binary_op1, __binary_op2,
65 typename _Tag::__is_vector{});
66 }
67
68 template <class _IsVector, class _ExecutionPolicy, class _RandomAccessIterator1, class _RandomAccessIterator2,
69 class _Tp, class _BinaryOperation1, class _BinaryOperation2>
70 _Tp
__pattern_transform_reduce(__parallel_tag<_IsVector> __tag,_ExecutionPolicy && __exec,_RandomAccessIterator1 __first1,_RandomAccessIterator1 __last1,_RandomAccessIterator2 __first2,_Tp __init,_BinaryOperation1 __binary_op1,_BinaryOperation2 __binary_op2)71 __pattern_transform_reduce(__parallel_tag<_IsVector> __tag, _ExecutionPolicy&& __exec, _RandomAccessIterator1 __first1,
72 _RandomAccessIterator1 __last1, _RandomAccessIterator2 __first2, _Tp __init,
73 _BinaryOperation1 __binary_op1, _BinaryOperation2 __binary_op2)
74 {
75 using __backend_tag = typename decltype(__tag)::__backend_tag;
76
77 return __internal::__except_handler(
78 [&]()
79 {
80 return __par_backend::__parallel_transform_reduce(
81 __backend_tag{}, std::forward<_ExecutionPolicy>(__exec), __first1, __last1,
82 [__first1, __first2, __binary_op2](_RandomAccessIterator1 __i) mutable
83 { return __binary_op2(*__i, *(__first2 + (__i - __first1))); },
84 __init,
85 __binary_op1, // Combine
86 [__first1, __first2, __binary_op1, __binary_op2](_RandomAccessIterator1 __i, _RandomAccessIterator1 __j,
87 _Tp __init) -> _Tp
88 {
89 return __internal::__brick_transform_reduce(__i, __j, __first2 + (__i - __first1), __init,
90 __binary_op1, __binary_op2, _IsVector{});
91 });
92 });
93 }
94
95 //------------------------------------------------------------------------
96 // transform_reduce (version with unary and binary functions)
97 //------------------------------------------------------------------------
98
99 template <class _ForwardIterator, class _Tp, class _BinaryOperation, class _UnaryOperation>
100 _Tp
__brick_transform_reduce(_ForwardIterator __first,_ForwardIterator __last,_Tp __init,_BinaryOperation __binary_op,_UnaryOperation __unary_op,std::false_type)101 __brick_transform_reduce(_ForwardIterator __first, _ForwardIterator __last, _Tp __init, _BinaryOperation __binary_op,
102 _UnaryOperation __unary_op, /*is_vector=*/std::false_type) noexcept
103 {
104 return std::transform_reduce(__first, __last, __init, __binary_op, __unary_op);
105 }
106
107 template <class _RandomAccessIterator, class _Tp, class _UnaryOperation, class _BinaryOperation>
108 _Tp
__brick_transform_reduce(_RandomAccessIterator __first,_RandomAccessIterator __last,_Tp __init,_BinaryOperation __binary_op,_UnaryOperation __unary_op,std::true_type)109 __brick_transform_reduce(_RandomAccessIterator __first, _RandomAccessIterator __last, _Tp __init,
110 _BinaryOperation __binary_op, _UnaryOperation __unary_op,
111 /*is_vector=*/std::true_type) noexcept
112 {
113 typedef typename std::iterator_traits<_RandomAccessIterator>::difference_type _DifferenceType;
114 return __unseq_backend::__simd_transform_reduce(
115 __last - __first, __init, __binary_op,
116 [=, &__unary_op](_DifferenceType __i) { return __unary_op(__first[__i]); });
117 }
118
119 template <class _Tag, class _ExecutionPolicy, class _ForwardIterator, class _Tp, class _BinaryOperation,
120 class _UnaryOperation>
121 _Tp
__pattern_transform_reduce(_Tag,_ExecutionPolicy &&,_ForwardIterator __first,_ForwardIterator __last,_Tp __init,_BinaryOperation __binary_op,_UnaryOperation __unary_op)122 __pattern_transform_reduce(_Tag, _ExecutionPolicy&&, _ForwardIterator __first, _ForwardIterator __last, _Tp __init,
123 _BinaryOperation __binary_op, _UnaryOperation __unary_op) noexcept
124 {
125 return __internal::__brick_transform_reduce(__first, __last, __init, __binary_op, __unary_op,
126 typename _Tag::__is_vector{});
127 }
128
129 template <class _IsVector, class _ExecutionPolicy, class _RandomAccessIterator, class _Tp, class _BinaryOperation,
130 class _UnaryOperation>
131 _Tp
__pattern_transform_reduce(__parallel_tag<_IsVector> __tag,_ExecutionPolicy && __exec,_RandomAccessIterator __first,_RandomAccessIterator __last,_Tp __init,_BinaryOperation __binary_op,_UnaryOperation __unary_op)132 __pattern_transform_reduce(__parallel_tag<_IsVector> __tag, _ExecutionPolicy&& __exec, _RandomAccessIterator __first,
133 _RandomAccessIterator __last, _Tp __init, _BinaryOperation __binary_op,
134 _UnaryOperation __unary_op)
135 {
136 using __backend_tag = typename decltype(__tag)::__backend_tag;
137
138 return __internal::__except_handler(
139 [&]()
140 {
141 return __par_backend::__parallel_transform_reduce(
142 __backend_tag{}, std::forward<_ExecutionPolicy>(__exec), __first, __last,
143 [__unary_op](_RandomAccessIterator __i) mutable { return __unary_op(*__i); }, __init, __binary_op,
144 [__unary_op, __binary_op](_RandomAccessIterator __i, _RandomAccessIterator __j, _Tp __init) {
145 return __internal::__brick_transform_reduce(__i, __j, __init, __binary_op, __unary_op, _IsVector{});
146 });
147 });
148 }
149
150 //------------------------------------------------------------------------
151 // transform_exclusive_scan
152 //
153 // walk3 evaluates f(x,y,z) for (x,y,z) drawn from [first1,last1), [first2,...), [first3,...)
154 //------------------------------------------------------------------------
155
156 // Exclusive form
157 template <class _ForwardIterator, class _OutputIterator, class _UnaryOperation, class _Tp, class _BinaryOperation>
158 std::pair<_OutputIterator, _Tp>
__brick_transform_scan(_ForwardIterator __first,_ForwardIterator __last,_OutputIterator __result,_UnaryOperation __unary_op,_Tp __init,_BinaryOperation __binary_op,std::false_type,std::false_type)159 __brick_transform_scan(_ForwardIterator __first, _ForwardIterator __last, _OutputIterator __result,
160 _UnaryOperation __unary_op, _Tp __init, _BinaryOperation __binary_op,
161 /*Inclusive*/ std::false_type, /*is_vector=*/std::false_type) noexcept
162 {
163 for (; __first != __last; ++__first, ++__result)
164 {
165 *__result = __init;
166 _PSTL_PRAGMA_FORCEINLINE
167 __init = __binary_op(__init, __unary_op(*__first));
168 }
169 return std::make_pair(__result, __init);
170 }
171
172 // Inclusive form
173 template <class _RandomAccessIterator, class _OutputIterator, class _UnaryOperation, class _Tp, class _BinaryOperation>
174 std::pair<_OutputIterator, _Tp>
__brick_transform_scan(_RandomAccessIterator __first,_RandomAccessIterator __last,_OutputIterator __result,_UnaryOperation __unary_op,_Tp __init,_BinaryOperation __binary_op,std::true_type,std::false_type)175 __brick_transform_scan(_RandomAccessIterator __first, _RandomAccessIterator __last, _OutputIterator __result,
176 _UnaryOperation __unary_op, _Tp __init, _BinaryOperation __binary_op,
177 /*Inclusive*/ std::true_type, /*is_vector=*/std::false_type) noexcept
178 {
179 for (; __first != __last; ++__first, ++__result)
180 {
181 _PSTL_PRAGMA_FORCEINLINE
182 __init = __binary_op(__init, __unary_op(*__first));
183 *__result = __init;
184 }
185 return std::make_pair(__result, __init);
186 }
187
188 // type is arithmetic and binary operation is a user defined operation.
189 template <typename _Tp, typename _BinaryOperation>
190 using is_arithmetic_udop = std::integral_constant<bool, std::is_arithmetic<_Tp>::value &&
191 !std::is_same<_BinaryOperation, std::plus<_Tp>>::value>;
192
193 // [restriction] - T shall be DefaultConstructible.
194 // [violation] - default ctor of T shall set the identity value for binary_op.
195 template <class _RandomAccessIterator, class _OutputIterator, class _UnaryOperation, class _Tp, class _BinaryOperation,
196 class _Inclusive>
197 typename std::enable_if<!is_arithmetic_udop<_Tp, _BinaryOperation>::value, std::pair<_OutputIterator, _Tp>>::type
__brick_transform_scan(_RandomAccessIterator __first,_RandomAccessIterator __last,_OutputIterator __result,_UnaryOperation __unary_op,_Tp __init,_BinaryOperation __binary_op,_Inclusive,std::true_type)198 __brick_transform_scan(_RandomAccessIterator __first, _RandomAccessIterator __last, _OutputIterator __result,
199 _UnaryOperation __unary_op, _Tp __init, _BinaryOperation __binary_op, _Inclusive,
200 /*is_vector=*/std::true_type) noexcept
201 {
202 #if defined(_PSTL_UDS_PRESENT)
203 return __unseq_backend::__simd_scan(__first, __last - __first, __result, __unary_op, __init, __binary_op,
204 _Inclusive());
205 #else
206 // We need to call serial brick here to call function for inclusive and exclusive scan that depends on _Inclusive() value
207 return __internal::__brick_transform_scan(__first, __last, __result, __unary_op, __init, __binary_op, _Inclusive(),
208 /*is_vector=*/std::false_type());
209 #endif
210 }
211
212 template <class _RandomAccessIterator, class _OutputIterator, class _UnaryOperation, class _Tp, class _BinaryOperation,
213 class _Inclusive>
214 typename std::enable_if<is_arithmetic_udop<_Tp, _BinaryOperation>::value, std::pair<_OutputIterator, _Tp>>::type
__brick_transform_scan(_RandomAccessIterator __first,_RandomAccessIterator __last,_OutputIterator __result,_UnaryOperation __unary_op,_Tp __init,_BinaryOperation __binary_op,_Inclusive,std::true_type)215 __brick_transform_scan(_RandomAccessIterator __first, _RandomAccessIterator __last, _OutputIterator __result,
216 _UnaryOperation __unary_op, _Tp __init, _BinaryOperation __binary_op, _Inclusive,
217 /*is_vector=*/std::true_type) noexcept
218 {
219 return __internal::__brick_transform_scan(__first, __last, __result, __unary_op, __init, __binary_op, _Inclusive(),
220 /*is_vector=*/std::false_type());
221 }
222
223 template <class _Tag, class _ExecutionPolicy, class _ForwardIterator, class _OutputIterator, class _UnaryOperation,
224 class _Tp, class _BinaryOperation, class _Inclusive>
225 _OutputIterator
__pattern_transform_scan(_Tag,_ExecutionPolicy &&,_ForwardIterator __first,_ForwardIterator __last,_OutputIterator __result,_UnaryOperation __unary_op,_Tp __init,_BinaryOperation __binary_op,_Inclusive)226 __pattern_transform_scan(_Tag, _ExecutionPolicy&&, _ForwardIterator __first, _ForwardIterator __last,
227 _OutputIterator __result, _UnaryOperation __unary_op, _Tp __init, _BinaryOperation __binary_op,
228 _Inclusive) noexcept
229 {
230 return __internal::__brick_transform_scan(__first, __last, __result, __unary_op, __init, __binary_op, _Inclusive(),
231 typename _Tag::__is_vector{})
232 .first;
233 }
234
235 template <class _IsVector, class _ExecutionPolicy, class _RandomAccessIterator, class _OutputIterator,
236 class _UnaryOperation, class _Tp, class _BinaryOperation, class _Inclusive>
237 typename std::enable_if<!std::is_floating_point<_Tp>::value, _OutputIterator>::type
__pattern_transform_scan(__parallel_tag<_IsVector> __tag,_ExecutionPolicy && __exec,_RandomAccessIterator __first,_RandomAccessIterator __last,_OutputIterator __result,_UnaryOperation __unary_op,_Tp __init,_BinaryOperation __binary_op,_Inclusive)238 __pattern_transform_scan(__parallel_tag<_IsVector> __tag, _ExecutionPolicy&& __exec, _RandomAccessIterator __first,
239 _RandomAccessIterator __last, _OutputIterator __result, _UnaryOperation __unary_op, _Tp __init,
240 _BinaryOperation __binary_op, _Inclusive)
241 {
242 using __backend_tag = typename decltype(__tag)::__backend_tag;
243
244 typedef typename std::iterator_traits<_RandomAccessIterator>::difference_type _DifferenceType;
245
246 return __internal::__except_handler(
247 [&]()
248 {
249 __par_backend::__parallel_transform_scan(
250 __backend_tag{}, std::forward<_ExecutionPolicy>(__exec), __last - __first,
251 [__first, __unary_op](_DifferenceType __i) mutable { return __unary_op(__first[__i]); }, __init,
252 __binary_op,
253 [__first, __unary_op, __binary_op](_DifferenceType __i, _DifferenceType __j, _Tp __init)
254 {
255 // Execute serial __brick_transform_reduce, due to the explicit SIMD vectorization (reduction) requires a commutative operation for the guarantee of correct scan.
256 return __internal::__brick_transform_reduce(__first + __i, __first + __j, __init, __binary_op,
257 __unary_op,
258 /*__is_vector*/ std::false_type());
259 },
260 [__first, __unary_op, __binary_op, __result](_DifferenceType __i, _DifferenceType __j, _Tp __init)
261 {
262 return __internal::__brick_transform_scan(__first + __i, __first + __j, __result + __i, __unary_op,
263 __init, __binary_op, _Inclusive(), _IsVector{})
264 .second;
265 });
266 return __result + (__last - __first);
267 });
268 }
269
270 template <class _IsVector, class _ExecutionPolicy, class _RandomAccessIterator, class _OutputIterator,
271 class _UnaryOperation, class _Tp, class _BinaryOperation, class _Inclusive>
272 typename std::enable_if<std::is_floating_point<_Tp>::value, _OutputIterator>::type
__pattern_transform_scan(__parallel_tag<_IsVector> __tag,_ExecutionPolicy && __exec,_RandomAccessIterator __first,_RandomAccessIterator __last,_OutputIterator __result,_UnaryOperation __unary_op,_Tp __init,_BinaryOperation __binary_op,_Inclusive)273 __pattern_transform_scan(__parallel_tag<_IsVector> __tag, _ExecutionPolicy&& __exec, _RandomAccessIterator __first,
274 _RandomAccessIterator __last, _OutputIterator __result, _UnaryOperation __unary_op, _Tp __init,
275 _BinaryOperation __binary_op, _Inclusive)
276 {
277 using __backend_tag = typename decltype(__tag)::__backend_tag;
278
279 typedef typename std::iterator_traits<_RandomAccessIterator>::difference_type _DifferenceType;
280 _DifferenceType __n = __last - __first;
281
282 if (__n <= 0)
283 {
284 return __result;
285 }
286 return __internal::__except_handler(
287 [&]()
288 {
289 __par_backend::__parallel_strict_scan(
290 __backend_tag{}, std::forward<_ExecutionPolicy>(__exec), __n, __init,
291 [__first, __unary_op, __binary_op, __result](_DifferenceType __i, _DifferenceType __len)
292 {
293 return __internal::__brick_transform_scan(__first + __i, __first + (__i + __len), __result + __i,
294 __unary_op, _Tp{}, __binary_op, _Inclusive(), _IsVector{})
295 .second;
296 },
297 __binary_op,
298 [__result, &__binary_op](_DifferenceType __i, _DifferenceType __len, _Tp __initial)
299 {
300 return *(std::transform(__result + __i, __result + __i + __len, __result + __i,
301 [&__initial, &__binary_op](const _Tp& __x)
302 {
303 _PSTL_PRAGMA_FORCEINLINE
304 return __binary_op(__initial, __x);
305 }) -
306 1);
307 },
308 [](_Tp) {});
309 return __result + (__last - __first);
310 });
311 }
312
313 //------------------------------------------------------------------------
314 // adjacent_difference
315 //------------------------------------------------------------------------
316
317 template <class _ForwardIterator, class _OutputIterator, class _BinaryOperation>
318 _OutputIterator
__brick_adjacent_difference(_ForwardIterator __first,_ForwardIterator __last,_OutputIterator __d_first,_BinaryOperation __op,std::false_type)319 __brick_adjacent_difference(_ForwardIterator __first, _ForwardIterator __last, _OutputIterator __d_first,
320 _BinaryOperation __op, /*is_vector*/ std::false_type) noexcept
321 {
322 return std::adjacent_difference(__first, __last, __d_first, __op);
323 }
324
325 template <class _RandomAccessIterator1, class _RandomAccessIterator2, class BinaryOperation>
326 _RandomAccessIterator2
__brick_adjacent_difference(_RandomAccessIterator1 __first,_RandomAccessIterator1 __last,_RandomAccessIterator2 __d_first,BinaryOperation __op,std::true_type)327 __brick_adjacent_difference(_RandomAccessIterator1 __first, _RandomAccessIterator1 __last,
328 _RandomAccessIterator2 __d_first, BinaryOperation __op,
329 /*is_vector=*/std::true_type) noexcept
330 {
331 _PSTL_ASSERT(__first != __last);
332
333 typedef typename std::iterator_traits<_RandomAccessIterator1>::reference _ReferenceType1;
334 typedef typename std::iterator_traits<_RandomAccessIterator2>::reference _ReferenceType2;
335
336 auto __n = __last - __first;
337 *__d_first = *__first;
338 return __unseq_backend::__simd_walk_3(
339 __first + 1, __n - 1, __first, __d_first + 1,
340 [&__op](_ReferenceType1 __x, _ReferenceType1 __y, _ReferenceType2 __z) { __z = __op(__x, __y); });
341 }
342
343 template <class _Tag, class _ExecutionPolicy, class _ForwardIterator, class _OutputIterator, class _BinaryOperation>
344 _OutputIterator
__pattern_adjacent_difference(_Tag,_ExecutionPolicy &&,_ForwardIterator __first,_ForwardIterator __last,_OutputIterator __d_first,_BinaryOperation __op)345 __pattern_adjacent_difference(_Tag, _ExecutionPolicy&&, _ForwardIterator __first, _ForwardIterator __last,
346 _OutputIterator __d_first, _BinaryOperation __op) noexcept
347 {
348 return __internal::__brick_adjacent_difference(__first, __last, __d_first, __op, typename _Tag::__is_vector{});
349 }
350
351 template <class _IsVector, class _ExecutionPolicy, class _RandomAccessIterator1, class _RandomAccessIterator2,
352 class _BinaryOperation>
353 _RandomAccessIterator2
__pattern_adjacent_difference(__parallel_tag<_IsVector> __tag,_ExecutionPolicy && __exec,_RandomAccessIterator1 __first,_RandomAccessIterator1 __last,_RandomAccessIterator2 __d_first,_BinaryOperation __op)354 __pattern_adjacent_difference(__parallel_tag<_IsVector> __tag, _ExecutionPolicy&& __exec,
355 _RandomAccessIterator1 __first, _RandomAccessIterator1 __last,
356 _RandomAccessIterator2 __d_first, _BinaryOperation __op)
357 {
358 _PSTL_ASSERT(__first != __last);
359 typedef typename std::iterator_traits<_RandomAccessIterator1>::reference _ReferenceType1;
360 typedef typename std::iterator_traits<_RandomAccessIterator2>::reference _ReferenceType2;
361
362 using __backend_tag = typename decltype(__tag)::__backend_tag;
363
364 *__d_first = *__first;
365 __par_backend::__parallel_for(__backend_tag{}, std::forward<_ExecutionPolicy>(__exec), __first, __last - 1,
366 [&__op, __d_first, __first](_RandomAccessIterator1 __b, _RandomAccessIterator1 __e)
367 {
368 _RandomAccessIterator2 __d_b = __d_first + (__b - __first);
369 __internal::__brick_walk3(
370 __b, __e, __b + 1, __d_b + 1,
371 [&__op](_ReferenceType1 __x, _ReferenceType1 __y, _ReferenceType2 __z)
372 { __z = __op(__y, __x); },
373 _IsVector{});
374 });
375 return __d_first + (__last - __first);
376 }
377
378 } // namespace __internal
379 } // namespace __pstl
380
381 _PSTL_HIDE_FROM_ABI_POP
382
383 #endif /* _PSTL_NUMERIC_IMPL_H */
384