1 | /* |
2 | Copyright (c) 2005-2021 Intel Corporation |
3 | |
4 | Licensed under the Apache License, Version 2.0 (the "License"); |
5 | you may not use this file except in compliance with the License. |
6 | You may obtain a copy of the License at |
7 | |
8 | http://www.apache.org/licenses/LICENSE-2.0 |
9 | |
10 | Unless required by applicable law or agreed to in writing, software |
11 | distributed under the License is distributed on an "AS IS" BASIS, |
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | See the License for the specific language governing permissions and |
14 | limitations under the License. |
15 | */ |
16 | |
17 | #ifndef __TBB_parallel_for_H |
18 | #define __TBB_parallel_for_H |
19 | |
20 | #include "detail/_config.h" |
21 | #include "detail/_namespace_injection.h" |
22 | #include "detail/_exception.h" |
23 | #include "detail/_task.h" |
24 | #include "detail/_small_object_pool.h" |
25 | #include "profiling.h" |
26 | |
27 | #include "partitioner.h" |
28 | #include "blocked_range.h" |
29 | #include "task_group.h" |
30 | |
31 | #include <cstddef> |
32 | #include <new> |
33 | |
34 | namespace tbb { |
35 | namespace detail { |
36 | #if __TBB_CPP20_CONCEPTS_PRESENT |
37 | inline namespace d0 { |
38 | |
39 | template <typename Body, typename Range> |
40 | concept parallel_for_body = std::copy_constructible<Body> && |
41 | requires( const std::remove_reference_t<Body>& body, Range& range ) { |
42 | body(range); |
43 | }; |
44 | |
45 | template <typename Index> |
46 | concept parallel_for_index = std::constructible_from<Index, int> && |
47 | std::copyable<Index> && |
48 | requires( const std::remove_reference_t<Index>& lhs, const std::remove_reference_t<Index>& rhs ) { |
49 | { lhs < rhs } -> adaptive_same_as<bool>; |
50 | { lhs - rhs } -> std::convertible_to<std::size_t>; |
51 | { lhs + (rhs - lhs) } -> std::convertible_to<Index>; |
52 | }; |
53 | |
54 | template <typename Function, typename Index> |
55 | concept parallel_for_function = requires( const std::remove_reference_t<Function>& func, Index index ) { |
56 | func(index); |
57 | }; |
58 | |
59 | } // namespace d0 |
60 | #endif // __TBB_CPP20_CONCEPTS_PRESENT |
61 | namespace d1 { |
62 | |
63 | //! Task type used in parallel_for |
64 | /** @ingroup algorithms */ |
65 | template<typename Range, typename Body, typename Partitioner> |
66 | struct start_for : public task { |
67 | Range my_range; |
68 | const Body my_body; |
69 | node* my_parent; |
70 | |
71 | typename Partitioner::task_partition_type my_partition; |
72 | small_object_allocator my_allocator; |
73 | |
74 | task* execute(execution_data&) override; |
75 | task* cancel(execution_data&) override; |
76 | void finalize(const execution_data&); |
77 | |
78 | //! Constructor for root task. |
79 | start_for( const Range& range, const Body& body, Partitioner& partitioner, small_object_allocator& alloc ) : |
80 | my_range(range), |
81 | my_body(body), |
82 | my_partition(partitioner), |
83 | my_allocator(alloc) {} |
84 | //! Splitting constructor used to generate children. |
85 | /** parent_ becomes left child. Newly constructed object is right child. */ |
86 | start_for( start_for& parent_, typename Partitioner::split_type& split_obj, small_object_allocator& alloc ) : |
87 | my_range(parent_.my_range, get_range_split_object<Range>(split_obj)), |
88 | my_body(parent_.my_body), |
89 | my_partition(parent_.my_partition, split_obj), |
90 | my_allocator(alloc) {} |
91 | //! Construct right child from the given range as response to the demand. |
92 | /** parent_ remains left child. Newly constructed object is right child. */ |
93 | start_for( start_for& parent_, const Range& r, depth_t d, small_object_allocator& alloc ) : |
94 | my_range(r), |
95 | my_body(parent_.my_body), |
96 | my_partition(parent_.my_partition, split()), |
97 | my_allocator(alloc) |
98 | { |
99 | my_partition.align_depth( d ); |
100 | } |
101 | static void run(const Range& range, const Body& body, Partitioner& partitioner) { |
102 | task_group_context context(PARALLEL_FOR); |
103 | run(range, body, partitioner, context); |
104 | } |
105 | |
106 | static void run(const Range& range, const Body& body, Partitioner& partitioner, task_group_context& context) { |
107 | if ( !range.empty() ) { |
108 | small_object_allocator alloc{}; |
109 | start_for& for_task = *alloc.new_object<start_for>(range, body, partitioner, alloc); |
110 | |
111 | // defer creation of the wait node until task allocation succeeds |
112 | wait_node wn; |
113 | for_task.my_parent = &wn; |
114 | execute_and_wait(for_task, context, wn.m_wait, context); |
115 | } |
116 | } |
117 | //! Run body for range, serves as callback for partitioner |
118 | void run_body( Range &r ) { |
119 | my_body( r ); |
120 | } |
121 | |
122 | //! spawn right task, serves as callback for partitioner |
123 | void offer_work(typename Partitioner::split_type& split_obj, execution_data& ed) { |
124 | offer_work_impl(ed, *this, split_obj); |
125 | } |
126 | |
127 | //! spawn right task, serves as callback for partitioner |
128 | void offer_work(const Range& r, depth_t d, execution_data& ed) { |
129 | offer_work_impl(ed, *this, r, d); |
130 | } |
131 | |
132 | private: |
133 | template <typename... Args> |
134 | void offer_work_impl(execution_data& ed, Args&&... constructor_args) { |
135 | // New right child |
136 | small_object_allocator alloc{}; |
137 | start_for& right_child = *alloc.new_object<start_for>(ed, std::forward<Args>(constructor_args)..., alloc); |
138 | |
139 | // New root node as a continuation and ref count. Left and right child attach to the new parent. |
140 | right_child.my_parent = my_parent = alloc.new_object<tree_node>(ed, args&: my_parent, args: 2, args&: alloc); |
141 | // Spawn the right sibling |
142 | right_child.spawn_self(ed); |
143 | } |
144 | |
145 | void spawn_self(execution_data& ed) { |
146 | my_partition.spawn_task(*this, *context(ed)); |
147 | } |
148 | }; |
149 | |
150 | //! fold the tree and deallocate the task |
151 | template<typename Range, typename Body, typename Partitioner> |
152 | void start_for<Range, Body, Partitioner>::finalize(const execution_data& ed) { |
153 | // Get the current parent and allocator an object destruction |
154 | node* parent = my_parent; |
155 | auto allocator = my_allocator; |
156 | // Task execution finished - destroy it |
157 | this->~start_for(); |
158 | // Unwind the tree decrementing the parent`s reference count |
159 | |
160 | fold_tree<tree_node>(n: parent, ed); |
161 | allocator.deallocate(this, ed); |
162 | |
163 | } |
164 | |
165 | //! execute task for parallel_for |
166 | template<typename Range, typename Body, typename Partitioner> |
167 | task* start_for<Range, Body, Partitioner>::execute(execution_data& ed) { |
168 | if (!is_same_affinity(ed)) { |
169 | my_partition.note_affinity(execution_slot(ed)); |
170 | } |
171 | my_partition.check_being_stolen(*this, ed); |
172 | my_partition.execute(*this, my_range, ed); |
173 | finalize(ed); |
174 | return nullptr; |
175 | } |
176 | |
177 | //! cancel task for parallel_for |
178 | template<typename Range, typename Body, typename Partitioner> |
179 | task* start_for<Range, Body, Partitioner>::cancel(execution_data& ed) { |
180 | finalize(ed); |
181 | return nullptr; |
182 | } |
183 | |
184 | //! Calls the function with values from range [begin, end) with a step provided |
185 | template<typename Function, typename Index> |
186 | class parallel_for_body_wrapper : detail::no_assign { |
187 | const Function &my_func; |
188 | const Index my_begin; |
189 | const Index my_step; |
190 | public: |
191 | parallel_for_body_wrapper( const Function& _func, Index& _begin, Index& _step ) |
192 | : my_func(_func), my_begin(_begin), my_step(_step) {} |
193 | |
194 | void operator()( const blocked_range<Index>& r ) const { |
195 | // A set of local variables to help the compiler with vectorization of the following loop. |
196 | Index b = r.begin(); |
197 | Index e = r.end(); |
198 | Index ms = my_step; |
199 | Index k = my_begin + b*ms; |
200 | |
201 | #if __INTEL_COMPILER |
202 | #pragma ivdep |
203 | #if __TBB_ASSERT_ON_VECTORIZATION_FAILURE |
204 | #pragma vector always assert |
205 | #endif |
206 | #endif |
207 | for ( Index i = b; i < e; ++i, k += ms ) { |
208 | my_func( k ); |
209 | } |
210 | } |
211 | }; |
212 | |
213 | // Requirements on Range concept are documented in blocked_range.h |
214 | |
215 | /** \page parallel_for_body_req Requirements on parallel_for body |
216 | Class \c Body implementing the concept of parallel_for body must define: |
217 | - \code Body::Body( const Body& ); \endcode Copy constructor |
218 | - \code Body::~Body(); \endcode Destructor |
219 | - \code void Body::operator()( Range& r ) const; \endcode Function call operator applying the body to range \c r. |
220 | **/ |
221 | |
222 | /** \name parallel_for |
223 | See also requirements on \ref range_req "Range" and \ref parallel_for_body_req "parallel_for Body". **/ |
224 | //@{ |
225 | |
226 | //! Parallel iteration over range with default partitioner. |
227 | /** @ingroup algorithms **/ |
228 | template<typename Range, typename Body> |
229 | __TBB_requires(tbb_range<Range> && parallel_for_body<Body, Range>) |
230 | void parallel_for( const Range& range, const Body& body ) { |
231 | start_for<Range,Body,const __TBB_DEFAULT_PARTITIONER>::run(range,body,__TBB_DEFAULT_PARTITIONER()); |
232 | } |
233 | |
234 | //! Parallel iteration over range with simple partitioner. |
235 | /** @ingroup algorithms **/ |
236 | template<typename Range, typename Body> |
237 | __TBB_requires(tbb_range<Range> && parallel_for_body<Body, Range>) |
238 | void parallel_for( const Range& range, const Body& body, const simple_partitioner& partitioner ) { |
239 | start_for<Range,Body,const simple_partitioner>::run(range,body,partitioner); |
240 | } |
241 | |
242 | //! Parallel iteration over range with auto_partitioner. |
243 | /** @ingroup algorithms **/ |
244 | template<typename Range, typename Body> |
245 | __TBB_requires(tbb_range<Range> && parallel_for_body<Body, Range>) |
246 | void parallel_for( const Range& range, const Body& body, const auto_partitioner& partitioner ) { |
247 | start_for<Range,Body,const auto_partitioner>::run(range,body,partitioner); |
248 | } |
249 | |
250 | //! Parallel iteration over range with static_partitioner. |
251 | /** @ingroup algorithms **/ |
252 | template<typename Range, typename Body> |
253 | __TBB_requires(tbb_range<Range> && parallel_for_body<Body, Range>) |
254 | void parallel_for( const Range& range, const Body& body, const static_partitioner& partitioner ) { |
255 | start_for<Range,Body,const static_partitioner>::run(range,body,partitioner); |
256 | } |
257 | |
258 | //! Parallel iteration over range with affinity_partitioner. |
259 | /** @ingroup algorithms **/ |
260 | template<typename Range, typename Body> |
261 | __TBB_requires(tbb_range<Range> && parallel_for_body<Body, Range>) |
262 | void parallel_for( const Range& range, const Body& body, affinity_partitioner& partitioner ) { |
263 | start_for<Range,Body,affinity_partitioner>::run(range,body,partitioner); |
264 | } |
265 | |
266 | //! Parallel iteration over range with default partitioner and user-supplied context. |
267 | /** @ingroup algorithms **/ |
268 | template<typename Range, typename Body> |
269 | __TBB_requires(tbb_range<Range> && parallel_for_body<Body, Range>) |
270 | void parallel_for( const Range& range, const Body& body, task_group_context& context ) { |
271 | start_for<Range,Body,const __TBB_DEFAULT_PARTITIONER>::run(range, body, __TBB_DEFAULT_PARTITIONER(), context); |
272 | } |
273 | |
274 | //! Parallel iteration over range with simple partitioner and user-supplied context. |
275 | /** @ingroup algorithms **/ |
276 | template<typename Range, typename Body> |
277 | __TBB_requires(tbb_range<Range> && parallel_for_body<Body, Range>) |
278 | void parallel_for( const Range& range, const Body& body, const simple_partitioner& partitioner, task_group_context& context ) { |
279 | start_for<Range,Body,const simple_partitioner>::run(range, body, partitioner, context); |
280 | } |
281 | |
282 | //! Parallel iteration over range with auto_partitioner and user-supplied context. |
283 | /** @ingroup algorithms **/ |
284 | template<typename Range, typename Body> |
285 | __TBB_requires(tbb_range<Range> && parallel_for_body<Body, Range>) |
286 | void parallel_for( const Range& range, const Body& body, const auto_partitioner& partitioner, task_group_context& context ) { |
287 | start_for<Range,Body,const auto_partitioner>::run(range, body, partitioner, context); |
288 | } |
289 | |
290 | //! Parallel iteration over range with static_partitioner and user-supplied context. |
291 | /** @ingroup algorithms **/ |
292 | template<typename Range, typename Body> |
293 | __TBB_requires(tbb_range<Range> && parallel_for_body<Body, Range>) |
294 | void parallel_for( const Range& range, const Body& body, const static_partitioner& partitioner, task_group_context& context ) { |
295 | start_for<Range,Body,const static_partitioner>::run(range, body, partitioner, context); |
296 | } |
297 | |
298 | //! Parallel iteration over range with affinity_partitioner and user-supplied context. |
299 | /** @ingroup algorithms **/ |
300 | template<typename Range, typename Body> |
301 | __TBB_requires(tbb_range<Range> && parallel_for_body<Body, Range>) |
302 | void parallel_for( const Range& range, const Body& body, affinity_partitioner& partitioner, task_group_context& context ) { |
303 | start_for<Range,Body,affinity_partitioner>::run(range,body,partitioner, context); |
304 | } |
305 | |
306 | //! Implementation of parallel iteration over stepped range of integers with explicit step and partitioner |
307 | template <typename Index, typename Function, typename Partitioner> |
308 | void parallel_for_impl(Index first, Index last, Index step, const Function& f, Partitioner& partitioner) { |
309 | if (step <= 0 ) |
310 | throw_exception(exception_id::nonpositive_step); // throws std::invalid_argument |
311 | else if (first < last) { |
312 | // Above "else" avoids "potential divide by zero" warning on some platforms |
313 | Index end = (last - first - Index(1)) / step + Index(1); |
314 | blocked_range<Index> range(static_cast<Index>(0), end); |
315 | parallel_for_body_wrapper<Function, Index> body(f, first, step); |
316 | parallel_for(range, body, partitioner); |
317 | } |
318 | } |
319 | |
320 | //! Parallel iteration over a range of integers with a step provided and default partitioner |
321 | template <typename Index, typename Function> |
322 | __TBB_requires(parallel_for_index<Index> && parallel_for_function<Function, Index>) |
323 | void parallel_for(Index first, Index last, Index step, const Function& f) { |
324 | parallel_for_impl<Index,Function,const auto_partitioner>(first, last, step, f, auto_partitioner()); |
325 | } |
326 | //! Parallel iteration over a range of integers with a step provided and simple partitioner |
327 | template <typename Index, typename Function> |
328 | __TBB_requires(parallel_for_index<Index> && parallel_for_function<Function, Index>) |
329 | void parallel_for(Index first, Index last, Index step, const Function& f, const simple_partitioner& partitioner) { |
330 | parallel_for_impl<Index,Function,const simple_partitioner>(first, last, step, f, partitioner); |
331 | } |
332 | //! Parallel iteration over a range of integers with a step provided and auto partitioner |
333 | template <typename Index, typename Function> |
334 | __TBB_requires(parallel_for_index<Index> && parallel_for_function<Function, Index>) |
335 | void parallel_for(Index first, Index last, Index step, const Function& f, const auto_partitioner& partitioner) { |
336 | parallel_for_impl<Index,Function,const auto_partitioner>(first, last, step, f, partitioner); |
337 | } |
338 | //! Parallel iteration over a range of integers with a step provided and static partitioner |
339 | template <typename Index, typename Function> |
340 | __TBB_requires(parallel_for_index<Index> && parallel_for_function<Function, Index>) |
341 | void parallel_for(Index first, Index last, Index step, const Function& f, const static_partitioner& partitioner) { |
342 | parallel_for_impl<Index,Function,const static_partitioner>(first, last, step, f, partitioner); |
343 | } |
344 | //! Parallel iteration over a range of integers with a step provided and affinity partitioner |
345 | template <typename Index, typename Function> |
346 | __TBB_requires(parallel_for_index<Index> && parallel_for_function<Function, Index>) |
347 | void parallel_for(Index first, Index last, Index step, const Function& f, affinity_partitioner& partitioner) { |
348 | parallel_for_impl(first, last, step, f, partitioner); |
349 | } |
350 | |
351 | //! Parallel iteration over a range of integers with a default step value and default partitioner |
352 | template <typename Index, typename Function> |
353 | __TBB_requires(parallel_for_index<Index> && parallel_for_function<Function, Index>) |
354 | void parallel_for(Index first, Index last, const Function& f) { |
355 | parallel_for_impl<Index,Function,const auto_partitioner>(first, last, static_cast<Index>(1), f, auto_partitioner()); |
356 | } |
357 | //! Parallel iteration over a range of integers with a default step value and simple partitioner |
358 | template <typename Index, typename Function> |
359 | __TBB_requires(parallel_for_index<Index> && parallel_for_function<Function, Index>) |
360 | void parallel_for(Index first, Index last, const Function& f, const simple_partitioner& partitioner) { |
361 | parallel_for_impl<Index,Function,const simple_partitioner>(first, last, static_cast<Index>(1), f, partitioner); |
362 | } |
363 | //! Parallel iteration over a range of integers with a default step value and auto partitioner |
364 | template <typename Index, typename Function> |
365 | __TBB_requires(parallel_for_index<Index> && parallel_for_function<Function, Index>) |
366 | void parallel_for(Index first, Index last, const Function& f, const auto_partitioner& partitioner) { |
367 | parallel_for_impl<Index,Function,const auto_partitioner>(first, last, static_cast<Index>(1), f, partitioner); |
368 | } |
369 | //! Parallel iteration over a range of integers with a default step value and static partitioner |
370 | template <typename Index, typename Function> |
371 | __TBB_requires(parallel_for_index<Index> && parallel_for_function<Function, Index>) |
372 | void parallel_for(Index first, Index last, const Function& f, const static_partitioner& partitioner) { |
373 | parallel_for_impl<Index,Function,const static_partitioner>(first, last, static_cast<Index>(1), f, partitioner); |
374 | } |
375 | //! Parallel iteration over a range of integers with a default step value and affinity partitioner |
376 | template <typename Index, typename Function> |
377 | __TBB_requires(parallel_for_index<Index> && parallel_for_function<Function, Index>) |
378 | void parallel_for(Index first, Index last, const Function& f, affinity_partitioner& partitioner) { |
379 | parallel_for_impl(first, last, static_cast<Index>(1), f, partitioner); |
380 | } |
381 | |
382 | //! Implementation of parallel iteration over stepped range of integers with explicit step, task group context, and partitioner |
383 | template <typename Index, typename Function, typename Partitioner> |
384 | void parallel_for_impl(Index first, Index last, Index step, const Function& f, Partitioner& partitioner, task_group_context &context) { |
385 | if (step <= 0 ) |
386 | throw_exception(exception_id::nonpositive_step); // throws std::invalid_argument |
387 | else if (first < last) { |
388 | // Above "else" avoids "potential divide by zero" warning on some platforms |
389 | Index end = (last - first - Index(1)) / step + Index(1); |
390 | blocked_range<Index> range(static_cast<Index>(0), end); |
391 | parallel_for_body_wrapper<Function, Index> body(f, first, step); |
392 | parallel_for(range, body, partitioner, context); |
393 | } |
394 | } |
395 | |
396 | //! Parallel iteration over a range of integers with explicit step, task group context, and default partitioner |
397 | template <typename Index, typename Function> |
398 | __TBB_requires(parallel_for_index<Index> && parallel_for_function<Function, Index>) |
399 | void parallel_for(Index first, Index last, Index step, const Function& f, task_group_context &context) { |
400 | parallel_for_impl<Index,Function,const auto_partitioner>(first, last, step, f, auto_partitioner(), context); |
401 | } |
402 | //! Parallel iteration over a range of integers with explicit step, task group context, and simple partitioner |
403 | template <typename Index, typename Function> |
404 | __TBB_requires(parallel_for_index<Index> && parallel_for_function<Function, Index>) |
405 | void parallel_for(Index first, Index last, Index step, const Function& f, const simple_partitioner& partitioner, task_group_context &context) { |
406 | parallel_for_impl<Index,Function,const simple_partitioner>(first, last, step, f, partitioner, context); |
407 | } |
408 | //! Parallel iteration over a range of integers with explicit step, task group context, and auto partitioner |
409 | template <typename Index, typename Function> |
410 | __TBB_requires(parallel_for_index<Index> && parallel_for_function<Function, Index>) |
411 | void parallel_for(Index first, Index last, Index step, const Function& f, const auto_partitioner& partitioner, task_group_context &context) { |
412 | parallel_for_impl<Index,Function,const auto_partitioner>(first, last, step, f, partitioner, context); |
413 | } |
414 | //! Parallel iteration over a range of integers with explicit step, task group context, and static partitioner |
415 | template <typename Index, typename Function> |
416 | __TBB_requires(parallel_for_index<Index> && parallel_for_function<Function, Index>) |
417 | void parallel_for(Index first, Index last, Index step, const Function& f, const static_partitioner& partitioner, task_group_context &context) { |
418 | parallel_for_impl<Index,Function,const static_partitioner>(first, last, step, f, partitioner, context); |
419 | } |
420 | //! Parallel iteration over a range of integers with explicit step, task group context, and affinity partitioner |
421 | template <typename Index, typename Function> |
422 | __TBB_requires(parallel_for_index<Index> && parallel_for_function<Function, Index>) |
423 | void parallel_for(Index first, Index last, Index step, const Function& f, affinity_partitioner& partitioner, task_group_context &context) { |
424 | parallel_for_impl(first, last, step, f, partitioner, context); |
425 | } |
426 | |
427 | //! Parallel iteration over a range of integers with a default step value, explicit task group context, and default partitioner |
428 | template <typename Index, typename Function> |
429 | __TBB_requires(parallel_for_index<Index> && parallel_for_function<Function, Index>) |
430 | void parallel_for(Index first, Index last, const Function& f, task_group_context &context) { |
431 | parallel_for_impl<Index,Function,const auto_partitioner>(first, last, static_cast<Index>(1), f, auto_partitioner(), context); |
432 | } |
433 | //! Parallel iteration over a range of integers with a default step value, explicit task group context, and simple partitioner |
434 | template <typename Index, typename Function> |
435 | __TBB_requires(parallel_for_index<Index> && parallel_for_function<Function, Index>) |
436 | void parallel_for(Index first, Index last, const Function& f, const simple_partitioner& partitioner, task_group_context &context) { |
437 | parallel_for_impl<Index,Function,const simple_partitioner>(first, last, static_cast<Index>(1), f, partitioner, context); |
438 | } |
439 | //! Parallel iteration over a range of integers with a default step value, explicit task group context, and auto partitioner |
440 | template <typename Index, typename Function> |
441 | __TBB_requires(parallel_for_index<Index> && parallel_for_function<Function, Index>) |
442 | void parallel_for(Index first, Index last, const Function& f, const auto_partitioner& partitioner, task_group_context &context) { |
443 | parallel_for_impl<Index,Function,const auto_partitioner>(first, last, static_cast<Index>(1), f, partitioner, context); |
444 | } |
445 | //! Parallel iteration over a range of integers with a default step value, explicit task group context, and static partitioner |
446 | template <typename Index, typename Function> |
447 | __TBB_requires(parallel_for_index<Index> && parallel_for_function<Function, Index>) |
448 | void parallel_for(Index first, Index last, const Function& f, const static_partitioner& partitioner, task_group_context &context) { |
449 | parallel_for_impl<Index,Function,const static_partitioner>(first, last, static_cast<Index>(1), f, partitioner, context); |
450 | } |
451 | //! Parallel iteration over a range of integers with a default step value, explicit task group context, and affinity_partitioner |
452 | template <typename Index, typename Function> |
453 | __TBB_requires(parallel_for_index<Index> && parallel_for_function<Function, Index>) |
454 | void parallel_for(Index first, Index last, const Function& f, affinity_partitioner& partitioner, task_group_context &context) { |
455 | parallel_for_impl(first, last, static_cast<Index>(1), f, partitioner, context); |
456 | } |
457 | // @} |
458 | |
459 | } // namespace d1 |
460 | } // namespace detail |
461 | |
462 | inline namespace v1 { |
463 | using detail::d1::parallel_for; |
464 | // Split types |
465 | using detail::split; |
466 | using detail::proportional_split; |
467 | } // namespace v1 |
468 | |
469 | } // namespace tbb |
470 | |
471 | #endif /* __TBB_parallel_for_H */ |
472 | |