| /* |
| Copyright 2005-2010 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_partitioner_H |
| #define __TBB_partitioner_H |
| |
| #include "task.h" |
| |
| namespace tbb { |
| class affinity_partitioner; |
| |
| //! @cond INTERNAL |
| namespace internal { |
| size_t __TBB_EXPORTED_FUNC get_initial_auto_partitioner_divisor(); |
| |
| //! Defines entry points into tbb run-time library; |
| /** The entry points are the constructor and destructor. */ |
| class affinity_partitioner_base_v3: no_copy { |
| friend class tbb::affinity_partitioner; |
| //! Array that remembers affinities of tree positions to affinity_id. |
| /** NULL if my_size==0. */ |
| affinity_id* my_array; |
| //! Number of elements in my_array. |
| size_t my_size; |
| //! Zeros the fields. |
| affinity_partitioner_base_v3() : my_array(NULL), my_size(0) {} |
| //! Deallocates my_array. |
| ~affinity_partitioner_base_v3() {resize(0);} |
| //! Resize my_array. |
| /** Retains values if resulting size is the same. */ |
| void __TBB_EXPORTED_METHOD resize( unsigned factor ); |
| friend class affinity_partition_type; |
| }; |
| |
| //! Provides default methods for partition objects without affinity. |
| class partition_type_base { |
| public: |
| void set_affinity( task & ) {} |
| void note_affinity( task::affinity_id ) {} |
| task* continue_after_execute_range() {return NULL;} |
| bool decide_whether_to_delay() {return false;} |
| void spawn_or_delay( bool, task& b ) { |
| task::spawn(b); |
| } |
| }; |
| |
| class affinity_partition_type; |
| |
| template<typename Range, typename Body, typename Partitioner> class start_for; |
| template<typename Range, typename Body, typename Partitioner> class start_reduce; |
| template<typename Range, typename Body> class start_reduce_with_affinity; |
| template<typename Range, typename Body, typename Partitioner> class start_scan; |
| |
| } // namespace internal |
| //! @endcond |
| |
| //! A simple partitioner |
| /** Divides the range until the range is not divisible. |
| @ingroup algorithms */ |
| class simple_partitioner { |
| public: |
| simple_partitioner() {} |
| private: |
| template<typename Range, typename Body, typename Partitioner> friend class internal::start_for; |
| template<typename Range, typename Body, typename Partitioner> friend class internal::start_reduce; |
| template<typename Range, typename Body, typename Partitioner> friend class internal::start_scan; |
| |
| class partition_type: public internal::partition_type_base { |
| public: |
| bool should_execute_range(const task& ) {return false;} |
| partition_type( const simple_partitioner& ) {} |
| partition_type( const partition_type&, split ) {} |
| }; |
| }; |
| |
| //! An auto partitioner |
| /** The range is initial divided into several large chunks. |
| Chunks are further subdivided into VICTIM_CHUNKS pieces if they are stolen and divisible. |
| @ingroup algorithms */ |
| class auto_partitioner { |
| public: |
| auto_partitioner() {} |
| |
| private: |
| template<typename Range, typename Body, typename Partitioner> friend class internal::start_for; |
| template<typename Range, typename Body, typename Partitioner> friend class internal::start_reduce; |
| template<typename Range, typename Body, typename Partitioner> friend class internal::start_scan; |
| |
| class partition_type: public internal::partition_type_base { |
| size_t num_chunks; |
| static const size_t VICTIM_CHUNKS = 4; |
| public: |
| bool should_execute_range(const task &t) { |
| if( num_chunks<VICTIM_CHUNKS && t.is_stolen_task() ) |
| num_chunks = VICTIM_CHUNKS; |
| return num_chunks==1; |
| } |
| partition_type( const auto_partitioner& ) : num_chunks(internal::get_initial_auto_partitioner_divisor()) {} |
| partition_type( partition_type& pt, split ) { |
| num_chunks = pt.num_chunks /= 2u; |
| } |
| }; |
| }; |
| |
| //! An affinity partitioner |
| class affinity_partitioner: internal::affinity_partitioner_base_v3 { |
| public: |
| affinity_partitioner() {} |
| |
| private: |
| template<typename Range, typename Body, typename Partitioner> friend class internal::start_for; |
| template<typename Range, typename Body, typename Partitioner> friend class internal::start_reduce; |
| template<typename Range, typename Body> friend class internal::start_reduce_with_affinity; |
| template<typename Range, typename Body, typename Partitioner> friend class internal::start_scan; |
| |
| typedef internal::affinity_partition_type partition_type; |
| friend class internal::affinity_partition_type; |
| }; |
| |
| //! @cond INTERNAL |
| namespace internal { |
| |
| class affinity_partition_type: public no_copy { |
| //! Must be power of two |
| static const unsigned factor = 16; |
| static const size_t VICTIM_CHUNKS = 4; |
| |
| internal::affinity_id* my_array; |
| task_list delay_list; |
| unsigned map_begin, map_end; |
| size_t num_chunks; |
| public: |
| affinity_partition_type( affinity_partitioner& ap ) { |
| __TBB_ASSERT( (factor&(factor-1))==0, "factor must be power of two" ); |
| ap.resize(factor); |
| my_array = ap.my_array; |
| map_begin = 0; |
| map_end = unsigned(ap.my_size); |
| num_chunks = internal::get_initial_auto_partitioner_divisor(); |
| } |
| affinity_partition_type(affinity_partition_type& p, split) : my_array(p.my_array) { |
| __TBB_ASSERT( p.map_end-p.map_begin<factor || (p.map_end-p.map_begin)%factor==0, NULL ); |
| num_chunks = p.num_chunks /= 2; |
| unsigned e = p.map_end; |
| unsigned d = (e - p.map_begin)/2; |
| if( d>factor ) |
| d &= 0u-factor; |
| map_end = e; |
| map_begin = p.map_end = e-d; |
| } |
| |
| bool should_execute_range(const task &t) { |
| if( num_chunks < VICTIM_CHUNKS && t.is_stolen_task() ) |
| num_chunks = VICTIM_CHUNKS; |
| return num_chunks == 1; |
| } |
| |
| void set_affinity( task &t ) { |
| if( map_begin<map_end ) |
| t.set_affinity( my_array[map_begin] ); |
| } |
| void note_affinity( task::affinity_id id ) { |
| if( map_begin<map_end ) |
| my_array[map_begin] = id; |
| } |
| task* continue_after_execute_range() { |
| task* first = NULL; |
| if( !delay_list.empty() ) { |
| first = &delay_list.pop_front(); |
| while( !delay_list.empty() ) { |
| task::spawn(*first); |
| first = &delay_list.pop_front(); |
| } |
| } |
| return first; |
| } |
| bool decide_whether_to_delay() { |
| // The possible underflow caused by "-1u" is deliberate |
| return (map_begin&(factor-1))==0 && map_end-map_begin-1u<factor; |
| } |
| void spawn_or_delay( bool delay, task& b ) { |
| if( delay ) |
| delay_list.push_back(b); |
| else |
| task::spawn(b); |
| } |
| |
| ~affinity_partition_type() { |
| // The delay_list can be non-empty if an exception is thrown. |
| while( !delay_list.empty() ) { |
| task& t = delay_list.pop_front(); |
| t.destroy(t); |
| } |
| } |
| }; |
| |
| } // namespace internal |
| //! @endcond |
| |
| |
| } // namespace tbb |
| |
| #endif /* __TBB_partitioner_H */ |