Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 10:12:57

0001 /*
0002     Copyright (c) 2005-2020 Intel Corporation
0003 
0004     Licensed under the Apache License, Version 2.0 (the "License");
0005     you may not use this file except in compliance with the License.
0006     You may obtain a copy of the License at
0007 
0008         http://www.apache.org/licenses/LICENSE-2.0
0009 
0010     Unless required by applicable law or agreed to in writing, software
0011     distributed under the License is distributed on an "AS IS" BASIS,
0012     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013     See the License for the specific language governing permissions and
0014     limitations under the License.
0015 */
0016 
0017 #ifndef __TBB_parallel_do_H
0018 #define __TBB_parallel_do_H
0019 
0020 #define __TBB_parallel_do_H_include_area
0021 #include "internal/_warning_suppress_enable_notice.h"
0022 
0023 #include "internal/_range_iterator.h"
0024 #include "internal/_template_helpers.h"
0025 #include "task.h"
0026 #include "aligned_space.h"
0027 #include <iterator>
0028 
0029 namespace tbb {
0030 namespace interface9 {
0031 //! @cond INTERNAL
0032 namespace internal {
0033     template<typename Body, typename Item> class parallel_do_feeder_impl;
0034 } // namespace internal
0035 //! @endcond
0036 
0037 //! Class the user supplied algorithm body uses to add new tasks
0038 /** \param Item Work item type **/
0039     template<typename Item>
0040     class parallel_do_feeder: ::tbb::internal::no_copy
0041     {
0042         parallel_do_feeder() {}
0043         virtual ~parallel_do_feeder () {}
0044         virtual void internal_add_copy( const Item& item ) = 0;
0045 #if __TBB_CPP11_RVALUE_REF_PRESENT
0046         virtual void internal_add_move( Item&& item ) = 0;
0047 #endif
0048         template<typename Body_, typename Item_> friend class internal::parallel_do_feeder_impl;
0049     public:
0050         //! Add a work item to a running parallel_do.
0051         void add( const Item& item ) {internal_add_copy(item);}
0052 #if __TBB_CPP11_RVALUE_REF_PRESENT
0053         void add( Item&& item ) {internal_add_move(std::move(item));}
0054 #endif
0055     };
0056 
0057 //! @cond INTERNAL
0058 namespace internal {
0059     template<typename Body> class do_group_task;
0060 
0061     //! For internal use only.
0062     /** Selects one of the two possible forms of function call member operator.
0063         @ingroup algorithms **/
0064     template<class Body, typename Item>
0065     class parallel_do_operator_selector
0066     {
0067         typedef parallel_do_feeder<Item> Feeder;
0068         template<typename A1, typename A2, typename CvItem >
0069         static void internal_call( const Body& obj, __TBB_FORWARDING_REF(A1) arg1, A2&, void (Body::*)(CvItem) const ) {
0070             obj(tbb::internal::forward<A1>(arg1));
0071         }
0072         template<typename A1, typename A2, typename CvItem >
0073         static void internal_call( const Body& obj, __TBB_FORWARDING_REF(A1) arg1, A2& arg2, void (Body::*)(CvItem, parallel_do_feeder<Item>&) const ) {
0074             obj(tbb::internal::forward<A1>(arg1), arg2);
0075         }
0076         template<typename A1, typename A2, typename CvItem >
0077         static void internal_call( const Body& obj, __TBB_FORWARDING_REF(A1) arg1, A2&, void (Body::*)(CvItem&) const ) {
0078             obj(arg1);
0079         }
0080         template<typename A1, typename A2, typename CvItem >
0081         static void internal_call( const Body& obj, __TBB_FORWARDING_REF(A1) arg1, A2& arg2, void (Body::*)(CvItem&, parallel_do_feeder<Item>&) const ) {
0082             obj(arg1, arg2);
0083         }
0084     public:
0085         template<typename A1, typename A2>
0086         static void call( const Body& obj, __TBB_FORWARDING_REF(A1) arg1, A2& arg2 )
0087         {
0088             internal_call( obj, tbb::internal::forward<A1>(arg1), arg2, &Body::operator() );
0089         }
0090     };
0091 
0092     //! For internal use only.
0093     /** Executes one iteration of a do.
0094         @ingroup algorithms */
0095     template<typename Body, typename Item>
0096     class do_iteration_task: public task
0097     {
0098         typedef parallel_do_feeder_impl<Body, Item> feeder_type;
0099 
0100         Item my_value;
0101         feeder_type& my_feeder;
0102 
0103         do_iteration_task( const Item& value, feeder_type& feeder ) :
0104             my_value(value), my_feeder(feeder)
0105         {}
0106 
0107 #if __TBB_CPP11_RVALUE_REF_PRESENT
0108         do_iteration_task( Item&& value, feeder_type& feeder ) :
0109             my_value(std::move(value)), my_feeder(feeder)
0110         {}
0111 #endif
0112 
0113         task* execute() __TBB_override
0114         {
0115             parallel_do_operator_selector<Body, Item>::call(*my_feeder.my_body, tbb::internal::move(my_value), my_feeder);
0116             return NULL;
0117         }
0118 
0119         template<typename Body_, typename Item_> friend class parallel_do_feeder_impl;
0120     }; // class do_iteration_task
0121 
0122     template<typename Iterator, typename Body, typename Item>
0123     class do_iteration_task_iter: public task
0124     {
0125         typedef parallel_do_feeder_impl<Body, Item> feeder_type;
0126 
0127         Iterator my_iter;
0128         feeder_type& my_feeder;
0129 
0130         do_iteration_task_iter( const Iterator& iter, feeder_type& feeder ) :
0131             my_iter(iter), my_feeder(feeder)
0132         {}
0133 
0134         task* execute() __TBB_override
0135         {
0136             parallel_do_operator_selector<Body, Item>::call(*my_feeder.my_body, *my_iter, my_feeder);
0137             return NULL;
0138         }
0139 
0140         template<typename Iterator_, typename Body_, typename Item_> friend class do_group_task_forward;
0141         template<typename Body_, typename Item_> friend class do_group_task_input;
0142         template<typename Iterator_, typename Body_, typename Item_> friend class do_task_iter;
0143     }; // class do_iteration_task_iter
0144 
0145     //! For internal use only.
0146     /** Implements new task adding procedure.
0147         @ingroup algorithms **/
0148     template<class Body, typename Item>
0149     class parallel_do_feeder_impl : public parallel_do_feeder<Item>
0150     {
0151 #if __TBB_CPP11_RVALUE_REF_PRESENT
0152         //Avoiding use of copy constructor in a virtual method if the type does not support it
0153         void internal_add_copy_impl(std::true_type, const Item& item) {
0154             typedef do_iteration_task<Body, Item> iteration_type;
0155             iteration_type& t = *new (task::allocate_additional_child_of(*my_barrier)) iteration_type(item, *this);
0156             task::spawn(t);
0157         }
0158         void internal_add_copy_impl(std::false_type, const Item&) {
0159             __TBB_ASSERT(false, "Overloading for r-value reference doesn't work or it's not movable and not copyable object");
0160         }
0161         void internal_add_copy( const Item& item ) __TBB_override
0162         {
0163 #if __TBB_CPP11_IS_COPY_CONSTRUCTIBLE_PRESENT
0164             internal_add_copy_impl(typename std::is_copy_constructible<Item>::type(), item);
0165 #else
0166             internal_add_copy_impl(std::true_type(), item);
0167 #endif
0168         }
0169         void internal_add_move( Item&& item ) __TBB_override
0170         {
0171             typedef do_iteration_task<Body, Item> iteration_type;
0172             iteration_type& t = *new (task::allocate_additional_child_of(*my_barrier)) iteration_type(std::move(item), *this);
0173             task::spawn(t);
0174         }
0175 #else /* ! __TBB_CPP11_RVALUE_REF_PRESENT */
0176         void internal_add_copy(const Item& item) __TBB_override {
0177             typedef do_iteration_task<Body, Item> iteration_type;
0178             iteration_type& t = *new (task::allocate_additional_child_of(*my_barrier)) iteration_type(item, *this);
0179             task::spawn(t);
0180         }
0181 #endif /* __TBB_CPP11_RVALUE_REF_PRESENT */
0182     public:
0183         const Body* my_body;
0184         empty_task* my_barrier;
0185 
0186         parallel_do_feeder_impl()
0187         {
0188             my_barrier = new( task::allocate_root() ) empty_task();
0189             __TBB_ASSERT(my_barrier, "root task allocation failed");
0190         }
0191 
0192 #if __TBB_TASK_GROUP_CONTEXT
0193         parallel_do_feeder_impl(tbb::task_group_context &context)
0194         {
0195             my_barrier = new( task::allocate_root(context) ) empty_task();
0196             __TBB_ASSERT(my_barrier, "root task allocation failed");
0197         }
0198 #endif
0199 
0200         ~parallel_do_feeder_impl()
0201         {
0202             my_barrier->destroy(*my_barrier);
0203         }
0204     }; // class parallel_do_feeder_impl
0205 
0206 
0207     //! For internal use only
0208     /** Unpacks a block of iterations.
0209         @ingroup algorithms */
0210 
0211     template<typename Iterator, typename Body, typename Item>
0212     class do_group_task_forward: public task
0213     {
0214         static const size_t max_arg_size = 4;
0215 
0216         typedef parallel_do_feeder_impl<Body, Item> feeder_type;
0217 
0218         feeder_type& my_feeder;
0219         Iterator my_first;
0220         size_t my_size;
0221 
0222         do_group_task_forward( Iterator first, size_t size, feeder_type& feeder )
0223             : my_feeder(feeder), my_first(first), my_size(size)
0224         {}
0225 
0226         task* execute() __TBB_override
0227         {
0228             typedef do_iteration_task_iter<Iterator, Body, Item> iteration_type;
0229             __TBB_ASSERT( my_size>0, NULL );
0230             task_list list;
0231             task* t;
0232             size_t k=0;
0233             for(;;) {
0234                 t = new( allocate_child() ) iteration_type( my_first, my_feeder );
0235                 ++my_first;
0236                 if( ++k==my_size ) break;
0237                 list.push_back(*t);
0238             }
0239             set_ref_count(int(k+1));
0240             spawn(list);
0241             spawn_and_wait_for_all(*t);
0242             return NULL;
0243         }
0244 
0245         template<typename Iterator_, typename Body_, typename _Item> friend class do_task_iter;
0246     }; // class do_group_task_forward
0247 
0248     template<typename Body, typename Item>
0249     class do_group_task_input: public task
0250     {
0251         static const size_t max_arg_size = 4;
0252 
0253         typedef parallel_do_feeder_impl<Body, Item> feeder_type;
0254 
0255         feeder_type& my_feeder;
0256         size_t my_size;
0257         aligned_space<Item, max_arg_size> my_arg;
0258 
0259         do_group_task_input( feeder_type& feeder )
0260             : my_feeder(feeder), my_size(0)
0261         {}
0262 
0263         task* execute() __TBB_override
0264         {
0265 #if __TBB_CPP11_RVALUE_REF_PRESENT
0266             typedef std::move_iterator<Item*> Item_iterator;
0267 #else
0268             typedef Item* Item_iterator;
0269 #endif
0270             typedef do_iteration_task_iter<Item_iterator, Body, Item> iteration_type;
0271             __TBB_ASSERT( my_size>0, NULL );
0272             task_list list;
0273             task* t;
0274             size_t k=0;
0275             for(;;) {
0276                 t = new( allocate_child() ) iteration_type( Item_iterator(my_arg.begin() + k), my_feeder );
0277                 if( ++k==my_size ) break;
0278                 list.push_back(*t);
0279             }
0280             set_ref_count(int(k+1));
0281             spawn(list);
0282             spawn_and_wait_for_all(*t);
0283             return NULL;
0284         }
0285 
0286         ~do_group_task_input(){
0287             for( size_t k=0; k<my_size; ++k)
0288                 (my_arg.begin() + k)->~Item();
0289         }
0290 
0291         template<typename Iterator_, typename Body_, typename Item_> friend class do_task_iter;
0292     }; // class do_group_task_input
0293 
0294     //! For internal use only.
0295     /** Gets block of iterations and packages them into a do_group_task.
0296         @ingroup algorithms */
0297     template<typename Iterator, typename Body, typename Item>
0298     class do_task_iter: public task
0299     {
0300         typedef parallel_do_feeder_impl<Body, Item> feeder_type;
0301 
0302     public:
0303         do_task_iter( Iterator first, Iterator last , feeder_type& feeder ) :
0304             my_first(first), my_last(last), my_feeder(feeder)
0305         {}
0306 
0307     private:
0308         Iterator my_first;
0309         Iterator my_last;
0310         feeder_type& my_feeder;
0311 
0312         /* Do not merge run(xxx) and run_xxx() methods. They are separated in order
0313             to make sure that compilers will eliminate unused argument of type xxx
0314             (that is will not put it on stack). The sole purpose of this argument
0315             is overload resolution.
0316 
0317             An alternative could be using template functions, but explicit specialization
0318             of member function templates is not supported for non specialized class
0319             templates. Besides template functions would always fall back to the least
0320             efficient variant (the one for input iterators) in case of iterators having
0321             custom tags derived from basic ones. */
0322         task* execute() __TBB_override
0323         {
0324             typedef typename std::iterator_traits<Iterator>::iterator_category iterator_tag;
0325             return run( (iterator_tag*)NULL );
0326         }
0327 
0328         /** This is the most restricted variant that operates on input iterators or
0329             iterators with unknown tags (tags not derived from the standard ones). **/
0330         inline task* run( void* ) { return run_for_input_iterator(); }
0331 
0332         task* run_for_input_iterator() {
0333             typedef do_group_task_input<Body, Item> block_type;
0334 
0335             block_type& t = *new( allocate_additional_child_of(*my_feeder.my_barrier) ) block_type(my_feeder);
0336             size_t k=0;
0337             while( !(my_first == my_last) ) {
0338                 // Move semantics are automatically used when supported by the iterator
0339                 new (t.my_arg.begin() + k) Item(*my_first);
0340                 ++my_first;
0341                 if( ++k==block_type::max_arg_size ) {
0342                     if ( !(my_first == my_last) )
0343                         recycle_to_reexecute();
0344                     break;
0345                 }
0346             }
0347             if( k==0 ) {
0348                 destroy(t);
0349                 return NULL;
0350             } else {
0351                 t.my_size = k;
0352                 return &t;
0353             }
0354         }
0355 
0356         inline task* run( std::forward_iterator_tag* ) { return run_for_forward_iterator(); }
0357 
0358         task* run_for_forward_iterator() {
0359             typedef do_group_task_forward<Iterator, Body, Item> block_type;
0360 
0361             Iterator first = my_first;
0362             size_t k=0;
0363             while( !(my_first==my_last) ) {
0364                 ++my_first;
0365                 if( ++k==block_type::max_arg_size ) {
0366                     if ( !(my_first==my_last) )
0367                         recycle_to_reexecute();
0368                     break;
0369                 }
0370             }
0371             return k==0 ? NULL : new( allocate_additional_child_of(*my_feeder.my_barrier) ) block_type(first, k, my_feeder);
0372         }
0373 
0374         inline task* run( std::random_access_iterator_tag* ) { return run_for_random_access_iterator(); }
0375 
0376         task* run_for_random_access_iterator() {
0377             typedef do_group_task_forward<Iterator, Body, Item> block_type;
0378             typedef do_iteration_task_iter<Iterator, Body, Item> iteration_type;
0379 
0380             size_t k = static_cast<size_t>(my_last-my_first);
0381             if( k > block_type::max_arg_size ) {
0382                 Iterator middle = my_first + k/2;
0383 
0384                 empty_task& c = *new( allocate_continuation() ) empty_task;
0385                 do_task_iter& b = *new( c.allocate_child() ) do_task_iter(middle, my_last, my_feeder);
0386                 recycle_as_child_of(c);
0387 
0388                 my_last = middle;
0389                 c.set_ref_count(2);
0390                 c.spawn(b);
0391                 return this;
0392             }else if( k != 0 ) {
0393                 task_list list;
0394                 task* t;
0395                 size_t k1=0;
0396                 for(;;) {
0397                     t = new( allocate_child() ) iteration_type(my_first, my_feeder);
0398                     ++my_first;
0399                     if( ++k1==k ) break;
0400                     list.push_back(*t);
0401                 }
0402                 set_ref_count(int(k+1));
0403                 spawn(list);
0404                 spawn_and_wait_for_all(*t);
0405             }
0406             return NULL;
0407         }
0408     }; // class do_task_iter
0409 
0410     //! For internal use only.
0411     /** Implements parallel iteration over a range.
0412         @ingroup algorithms */
0413     template<typename Iterator, typename Body, typename Item>
0414     void run_parallel_do( Iterator first, Iterator last, const Body& body
0415 #if __TBB_TASK_GROUP_CONTEXT
0416         , task_group_context& context
0417 #endif
0418         )
0419     {
0420         typedef do_task_iter<Iterator, Body, Item> root_iteration_task;
0421 #if __TBB_TASK_GROUP_CONTEXT
0422         parallel_do_feeder_impl<Body, Item> feeder(context);
0423 #else
0424         parallel_do_feeder_impl<Body, Item> feeder;
0425 #endif
0426         feeder.my_body = &body;
0427 
0428         root_iteration_task &t = *new( feeder.my_barrier->allocate_child() ) root_iteration_task(first, last, feeder);
0429 
0430         feeder.my_barrier->set_ref_count(2);
0431         feeder.my_barrier->spawn_and_wait_for_all(t);
0432     }
0433 
0434     //! For internal use only.
0435     /** Detects types of Body's operator function arguments.
0436         @ingroup algorithms **/
0437     template<typename Iterator, typename Body, typename Item>
0438     void select_parallel_do( Iterator first, Iterator last, const Body& body, void (Body::*)(Item) const
0439 #if __TBB_TASK_GROUP_CONTEXT
0440         , task_group_context& context
0441 #endif
0442         )
0443     {
0444         run_parallel_do<Iterator, Body, typename ::tbb::internal::strip<Item>::type>( first, last, body
0445 #if __TBB_TASK_GROUP_CONTEXT
0446             , context
0447 #endif
0448             );
0449     }
0450 
0451     //! For internal use only.
0452     /** Detects types of Body's operator function arguments.
0453         @ingroup algorithms **/
0454     template<typename Iterator, typename Body, typename Item, typename _Item>
0455     void select_parallel_do( Iterator first, Iterator last, const Body& body, void (Body::*)(Item, parallel_do_feeder<_Item>&) const
0456 #if __TBB_TASK_GROUP_CONTEXT
0457         , task_group_context& context
0458 #endif
0459         )
0460     {
0461         run_parallel_do<Iterator, Body, typename ::tbb::internal::strip<Item>::type>( first, last, body
0462 #if __TBB_TASK_GROUP_CONTEXT
0463             , context
0464 #endif
0465             );
0466     }
0467 
0468 } // namespace internal
0469 } // namespace interface9
0470 //! @endcond
0471 
0472 /** \page parallel_do_body_req Requirements on parallel_do body
0473     Class \c Body implementing the concept of parallel_do body must define:
0474     - \code
0475         B::operator()(
0476                 cv_item_type item,
0477                 parallel_do_feeder<item_type>& feeder
0478         ) const
0479 
0480         OR
0481 
0482         B::operator()( cv_item_type& item ) const
0483       \endcode                                               Process item.
0484                                                              May be invoked concurrently  for the same \c this but different \c item.
0485 
0486     - \code item_type( const item_type& ) \endcode
0487                                                              Copy a work item.
0488     - \code ~item_type() \endcode                            Destroy a work item
0489 **/
0490 
0491 /** \name parallel_do
0492     See also requirements on \ref parallel_do_body_req "parallel_do Body". **/
0493 //@{
0494 //! Parallel iteration over a range, with optional addition of more work.
0495 /** @ingroup algorithms */
0496 template<typename Iterator, typename Body>
0497 void parallel_do( Iterator first, Iterator last, const Body& body )
0498 {
0499     if ( first == last )
0500         return;
0501 #if __TBB_TASK_GROUP_CONTEXT
0502     task_group_context context(internal::PARALLEL_DO);
0503 #endif
0504     interface9::internal::select_parallel_do( first, last, body, &Body::operator()
0505 #if __TBB_TASK_GROUP_CONTEXT
0506         , context
0507 #endif
0508         );
0509 }
0510 
0511 template<typename Range, typename Body>
0512 void parallel_do(Range& rng, const Body& body) {
0513     parallel_do(tbb::internal::first(rng), tbb::internal::last(rng), body);
0514 }
0515 
0516 template<typename Range, typename Body>
0517 void parallel_do(const Range& rng, const Body& body) {
0518     parallel_do(tbb::internal::first(rng), tbb::internal::last(rng), body);
0519 }
0520 
0521 #if __TBB_TASK_GROUP_CONTEXT
0522 //! Parallel iteration over a range, with optional addition of more work and user-supplied context
0523 /** @ingroup algorithms */
0524 template<typename Iterator, typename Body>
0525 void parallel_do( Iterator first, Iterator last, const Body& body, task_group_context& context  )
0526 {
0527     if ( first == last )
0528         return;
0529     interface9::internal::select_parallel_do( first, last, body, &Body::operator(), context );
0530 }
0531 
0532 template<typename Range, typename Body>
0533 void parallel_do(Range& rng, const Body& body, task_group_context& context) {
0534     parallel_do(tbb::internal::first(rng), tbb::internal::last(rng), body, context);
0535 }
0536 
0537 template<typename Range, typename Body>
0538 void parallel_do(const Range& rng, const Body& body, task_group_context& context) {
0539     parallel_do(tbb::internal::first(rng), tbb::internal::last(rng), body, context);
0540 }
0541 
0542 #endif // __TBB_TASK_GROUP_CONTEXT
0543 
0544 //@}
0545 
0546 using interface9::parallel_do_feeder;
0547 
0548 } // namespace
0549 
0550 #include "internal/_warning_suppress_disable_notice.h"
0551 #undef __TBB_parallel_do_H_include_area
0552 
0553 #endif /* __TBB_parallel_do_H */