You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							541 lines
						
					
					
						
							25 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							541 lines
						
					
					
						
							25 KiB
						
					
					
				| /* | |
|     Copyright 2005-2014 Intel Corporation.  All Rights Reserved. | |
|  | |
|     This file is part of Threading Building Blocks. | |
|  | |
|     Threading Building Blocks is free software; you can redistribute it | |
|     and/or modify it under the terms of the GNU General Public License | |
|     version 2 as published by the Free Software Foundation. | |
|  | |
|     Threading Building Blocks is distributed in the hope that it will be | |
|     useful, but WITHOUT ANY WARRANTY; without even the implied warranty | |
|     of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | |
|     GNU General Public License for more details. | |
|  | |
|     You should have received a copy of the GNU General Public License | |
|     along with Threading Building Blocks; if not, write to the Free Software | |
|     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA | |
|  | |
|     As a special exception, you may use this file as part of a free software | |
|     library without restriction.  Specifically, if other files instantiate | |
|     templates or use macros or inline functions from this file, or you compile | |
|     this file and link it with other files to produce an executable, this | |
|     file does not by itself cause the resulting executable to be covered by | |
|     the GNU General Public License.  This exception does not however | |
|     invalidate any other reasons why the executable file might be covered by | |
|     the GNU General Public License. | |
| */ | |
| 
 | |
