Shadowrun: Awakened 29 September 2011 - Build 871
parallel_do.h
Go to the documentation of this file.
00001 /*
00002     Copyright 2005-2010 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
00493 
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 © 2007-2010 by The Shadowrun: Awakened Team. This work is licensed under the GNU Lesser General Public License 3.

GNU Lesser General Public License 3 Sourceforge.net