- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

# parallel_do.h

    00001 /*
    00002     Copyright 2005-2012 Intel Corporation.  All Rights Reserved.
    00003
    00004     This file is part of Threading Building Blocks.
    00005
    00006     Threading Building Blocks is free software; you can redistribute it
    00007     and/or modify it under the terms of the GNU General Public License
    00008     version 2 as published by the Free Software Foundation.
    00009
    00010     Threading Building Blocks is distributed in the hope that it will be
    00011     useful, but WITHOUT ANY WARRANTY; without even the implied warranty
    00012     of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    00013     GNU General Public License for more details.
    00014
    00015     You should have received a copy of the GNU General Public License
    00016     along with Threading Building Blocks; if not, write to the Free Software
    00017     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    00018
    00019     As a special exception, you may use this file as part of a free software
    00020     library without restriction.  Specifically, if other files instantiate
    00021     templates or use macros or inline functions from this file, or you compile
    00022     this file and link it with other files to produce an executable, this
    00023     file does not by itself cause the resulting executable to be covered by
    00024     the GNU General Public License.  This exception does not however
    00025     invalidate any other reasons why the executable file might be covered by
    00026     the GNU General Public License.
    00027 */
    00028
    00029 #ifndef __TBB_parallel_do_H
    00030 #define __TBB_parallel_do_H
    00031
    00032 #include "task.h"
    00033 #include "aligned_space.h"
    00034 #include <iterator>
    00035
    00036 namespace tbb {
    00037
    00039 namespace internal {
    00040     template<typename Body, typename Item> class parallel_do_feeder_impl;
    00041     template<typename Body> class do_group_task;
    00042
    00044     template<typename T>
    00045     struct strip { typedef T type; };
    00046     template<typename T>
    00047     struct strip<T&> { typedef T type; };
    00048     template<typename T>
    00049     struct strip<const T&> { typedef T type; };
    00050     template<typename T>
    00051     struct strip<volatile T&> { typedef T type; };
    00052     template<typename T>
    00053     struct strip<const volatile T&> { typedef T type; };
    00054     // Most of the compilers remove cv-qualifiers from non-reference function argument types.
    00055     // But unfortunately there are those that don't.
    00056     template<typename T>
    00057     struct strip<const T> { typedef T type; };
    00058     template<typename T>
    00059     struct strip<volatile T> { typedef T type; };
    00060     template<typename T>
    00061     struct strip<const volatile T> { typedef T type; };
    00062 } // namespace internal
    00064
    00066
    00067 template<typename Item>
    00068 class parallel_do_feeder: internal::no_copy
    00069 {
    00070     parallel_do_feeder() {}
    00071     virtual ~parallel_do_feeder () {}
    00072     virtual void internal_add( const Item& item ) = 0;
    00073     template<typename Body_, typename Item_> friend class internal::parallel_do_feeder_impl;
    00074 public:
    00076     void add( const Item& item ) {internal_add(item);}
    00077 };
    00078
    00080 namespace internal {
    00082
    00084     template<class Body, typename Item>
    00085     class parallel_do_operator_selector
    00086     {
    00087         typedef parallel_do_feeder<Item> Feeder;
    00088         template<typename A1, typename A2, typename CvItem >
    00089         static void internal_call( const Body& obj, A1& arg1, A2&, void (Body::*)(CvItem) const ) {
    00090             obj(arg1);
    00091         }
    00092         template<typename A1, typename A2, typename CvItem >
    00093         static void internal_call( const Body& obj, A1& arg1, A2& arg2, void (Body::*)(CvItem, parallel_do_feeder<Item>&) const ) {
    00094             obj(arg1, arg2);
    00095         }
    00096
    00097     public:
    00098         template<typename A1, typename A2 >
    00099         static void call( const Body& obj, A1& arg1, A2& arg2 )
    00100         {
    00101             internal_call( obj, arg1, arg2, &Body::operator() );
    00102         }
    00103     };
    00104
    00106
    00108     template<typename Body, typename Item>
    00109     class do_iteration_task: public task
    00110     {
    00111         typedef parallel_do_feeder_impl<Body, Item> feeder_type;
    00112
    00113         Item my_value;
    00114         feeder_type& my_feeder;
    00115
    00116         do_iteration_task( const Item& value, feeder_type& feeder ) :
    00117             my_value(value), my_feeder(feeder)
    00118         {}
    00119
    00120         /*override*/
    00121         task* execute()
    00122         {
    00123             parallel_do_operator_selector<Body, Item>::call(*my_feeder.my_body, my_value, my_feeder);
    00124             return NULL;
    00125         }
    00126
    00127         template<typename Body_, typename Item_> friend class parallel_do_feeder_impl;
    00128     }; // class do_iteration_task
    00129
    00130     template<typename Iterator, typename Body, typename Item>
    00131     class do_iteration_task_iter: public task
    00132     {
    00133         typedef parallel_do_feeder_impl<Body, Item> feeder_type;
    00134
    00135         Iterator my_iter;
    00136         feeder_type& my_feeder;
    00137
    00138         do_iteration_task_iter( const Iterator& iter, feeder_type& feeder ) :
    00139             my_iter(iter), my_feeder(feeder)
    00140         {}
    00141
    00142         /*override*/
    00143         task* execute()
    00144         {
    00145             parallel_do_operator_selector<Body, Item>::call(*my_feeder.my_body, *my_iter, my_feeder);
    00146             return NULL;
    00147         }
    00148
    00149         template<typename Iterator_, typename Body_, typename Item_> friend class do_group_task_forward;
    00150         template<typename Body_, typename Item_> friend class do_group_task_input;
    00151         template<typename Iterator_, typename Body_, typename Item_> friend class do_task_iter;
    00152     }; // class do_iteration_task_iter
    00153
    00155
    00157     template<class Body, typename Item>
    00158     class parallel_do_feeder_impl : public parallel_do_feeder<Item>
    00159     {
    00160         /*override*/
    00161         void internal_add( const Item& item )
    00162         {
    00163             typedef do_iteration_task<Body, Item> iteration_type;
    00164
    00165             iteration_type& t = *new (task::allocate_additional_child_of(*my_barrier)) iteration_type(item, *this);
    00166
    00167             t.spawn( t );
    00168         }
    00169     public:
    00170         const Body* my_body;
    00171         empty_task* my_barrier;
    00172
    00173         parallel_do_feeder_impl()
    00174         {
    00175             my_barrier = new( task::allocate_root() ) empty_task();
    00176             __TBB_ASSERT(my_barrier, "root task allocation failed");
    00177         }
    00178
    00179 #if __TBB_TASK_GROUP_CONTEXT
    00180         parallel_do_feeder_impl(tbb::task_group_context &context)
    00181         {
    00182             my_barrier = new( task::allocate_root(context) ) empty_task();
    00183             __TBB_ASSERT(my_barrier, "root task allocation failed");
    00184         }
    00185 #endif
    00186
    00187         ~parallel_do_feeder_impl()
    00188         {
    00189             my_barrier->destroy(*my_barrier);
    00190         }
    00191     }; // class parallel_do_feeder_impl
    00192
    00193
    00195
    00198     template<typename Iterator, typename Body, typename Item>
    00199     class do_group_task_forward: public task
    00200     {
    00201         static const size_t max_arg_size = 4;
    00202
    00203         typedef parallel_do_feeder_impl<Body, Item> feeder_type;
    00204
    00205         feeder_type& my_feeder;
    00206         Iterator my_first;
    00207         size_t my_size;
    00208
    00209         do_group_task_forward( Iterator first, size_t size, feeder_type& feeder )
    00210             : my_feeder(feeder), my_first(first), my_size(size)
    00211         {}
    00212
    00213         /*override*/ task* execute()
    00214         {
    00215             typedef do_iteration_task_iter<Iterator, Body, Item> iteration_type;
    00216             __TBB_ASSERT( my_size>0, NULL );
    00217             task_list list;
    00218             task* t;
    00219             size_t k=0;
    00220             for(;;) {
    00221                 t = new( allocate_child() ) iteration_type( my_first, my_feeder );
    00222                 ++my_first;
    00223                 if( ++k==my_size ) break;
    00224                 list.push_back(*t);
    00225             }
    00226             set_ref_count(int(k+1));
    00227             spawn(list);
    00228             spawn_and_wait_for_all(*t);
    00229             return NULL;
    00230         }
    00231
    00232         template<typename Iterator_, typename Body_, typename _Item> friend class do_task_iter;
    00233     }; // class do_group_task_forward
    00234
    00235     template<typename Body, typename Item>
    00236     class do_group_task_input: public task
    00237     {
    00238         static const size_t max_arg_size = 4;
    00239
    00240         typedef parallel_do_feeder_impl<Body, Item> feeder_type;
    00241
    00242         feeder_type& my_feeder;
    00243         size_t my_size;
    00244         aligned_space<Item, max_arg_size> my_arg;
    00245
    00246         do_group_task_input( feeder_type& feeder )
    00247             : my_feeder(feeder), my_size(0)
    00248         {}
    00249
    00250         /*override*/ task* execute()
    00251         {
    00252             typedef do_iteration_task_iter<Item*, Body, Item> iteration_type;
    00253             __TBB_ASSERT( my_size>0, NULL );
    00254             task_list list;
    00255             task* t;
    00256             size_t k=0;
    00257             for(;;) {
    00258                 t = new( allocate_child() ) iteration_type( my_arg.begin() + k, my_feeder );
    00259                 if( ++k==my_size ) break;
    00260                 list.push_back(*t);
    00261             }
    00262             set_ref_count(int(k+1));
    00263             spawn(list);
    00264             spawn_and_wait_for_all(*t);
    00265             return NULL;
    00266         }
    00267
    00268         ~do_group_task_input(){
    00269             for( size_t k=0; k<my_size; ++k)
    00270                 (my_arg.begin() + k)->~Item();
    00271         }
    00272
    00273         template<typename Iterator_, typename Body_, typename Item_> friend class do_task_iter;
    00274     }; // class do_group_task_input
    00275
    00277
    00279     template<typename Iterator, typename Body, typename Item>
    00280     class do_task_iter: public task
    00281     {
    00282         typedef parallel_do_feeder_impl<Body, Item> feeder_type;
    00283
    00284     public:
    00285         do_task_iter( Iterator first, Iterator last , feeder_type& feeder ) :
    00286             my_first(first), my_last(last), my_feeder(feeder)
    00287         {}
    00288
    00289     private:
    00290         Iterator my_first;
    00291         Iterator my_last;
    00292         feeder_type& my_feeder;
    00293
    00294         /* Do not merge run(xxx) and run_xxx() methods. They are separated in order
    00295             to make sure that compilers will eliminate unused argument of type xxx
    00296             (that is will not put it on stack). The sole purpose of this argument
    00297             is overload resolution.
    00298
    00299             An alternative could be using template functions, but explicit specialization
    00300             of member function templates is not supported for non specialized class
    00301             templates. Besides template functions would always fall back to the least
    00302             efficient variant (the one for input iterators) in case of iterators having
    00303             custom tags derived from basic ones. */
    00304         /*override*/ task* execute()
    00305         {
    00306             typedef typename std::iterator_traits<Iterator>::iterator_category iterator_tag;
    00307             return run( (iterator_tag*)NULL );
    00308         }
    00309
    00312         inline task* run( void* ) { return run_for_input_iterator(); }
    00313
    00314         task* run_for_input_iterator() {
    00315             typedef do_group_task_input<Body, Item> block_type;
    00316
    00317             block_type& t = *new( allocate_additional_child_of(*my_feeder.my_barrier) ) block_type(my_feeder);
    00318             size_t k=0;
    00319             while( !(my_first == my_last) ) {
    00320                 new (t.my_arg.begin() + k) Item(*my_first);
    00321                 ++my_first;
    00322                 if( ++k==block_type::max_arg_size ) {
    00323                     if ( !(my_first == my_last) )
    00324                         recycle_to_reexecute();
    00325                     break;
    00326                 }
    00327             }
    00328             if( k==0 ) {
    00329                 destroy(t);
    00330                 return NULL;
    00331             } else {
    00332                 t.my_size = k;
    00333                 return &t;
    00334             }
    00335         }
    00336
    00337         inline task* run( std::forward_iterator_tag* ) { return run_for_forward_iterator(); }
    00338
    00339         task* run_for_forward_iterator() {
    00340             typedef do_group_task_forward<Iterator, Body, Item> block_type;
    00341
    00342             Iterator first = my_first;
    00343             size_t k=0;
    00344             while( !(my_first==my_last) ) {
    00345                 ++my_first;
    00346                 if( ++k==block_type::max_arg_size ) {
    00347                     if ( !(my_first==my_last) )
    00348                         recycle_to_reexecute();
    00349                     break;
    00350                 }
    00351             }
    00352             return k==0 ? NULL : new( allocate_additional_child_of(*my_feeder.my_barrier) ) block_type(first, k, my_feeder);
    00353         }
    00354
    00355         inline task* run( std::random_access_iterator_tag* ) { return run_for_random_access_iterator(); }
    00356
    00357         task* run_for_random_access_iterator() {
    00358             typedef do_group_task_forward<Iterator, Body, Item> block_type;
    00359             typedef do_iteration_task_iter<Iterator, Body, Item> iteration_type;
    00360
    00361             size_t k = static_cast<size_t>(my_last-my_first);
    00362             if( k > block_type::max_arg_size ) {
    00363                 Iterator middle = my_first + k/2;
    00364
    00365                 empty_task& c = *new( allocate_continuation() ) empty_task;
    00366                 do_task_iter& b = *new( c.allocate_child() ) do_task_iter(middle, my_last, my_feeder);
    00367                 recycle_as_child_of(c);
    00368
    00369                 my_last = middle;
    00370                 c.set_ref_count(2);
    00371                 c.spawn(b);
    00372                 return this;
    00373             }else if( k != 0 ) {
    00374                 task_list list;
    00375                 task* t;
    00376                 size_t k1=0;
    00377                 for(;;) {
    00378                     t = new( allocate_child() ) iteration_type(my_first, my_feeder);
    00379                     ++my_first;
    00380                     if( ++k1==k ) break;
    00381                     list.push_back(*t);
    00382                 }
    00383                 set_ref_count(int(k+1));
    00384                 spawn(list);
    00385                 spawn_and_wait_for_all(*t);
    00386             }
    00387             return NULL;
    00388         }
    00389     }; // class do_task_iter
    00390
    00392
    00394     template<typename Iterator, typename Body, typename Item>
    00395     void run_parallel_do( Iterator first, Iterator last, const Body& body
    00396 #if __TBB_TASK_GROUP_CONTEXT
    00397         , task_group_context& context
    00398 #endif
    00399         )
    00400     {
    00401         typedef do_task_iter<Iterator, Body, Item> root_iteration_task;
    00402 #if __TBB_TASK_GROUP_CONTEXT
    00403         parallel_do_feeder_impl<Body, Item> feeder(context);
    00404 #else
    00405         parallel_do_feeder_impl<Body, Item> feeder;
    00406 #endif
    00407         feeder.my_body = &body;
    00408
    00409         root_iteration_task &t = *new( feeder.my_barrier->allocate_child() ) root_iteration_task(first, last, feeder);
    00410
    00411         feeder.my_barrier->set_ref_count(2);
    00412         feeder.my_barrier->spawn_and_wait_for_all(t);
    00413     }
    00414
    00416
    00418     template<typename Iterator, typename Body, typename Item>
    00419     void select_parallel_do( Iterator first, Iterator last, const Body& body, void (Body::*)(Item) const
    00420 #if __TBB_TASK_GROUP_CONTEXT
    00421         , task_group_context& context
    00422 #endif // __TBB_TASK_GROUP_CONTEXT
    00423         )
    00424     {
    00425         run_parallel_do<Iterator, Body, typename strip<Item>::type>( first, last, body
    00426 #if __TBB_TASK_GROUP_CONTEXT
    00427             , context
    00428 #endif // __TBB_TASK_GROUP_CONTEXT
    00429             );
    00430     }
    00431
    00433
    00435     template<typename Iterator, typename Body, typename Item, typename _Item>
    00436     void select_parallel_do( Iterator first, Iterator last, const Body& body, void (Body::*)(Item, parallel_do_feeder<_Item>&) const
    00437 #if __TBB_TASK_GROUP_CONTEXT
    00438         , task_group_context& context
    00439 #endif // __TBB_TASK_GROUP_CONTEXT
    00440         )
    00441     {
    00442         run_parallel_do<Iterator, Body, typename strip<Item>::type>( first, last, body
    00443 #if __TBB_TASK_GROUP_CONTEXT
    00444             , context
    00445 #endif // __TBB_TASK_GROUP_CONTEXT
    00446             );
    00447     }
    00448
    00449 } // namespace internal
    00451
    00452
    00475
    00476
    00477 template<typename Iterator, typename Body>
    00478 void parallel_do( Iterator first, Iterator last, const Body& body )
    00479 {
    00480     if ( first == last )
    00481         return;
    00482 #if __TBB_TASK_GROUP_CONTEXT
    00483     task_group_context context;
    00484 #endif // __TBB_TASK_GROUP_CONTEXT
    00485     internal::select_parallel_do( first, last, body, &Body::operator()
    00486 #if __TBB_TASK_GROUP_CONTEXT
    00487         , context
    00488 #endif // __TBB_TASK_GROUP_CONTEXT
    00489         );
    00490 }
    00491
    00492 #if __TBB_TASK_GROUP_CONTEXT
    00494
    00495 template<typename Iterator, typename Body>
    00496 void parallel_do( Iterator first, Iterator last, const Body& body, task_group_context& context  )
    00497 {
    00498     if ( first == last )
    00499         return;
    00500     internal::select_parallel_do( first, last, body, &Body::operator(), context );
    00501 }
    00502 #endif // __TBB_TASK_GROUP_CONTEXT
    00503
    00505
    00506 } // namespace
    00507
    00508 #endif /* __TBB_parallel_do_H */

Copyright © 2005-2012 Intel Corporation. All Rights Reserved.

Intel, Pentium, Intel Xeon, Itanium, Intel XScale and VTune are registered trademarks or trademarks of Intel Corporation or its subsidiaries in the United States and other countries.

* Other names and brands may be claimed as the property of others.