| #ifndef __TBB_parallel_reduce_H | |
| #define __TBB_parallel_reduce_H | |
|  | |
| #include <new> | |
| #include "task.h" | |
| #include "aligned_space.h" | |
| #include "partitioner.h" | |
| #include "tbb_profiling.h" | |
|  | |
| namespace tbb { | |
| 
 | |
| namespace interface6 { | |
| //! @cond INTERNAL | |
| namespace internal { | |
| 
 | |
|     using namespace tbb::internal; | |
| 
 | |
|     /** Values for reduction_context. */ | |
|     enum { | |
|         root_task, left_child, right_child | |
|     }; | |
| 
 | |
|     /** Represented as a char, not enum, for compactness. */ | |
|     typedef char reduction_context; | |
| 
 | |
|     //! Task type used to combine the partial results of parallel_reduce. | |
|     /** @ingroup algorithms */ | |
|     template<typename Body> | |
|     class finish_reduce: public flag_task { | |
|         //! Pointer to body, or NULL if the left child has not yet finished. | |
|         bool has_right_zombie; | |
|         const reduction_context my_context; | |
|         Body* my_body; | |
|         aligned_space<Body,1> zombie_space; | |
|         finish_reduce( reduction_context context_ ) : | |
|             has_right_zombie(false), // TODO: substitute by flag_task::child_stolen? | |
|             my_context(context_), | |
|             my_body(NULL) | |
|         { | |
|         } | |
|         ~finish_reduce() { | |
|             if( has_right_zombie ) | |
|                 zombie_space.begin()->~Body(); | |
|         } | |
|         task* execute() { | |
|             if( has_right_zombie ) { | |
|                 // Right child was stolen. | |
|                 Body* s = zombie_space.begin(); | |
|                 my_body->join( *s ); | |
|                 // Body::join() won't be called if canceled. Defer destruction to destructor | |
|             } | |
|             if( my_context==left_child ) | |
|                 itt_store_word_with_release( static_cast<finish_reduce*>(parent())->my_body, my_body ); | |
|             return NULL; | |
|         } | |
|         template<typename Range,typename Body_, typename Partitioner> | |
|         friend class start_reduce; | |
|     }; | |
| 
 | |
|     //! allocate right task with new parent | |
|     void allocate_sibling(task* start_reduce_task, task *tasks[], size_t start_bytes, size_t finish_bytes); | |
| 
 | |
|     //! Task type used to split the work of parallel_reduce. | |
|     /** @ingroup algorithms */ | |
|     template<typename Range, typename Body, typename Partitioner> | |
|     class start_reduce: public task { | |
|         typedef finish_reduce<Body> finish_type; | |
|         Body* my_body; | |
|         Range my_range; | |
|         typename Partitioner::task_partition_type my_partition; | |
|         reduction_context my_context; | |
|         /*override*/ task* execute(); | |
|         //! Update affinity info, if any | |
|         /*override*/ void note_affinity( affinity_id id ) { | |
|             my_partition.note_affinity( id ); | |
|         } | |
|         template<typename Body_> | |
|         friend class finish_reduce; | |
| 
 | |
| public: | |
|         //! Constructor used for root task | |
|         start_reduce( const Range& range, Body* body, Partitioner& partitioner ) : | |
|             my_body(body), | |
|             my_range(range), | |
|             my_partition(partitioner), | |
|             my_context(root_task) | |
|         { | |
|         } | |
|         //! Splitting constructor used to generate children. | |
|         /** parent_ becomes left child.  Newly constructed object is right child. */ | |
|         start_reduce( start_reduce& parent_, split ) : | |
|             my_body(parent_.my_body), | |
|             my_range(parent_.my_range, split()), | |
|             my_partition(parent_.my_partition, split()), | |
|             my_context(right_child) | |
|         { | |
|             my_partition.set_affinity(*this); | |
|             parent_.my_context = left_child; | |
|         } | |
|         //! Construct right child from the given range as response to the demand. | |
|         /** parent_ remains left child.  Newly constructed object is right child. */ | |
|         start_reduce( start_reduce& parent_, const Range& r, depth_t d ) : | |
|             my_body(parent_.my_body), | |
|             my_range(r), | |
|             my_partition(parent_.my_partition, split()), | |
|             my_context(right_child) | |
|         { | |
|             my_partition.set_affinity(*this); | |
|             my_partition.align_depth( d ); // TODO: move into constructor of partitioner | |
|             parent_.my_context = left_child; | |
|         } | |
|         static void run( const Range& range, Body& body, Partitioner& partitioner ) { | |
|             if( !range.empty() ) { | |
| #if !__TBB_TASK_GROUP_CONTEXT || TBB_JOIN_OUTER_TASK_GROUP | |
|                 task::spawn_root_and_wait( *new(task::allocate_root()) start_reduce(range,&body,partitioner) ); | |
| #else | |
|                 // Bound context prevents exceptions from body to affect nesting or sibling algorithms, | |
|                 // and allows users to handle exceptions safely by wrapping parallel_for in the try-block. | |
|                 task_group_context context; | |
|                 task::spawn_root_and_wait( *new(task::allocate_root(context)) start_reduce(range,&body,partitioner) ); | |
| #endif /* __TBB_TASK_GROUP_CONTEXT && !TBB_JOIN_OUTER_TASK_GROUP */ | |
|             } | |
|         } | |
| #if __TBB_TASK_GROUP_CONTEXT | |
|         static void run( const Range& range, Body& body, Partitioner& partitioner, task_group_context& context ) { | |
|             if( !range.empty() ) | |
|                 task::spawn_root_and_wait( *new(task::allocate_root(context)) start_reduce(range,&body,partitioner) ); | |
|         } | |
| #endif /* __TBB_TASK_GROUP_CONTEXT */ | |
|         //! Run body for range | |
|         void run_body( Range &r ) { (*my_body)( r ); } | |
| 
 | |
|         //! spawn right task, serves as callback for partitioner | |
|         // TODO: remove code duplication from 'offer_work' methods | |
|         void offer_work(split) { | |
|             task *tasks[2]; | |
|             allocate_sibling(static_cast<task*>(this), tasks, sizeof(start_reduce), sizeof(finish_type)); | |
|             new((void*)tasks[0]) finish_type(my_context); | |
|             new((void*)tasks[1]) start_reduce(*this, split()); | |
|             spawn(*tasks[1]); | |
|         } | |
|         //! spawn right task, serves as callback for partitioner | |
|         void offer_work(const Range& r, depth_t d = 0) { | |
|             task *tasks[2]; | |
|             allocate_sibling(static_cast<task*>(this), tasks, sizeof(start_reduce), sizeof(finish_type)); | |
|             new((void*)tasks[0]) finish_type(my_context); | |
|             new((void*)tasks[1]) start_reduce(*this, r, d); | |
|             spawn(*tasks[1]); | |
|         } | |
|     }; | |
| 
 | |
|     //! allocate right task with new parent | |
|     // TODO: 'inline' here is to avoid multiple definition error but for sake of code size this should not be inlined | |
|     inline void allocate_sibling(task* start_reduce_task, task *tasks[], size_t start_bytes, size_t finish_bytes) { | |
|         tasks[0] = &start_reduce_task->allocate_continuation().allocate(finish_bytes); | |
|         start_reduce_task->set_parent(tasks[0]); | |
|         tasks[0]->set_ref_count(2); | |
|         tasks[1] = &tasks[0]->allocate_child().allocate(start_bytes); | |
|     } | |
| 
 | |
|     template<typename Range, typename Body, typename Partitioner> | |
|     task* start_reduce<Range,Body,Partitioner>::execute() { | |
|         my_partition.check_being_stolen( *this ); | |
|         if( my_context==right_child ) { | |
|             finish_type* parent_ptr = static_cast<finish_type*>(parent()); | |
|             if( !itt_load_word_with_acquire(parent_ptr->my_body) ) { // TODO: replace by is_stolen_task() or by parent_ptr->ref_count() == 2??? | |
|                 my_body = new( parent_ptr->zombie_space.begin() ) Body(*my_body,split()); | |
|                 parent_ptr->has_right_zombie = true; | |
|             } | |
|         } else __TBB_ASSERT(my_context==root_task,NULL);// because left leaf spawns right leafs without recycling | |
|         my_partition.execute(*this, my_range); | |
|         if( my_context==left_child ) { | |
|             finish_type* parent_ptr = static_cast<finish_type*>(parent()); | |
|             __TBB_ASSERT(my_body!=parent_ptr->zombie_space.begin(),NULL); | |
|             itt_store_word_with_release(parent_ptr->my_body, my_body ); | |
|         } | |
|         return NULL; | |
|     } | |
| 
 | |
|     //! Task type used to combine the partial results of parallel_deterministic_reduce. | |
|     /** @ingroup algorithms */ | |
|     template<typename Body> | |
|     class finish_deterministic_reduce: public task { | |
|         Body &my_left_body; | |
|         Body my_right_body; | |
| 
 | |
|         finish_deterministic_reduce( Body &body ) : | |
|             my_left_body( body ), | |
|             my_right_body( body, split() ) | |
|         { | |
|         } | |
|         task* execute() { | |
|             my_left_body.join( my_right_body ); | |
|             return NULL; | |
|         } | |
|         template<typename Range,typename Body_> | |
|         friend class start_deterministic_reduce; | |
|     }; | |
| 
 | |
|     //! Task type used to split the work of parallel_deterministic_reduce. | |
|     /** @ingroup algorithms */ | |
|     template<typename Range, typename Body> | |
|     class start_deterministic_reduce: public task { | |
|         typedef finish_deterministic_reduce<Body> finish_type; | |
|         Body &my_body; | |
|         Range my_range; | |
|         /*override*/ task* execute(); | |
| 
 | |
|         //! Constructor used for root task | |
|         start_deterministic_reduce( const Range& range, Body& body ) : | |
|             my_body( body ), | |
|             my_range( range ) | |
|         { | |
|         } | |
|         //! Splitting constructor used to generate children. | |
|         /** parent_ becomes left child.  Newly constructed object is right child. */ | |
|         start_deterministic_reduce( start_deterministic_reduce& parent_, finish_type& c ) : | |
|             my_body( c.my_right_body ), | |
|             my_range( parent_.my_range, split() ) | |
|         { | |
|         } | |
| 
 | |
| public: | |
|         static void run( const Range& range, Body& body ) { | |
|             if( !range.empty() ) { | |
| #if !__TBB_TASK_GROUP_CONTEXT || TBB_JOIN_OUTER_TASK_GROUP | |
|                 task::spawn_root_and_wait( *new(task::allocate_root()) start_deterministic_reduce(range,&body) ); | |
| #else | |
|                 // Bound context prevents exceptions from body to affect nesting or sibling algorithms, | |
|                 // and allows users to handle exceptions safely by wrapping parallel_for in the try-block. | |
|                 task_group_context context; | |
|                 task::spawn_root_and_wait( *new(task::allocate_root(context)) start_deterministic_reduce(range,body) ); | |
| #endif /* __TBB_TASK_GROUP_CONTEXT && !TBB_JOIN_OUTER_TASK_GROUP */ | |
|             } | |
|         } | |
| #if __TBB_TASK_GROUP_CONTEXT | |
|         static void run( const Range& range, Body& body, task_group_context& context ) { | |
|             if( !range.empty() ) | |
|                 task::spawn_root_and_wait( *new(task::allocate_root(context)) start_deterministic_reduce(range,body) ); | |
|         } | |
| #endif /* __TBB_TASK_GROUP_CONTEXT */ | |
|     }; | |
| 
 | |
|     template<typename Range, typename Body> | |
|     task* start_deterministic_reduce<Range,Body>::execute() { | |
|         if( !my_range.is_divisible() ) { | |
|             my_body( my_range ); | |
|             return NULL; | |
|         } else { | |
|             finish_type& c = *new( allocate_continuation() ) finish_type( my_body ); | |
|             recycle_as_child_of(c); | |
|             c.set_ref_count(2); | |
|             start_deterministic_reduce& b = *new( c.allocate_child() ) start_deterministic_reduce( *this, c ); | |
|             task::spawn(b); | |
|             return this; | |
|         } | |
|     } | |
| } // namespace internal | |
| //! @endcond | |
| } //namespace interfaceX | |
|  | |
| //! @cond INTERNAL | |
| namespace internal { | |
|     using interface6::internal::start_reduce; | |
|     using interface6::internal::start_deterministic_reduce; | |
|     //! Auxiliary class for parallel_reduce; for internal use only. | |
|     /** The adaptor class that implements \ref parallel_reduce_body_req "parallel_reduce Body" | |
|         using given \ref parallel_reduce_lambda_req "anonymous function objects". | |
|      **/ | |
|     /** @ingroup algorithms */ | |
|     template<typename Range, typename Value, typename RealBody, typename Reduction> | |
|     class lambda_reduce_body { | |
| 
 | |
| //FIXME: decide if my_real_body, my_reduction, and identity_element should be copied or referenced | |
| //       (might require some performance measurements) | |
|  | |
|         const Value&     identity_element; | |
|         const RealBody&  my_real_body; | |
|         const Reduction& my_reduction; | |
|         Value            my_value; | |
|         lambda_reduce_body& operator= ( const lambda_reduce_body& other ); | |
|     public: | |
|         lambda_reduce_body( const Value& identity, const RealBody& body, const Reduction& reduction ) | |
|             : identity_element(identity) | |
|             , my_real_body(body) | |
|             , my_reduction(reduction) | |
|             , my_value(identity) | |
|         { } | |
|         lambda_reduce_body( const lambda_reduce_body& other ) | |
|             : identity_element(other.identity_element) | |
|             , my_real_body(other.my_real_body) | |
|             , my_reduction(other.my_reduction) | |
|             , my_value(other.my_value) | |
|         { } | |
|         lambda_reduce_body( lambda_reduce_body& other, tbb::split ) | |
|             : identity_element(other.identity_element) | |
|             , my_real_body(other.my_real_body) | |
|             , my_reduction(other.my_reduction) | |
|             , my_value(other.identity_element) | |
|         { } | |
|         void operator()(Range& range) { | |
|             my_value = my_real_body(range, const_cast<const Value&>(my_value)); | |
|         } | |
|         void join( lambda_reduce_body& rhs ) { | |
|             my_value = my_reduction(const_cast<const Value&>(my_value), const_cast<const Value&>(rhs.my_value)); | |
|         } | |
|         Value result() const { | |
|             return my_value; | |
|         } | |
|     }; | |
| 
 | |
| } // namespace internal | |
| //! @endcond | |
|  | |
| // Requirements on Range concept are documented in blocked_range.h | |
|  | |
| /** \page parallel_reduce_body_req Requirements on parallel_reduce body | |
|     Class \c Body implementing the concept of parallel_reduce body must define: | |
|     - \code Body::Body( Body&, split ); \endcode        Splitting constructor. | |
|                                                         Must be able to run concurrently with operator() and method \c join | |
|     - \code Body::~Body(); \endcode                     Destructor | |
|     - \code void Body::operator()( Range& r ); \endcode Function call operator applying body to range \c r | |
|                                                         and accumulating the result | |
|     - \code void Body::join( Body& b ); \endcode        Join results. | |
|                                                         The result in \c b should be merged into the result of \c this | |
| **/ | |
| 
 | |
| /** \page parallel_reduce_lambda_req Requirements on parallel_reduce anonymous function objects (lambda functions) | |
|     TO BE DOCUMENTED | |
| **/ | |
| 
 | |
| /** \name parallel_reduce | |
|     See also requirements on \ref range_req "Range" and \ref parallel_reduce_body_req "parallel_reduce Body". **/ | |
| //@{ | |
|  | |
| //! Parallel iteration with reduction and default partitioner. | |
| /** @ingroup algorithms **/ | |
| template<typename Range, typename Body> | |
| void parallel_reduce( const Range& range, Body& body ) { | |
|     internal::start_reduce<Range,Body, const __TBB_DEFAULT_PARTITIONER>::run( range, body, __TBB_DEFAULT_PARTITIONER() ); | |
| } | |
| 
 | |
| //! Parallel iteration with reduction and simple_partitioner | |
| /** @ingroup algorithms **/ | |
| template<typename Range, typename Body> | |
| void parallel_reduce( const Range& range, Body& body, const simple_partitioner& partitioner ) { | |
|     internal::start_reduce<Range,Body,const simple_partitioner>::run( range, body, partitioner ); | |
| } | |
| 
 | |
| //! Parallel iteration with reduction and auto_partitioner | |
| /** @ingroup algorithms **/ | |
| template<typename Range, typename Body> | |
| void parallel_reduce( const Range& range, Body& body, const auto_partitioner& partitioner ) { | |
|     internal::start_reduce<Range,Body,const auto_partitioner>::run( range, body, partitioner ); | |
| } | |
| 
 | |
| //! Parallel iteration with reduction and affinity_partitioner | |
| /** @ingroup algorithms **/ | |
| template<typename Range, typename Body> | |
| void parallel_reduce( const Range& range, Body& body, affinity_partitioner& partitioner ) { | |
|     internal::start_reduce<Range,Body,affinity_partitioner>::run( range, body, partitioner ); | |
| } | |
| 
 | |
| #if __TBB_TASK_GROUP_CONTEXT | |
| //! Parallel iteration with reduction, simple partitioner and user-supplied context. | |
| /** @ingroup algorithms **/ | |
| template<typename Range, typename Body> | |
| void parallel_reduce( const Range& range, Body& body, const simple_partitioner& partitioner, task_group_context& context ) { | |
|     internal::start_reduce<Range,Body,const simple_partitioner>::run( range, body, partitioner, context ); | |
| } | |
| 
 | |
| //! Parallel iteration with reduction, auto_partitioner and user-supplied context | |
| /** @ingroup algorithms **/ | |
| template<typename Range, typename Body> | |
| void parallel_reduce( const Range& range, Body& body, const auto_partitioner& partitioner, task_group_context& context ) { | |
|     internal::start_reduce<Range,Body,const auto_partitioner>::run( range, body, partitioner, context ); | |
| } | |
| 
 | |
| //! Parallel iteration with reduction, affinity_partitioner and user-supplied context | |
| /** @ingroup algorithms **/ | |
| template<typename Range, typename Body> | |
| void parallel_reduce( const Range& range, Body& body, affinity_partitioner& partitioner, task_group_context& context ) { | |
|     internal::start_reduce<Range,Body,affinity_partitioner>::run( range, body, partitioner, context ); | |
| } | |
| #endif /* __TBB_TASK_GROUP_CONTEXT */ | |
|  | |
| /** parallel_reduce overloads that work with anonymous function objects | |
|     (see also \ref parallel_reduce_lambda_req "requirements on parallel_reduce anonymous function objects"). **/ | |
| 
 | |
| //! Parallel iteration with reduction and default partitioner. | |
| /** @ingroup algorithms **/ | |
| template<typename Range, typename Value, typename RealBody, typename Reduction> | |
| Value parallel_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction ) { | |
|     internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(identity, real_body, reduction); | |
|     internal::start_reduce<Range,internal::lambda_reduce_body<Range,Value,RealBody,Reduction>,const __TBB_DEFAULT_PARTITIONER> | |
|                           ::run(range, body, __TBB_DEFAULT_PARTITIONER() ); | |
|     return body.result(); | |
| } | |
| 
 | |
| //! Parallel iteration with reduction and simple_partitioner. | |
| /** @ingroup algorithms **/ | |
| template<typename Range, typename Value, typename RealBody, typename Reduction> | |
| Value parallel_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction, | |
|                        const simple_partitioner& partitioner ) { | |
|     internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(identity, real_body, reduction); | |
|     internal::start_reduce<Range,internal::lambda_reduce_body<Range,Value,RealBody,Reduction>,const simple_partitioner> | |
|                           ::run(range, body, partitioner ); | |
|     return body.result(); | |
| } | |
| 
 | |
| //! Parallel iteration with reduction and auto_partitioner | |
| /** @ingroup algorithms **/ | |
| template<typename Range, typename Value, typename RealBody, typename Reduction> | |
| Value parallel_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction, | |
|                        const auto_partitioner& partitioner ) { | |
|     internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(identity, real_body, reduction); | |
|     internal::start_reduce<Range,internal::lambda_reduce_body<Range,Value,RealBody,Reduction>,const auto_partitioner> | |
|                           ::run( range, body, partitioner ); | |
|     return body.result(); | |
| } | |
| 
 | |
| //! Parallel iteration with reduction and affinity_partitioner | |
| /** @ingroup algorithms **/ | |
| template<typename Range, typename Value, typename RealBody, typename Reduction> | |
| Value parallel_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction, | |
|                        affinity_partitioner& partitioner ) { | |
|     internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(identity, real_body, reduction); | |
|     internal::start_reduce<Range,internal::lambda_reduce_body<Range,Value,RealBody,Reduction>,affinity_partitioner> | |
|                                         ::run( range, body, partitioner ); | |
|     return body.result(); | |
| } | |
| 
 | |
| #if __TBB_TASK_GROUP_CONTEXT | |
| //! Parallel iteration with reduction, simple partitioner and user-supplied context. | |
| /** @ingroup algorithms **/ | |
| template<typename Range, typename Value, typename RealBody, typename Reduction> | |
| Value parallel_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction, | |
|                        const simple_partitioner& partitioner, task_group_context& context ) { | |
|     internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(identity, real_body, reduction); | |
|     internal::start_reduce<Range,internal::lambda_reduce_body<Range,Value,RealBody,Reduction>,const simple_partitioner> | |
|                           ::run( range, body, partitioner, context ); | |
|     return body.result(); | |
| } | |
| 
 | |
| //! Parallel iteration with reduction, auto_partitioner and user-supplied context | |
| /** @ingroup algorithms **/ | |
| template<typename Range, typename Value, typename RealBody, typename Reduction> | |
| Value parallel_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction, | |
|                        const auto_partitioner& partitioner, task_group_context& context ) { | |
|     internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(identity, real_body, reduction); | |
|     internal::start_reduce<Range,internal::lambda_reduce_body<Range,Value,RealBody,Reduction>,const auto_partitioner> | |
|                           ::run( range, body, partitioner, context ); | |
|     return body.result(); | |
| } | |
| 
 | |
| //! Parallel iteration with reduction, affinity_partitioner and user-supplied context | |
| /** @ingroup algorithms **/ | |
| template<typename Range, typename Value, typename RealBody, typename Reduction> | |
| Value parallel_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction, | |
|                        affinity_partitioner& partitioner, task_group_context& context ) { | |
|     internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(identity, real_body, reduction); | |
|     internal::start_reduce<Range,internal::lambda_reduce_body<Range,Value,RealBody,Reduction>,affinity_partitioner> | |
|                                         ::run( range, body, partitioner, context ); | |
|     return body.result(); | |
| } | |
| #endif /* __TBB_TASK_GROUP_CONTEXT */ | |
|  | |
| //! Parallel iteration with deterministic reduction and default partitioner. | |
| /** @ingroup algorithms **/ | |
| template<typename Range, typename Body> | |
| void parallel_deterministic_reduce( const Range& range, Body& body ) { | |
|     internal::start_deterministic_reduce<Range,Body>::run( range, body ); | |
| } | |
| 
 | |
| #if __TBB_TASK_GROUP_CONTEXT | |
| //! Parallel iteration with deterministic reduction, simple partitioner and user-supplied context. | |
| /** @ingroup algorithms **/ | |
| template<typename Range, typename Body> | |
| void parallel_deterministic_reduce( const Range& range, Body& body, task_group_context& context ) { | |
|     internal::start_deterministic_reduce<Range,Body>::run( range, body, context ); | |
| } | |
| #endif /* __TBB_TASK_GROUP_CONTEXT */ | |
|  | |
| /** parallel_reduce overloads that work with anonymous function objects | |
|     (see also \ref parallel_reduce_lambda_req "requirements on parallel_reduce anonymous function objects"). **/ | |
| 
 | |
| //! Parallel iteration with deterministic reduction and default partitioner. | |
| /** @ingroup algorithms **/ | |
| template<typename Range, typename Value, typename RealBody, typename Reduction> | |
| Value parallel_deterministic_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction ) { | |
|     internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(identity, real_body, reduction); | |
|     internal::start_deterministic_reduce<Range,internal::lambda_reduce_body<Range,Value,RealBody,Reduction> > | |
|                           ::run(range, body); | |
|     return body.result(); | |
| } | |
| 
 | |
| #if __TBB_TASK_GROUP_CONTEXT | |
| //! Parallel iteration with deterministic reduction, simple partitioner and user-supplied context. | |
| /** @ingroup algorithms **/ | |
| template<typename Range, typename Value, typename RealBody, typename Reduction> | |
| Value parallel_deterministic_reduce( const Range& range, const Value& identity, const RealBody& real_body, const Reduction& reduction, | |
|                        task_group_context& context ) { | |
|     internal::lambda_reduce_body<Range,Value,RealBody,Reduction> body(identity, real_body, reduction); | |
|     internal::start_deterministic_reduce<Range,internal::lambda_reduce_body<Range,Value,RealBody,Reduction> > | |
|                           ::run( range, body, context ); | |
|     return body.result(); | |
| } | |
| #endif /* __TBB_TASK_GROUP_CONTEXT */ | |
| //@} | |
|  | |
| } // namespace tbb | |
|  | |
| #endif /* __TBB_parallel_reduce_H */
 |