149e08aacStbbdev /* 2*b15aabb3Stbbdev Copyright (c) 2005-2021 Intel Corporation 349e08aacStbbdev 449e08aacStbbdev Licensed under the Apache License, Version 2.0 (the "License"); 549e08aacStbbdev you may not use this file except in compliance with the License. 649e08aacStbbdev You may obtain a copy of the License at 749e08aacStbbdev 849e08aacStbbdev http://www.apache.org/licenses/LICENSE-2.0 949e08aacStbbdev 1049e08aacStbbdev Unless required by applicable law or agreed to in writing, software 1149e08aacStbbdev distributed under the License is distributed on an "AS IS" BASIS, 1249e08aacStbbdev WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1349e08aacStbbdev See the License for the specific language governing permissions and 1449e08aacStbbdev limitations under the License. 1549e08aacStbbdev */ 1649e08aacStbbdev 1749e08aacStbbdev #ifndef __TBB_parallel_for_each_H 1849e08aacStbbdev #define __TBB_parallel_for_each_H 1949e08aacStbbdev 2049e08aacStbbdev #include "detail/_config.h" 2149e08aacStbbdev #include "detail/_namespace_injection.h" 2249e08aacStbbdev #include "detail/_exception.h" 2349e08aacStbbdev #include "detail/_task.h" 2449e08aacStbbdev #include "detail/_aligned_space.h" 2549e08aacStbbdev #include "detail/_small_object_pool.h" 2649e08aacStbbdev 2749e08aacStbbdev #include "parallel_for.h" 2849e08aacStbbdev #include "task_group.h" // task_group_context 2949e08aacStbbdev 3049e08aacStbbdev #include <iterator> 3149e08aacStbbdev #include <type_traits> 3249e08aacStbbdev 3349e08aacStbbdev namespace tbb { 3449e08aacStbbdev namespace detail { 35*b15aabb3Stbbdev namespace d2 { 3649e08aacStbbdev template<typename Body, typename Item> class feeder_impl; 37*b15aabb3Stbbdev } // namespace d2 3849e08aacStbbdev 39*b15aabb3Stbbdev namespace d1 { 4049e08aacStbbdev //! Class the user supplied algorithm body uses to add new tasks 4149e08aacStbbdev template<typename Item> 4249e08aacStbbdev class feeder { 4349e08aacStbbdev feeder() {} 4449e08aacStbbdev feeder(const feeder&) = delete; 4549e08aacStbbdev void operator=( const feeder&) = delete; 4649e08aacStbbdev 4749e08aacStbbdev virtual ~feeder () {} 4849e08aacStbbdev virtual void internal_add_copy(const Item& item) = 0; 4949e08aacStbbdev virtual void internal_add_move(Item&& item) = 0; 5049e08aacStbbdev 51*b15aabb3Stbbdev template<typename Body_, typename Item_> friend class detail::d2::feeder_impl; 5249e08aacStbbdev public: 5349e08aacStbbdev //! Add a work item to a running parallel_for_each. 5449e08aacStbbdev void add(const Item& item) {internal_add_copy(item);} 5549e08aacStbbdev void add(Item&& item) {internal_add_move(std::move(item));} 5649e08aacStbbdev }; 5749e08aacStbbdev 58*b15aabb3Stbbdev } // namespace d1 59*b15aabb3Stbbdev 60*b15aabb3Stbbdev namespace d2 { 61*b15aabb3Stbbdev using namespace tbb::detail::d1; 6249e08aacStbbdev /** Selects one of the two possible forms of function call member operator. 6349e08aacStbbdev @ingroup algorithms **/ 64*b15aabb3Stbbdev template<class Body> 65*b15aabb3Stbbdev struct parallel_for_each_operator_selector { 6649e08aacStbbdev public: 6749e08aacStbbdev template<typename ItemArg, typename FeederArg> 68*b15aabb3Stbbdev static auto call(const Body& body, ItemArg&& item, FeederArg*) 6949e08aacStbbdev -> decltype(body(std::forward<ItemArg>(item)), void()) { 7049e08aacStbbdev #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) 7149e08aacStbbdev // Suppression of Microsoft non-standard extension warnings 7249e08aacStbbdev #pragma warning (push) 7349e08aacStbbdev #pragma warning (disable: 4239) 7449e08aacStbbdev #endif 7549e08aacStbbdev 7649e08aacStbbdev body(std::forward<ItemArg>(item)); 7749e08aacStbbdev 7849e08aacStbbdev #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) 7949e08aacStbbdev #pragma warning (push) 8049e08aacStbbdev #endif 8149e08aacStbbdev } 8249e08aacStbbdev 8349e08aacStbbdev template<typename ItemArg, typename FeederArg> 84*b15aabb3Stbbdev static auto call(const Body& body, ItemArg&& item, FeederArg* feeder) 85*b15aabb3Stbbdev -> decltype(body(std::forward<ItemArg>(item), *feeder), void()) { 8649e08aacStbbdev #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) 8749e08aacStbbdev // Suppression of Microsoft non-standard extension warnings 8849e08aacStbbdev #pragma warning (push) 8949e08aacStbbdev #pragma warning (disable: 4239) 9049e08aacStbbdev #endif 91*b15aabb3Stbbdev __TBB_ASSERT(feeder, "Feeder was not created but should be"); 9249e08aacStbbdev 93*b15aabb3Stbbdev body(std::forward<ItemArg>(item), *feeder); 9449e08aacStbbdev 9549e08aacStbbdev #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) 9649e08aacStbbdev #pragma warning (push) 9749e08aacStbbdev #endif 9849e08aacStbbdev } 9949e08aacStbbdev }; 10049e08aacStbbdev 10149e08aacStbbdev template<typename Body, typename Item> 10249e08aacStbbdev struct feeder_item_task: public task { 10349e08aacStbbdev using feeder_type = feeder_impl<Body, Item>; 10449e08aacStbbdev 10549e08aacStbbdev template <typename ItemType> 10649e08aacStbbdev feeder_item_task(ItemType&& input_item, feeder_type& feeder, small_object_allocator& alloc) : 10749e08aacStbbdev item(std::forward<ItemType>(input_item)), 10849e08aacStbbdev my_feeder(feeder), 10949e08aacStbbdev my_allocator(alloc) 11049e08aacStbbdev {} 11149e08aacStbbdev 11249e08aacStbbdev void finalize(const execution_data& ed) { 11349e08aacStbbdev my_feeder.my_wait_context.release(); 11449e08aacStbbdev my_allocator.delete_object(this, ed); 11549e08aacStbbdev } 11649e08aacStbbdev 117*b15aabb3Stbbdev //! Hack for resolve ambiguity between calls to the body with and without moving the stored copy 118*b15aabb3Stbbdev //! Executing body with moving the copy should have higher priority 119*b15aabb3Stbbdev using first_priority = int; 120*b15aabb3Stbbdev using second_priority = double; 121*b15aabb3Stbbdev 122*b15aabb3Stbbdev template <typename BodyType, typename ItemType, typename FeederType> 123*b15aabb3Stbbdev static auto call(const BodyType& call_body, ItemType& call_item, FeederType& call_feeder, first_priority) 124*b15aabb3Stbbdev -> decltype(parallel_for_each_operator_selector<Body>::call(call_body, std::move(call_item), &call_feeder), void()) 125*b15aabb3Stbbdev { 126*b15aabb3Stbbdev parallel_for_each_operator_selector<Body>::call(call_body, std::move(call_item), &call_feeder); 127*b15aabb3Stbbdev } 128*b15aabb3Stbbdev 129*b15aabb3Stbbdev template <typename BodyType, typename ItemType, typename FeederType> 130*b15aabb3Stbbdev static void call(const BodyType& call_body, ItemType& call_item, FeederType& call_feeder, second_priority) { 131*b15aabb3Stbbdev parallel_for_each_operator_selector<Body>::call(call_body, call_item, &call_feeder); 132*b15aabb3Stbbdev } 133*b15aabb3Stbbdev 13449e08aacStbbdev task* execute(execution_data& ed) override { 135*b15aabb3Stbbdev call(my_feeder.my_body, item, my_feeder, first_priority{}); 13649e08aacStbbdev finalize(ed); 13749e08aacStbbdev return nullptr; 13849e08aacStbbdev } 13949e08aacStbbdev 14049e08aacStbbdev task* cancel(execution_data& ed) override { 14149e08aacStbbdev finalize(ed); 14249e08aacStbbdev return nullptr; 14349e08aacStbbdev } 14449e08aacStbbdev 14549e08aacStbbdev Item item; 14649e08aacStbbdev feeder_type& my_feeder; 14749e08aacStbbdev small_object_allocator my_allocator; 14849e08aacStbbdev }; // class feeder_item_task 14949e08aacStbbdev 15049e08aacStbbdev /** Implements new task adding procedure. 15149e08aacStbbdev @ingroup algorithms **/ 152*b15aabb3Stbbdev template<typename Body, typename Item> 15349e08aacStbbdev class feeder_impl : public feeder<Item> { 15449e08aacStbbdev // Avoiding use of copy constructor in a virtual method if the type does not support it 15549e08aacStbbdev void internal_add_copy_impl(std::true_type, const Item& item) { 15649e08aacStbbdev using feeder_task = feeder_item_task<Body, Item>; 15749e08aacStbbdev small_object_allocator alloc; 15849e08aacStbbdev auto task = alloc.new_object<feeder_task>(item, *this, alloc); 15949e08aacStbbdev 16049e08aacStbbdev my_wait_context.reserve(); 16149e08aacStbbdev spawn(*task, my_execution_context); 16249e08aacStbbdev } 16349e08aacStbbdev 16449e08aacStbbdev void internal_add_copy_impl(std::false_type, const Item&) { 16549e08aacStbbdev __TBB_ASSERT(false, "Overloading for r-value reference doesn't work or it's not movable and not copyable object"); 16649e08aacStbbdev } 16749e08aacStbbdev 16849e08aacStbbdev void internal_add_copy(const Item& item) override { 16949e08aacStbbdev internal_add_copy_impl(typename std::is_copy_constructible<Item>::type(), item); 17049e08aacStbbdev } 17149e08aacStbbdev 17249e08aacStbbdev void internal_add_move(Item&& item) override { 17349e08aacStbbdev using feeder_task = feeder_item_task<Body, Item>; 17449e08aacStbbdev small_object_allocator alloc{}; 17549e08aacStbbdev auto task = alloc.new_object<feeder_task>(std::move(item), *this, alloc); 17649e08aacStbbdev 17749e08aacStbbdev my_wait_context.reserve(); 17849e08aacStbbdev spawn(*task, my_execution_context); 17949e08aacStbbdev } 18049e08aacStbbdev public: 181*b15aabb3Stbbdev feeder_impl(const Body& body, wait_context& w_context, task_group_context &context) 182*b15aabb3Stbbdev : my_body(body), 183*b15aabb3Stbbdev my_wait_context(w_context) 18449e08aacStbbdev , my_execution_context(context) 18549e08aacStbbdev {} 18649e08aacStbbdev 18749e08aacStbbdev const Body& my_body; 188*b15aabb3Stbbdev wait_context& my_wait_context; 18949e08aacStbbdev task_group_context& my_execution_context; 19049e08aacStbbdev }; // class feeder_impl 19149e08aacStbbdev 19249e08aacStbbdev /** Execute computation under one element of the range 19349e08aacStbbdev @ingroup algorithms **/ 19449e08aacStbbdev template<typename Iterator, typename Body, typename Item> 19549e08aacStbbdev struct for_each_iteration_task: public task { 19649e08aacStbbdev using feeder_type = feeder_impl<Body, Item>; 19749e08aacStbbdev 198*b15aabb3Stbbdev for_each_iteration_task(Iterator input_item_ptr, const Body& body, feeder_impl<Body, Item>* feeder_ptr, wait_context& wait_context) : 199*b15aabb3Stbbdev item_ptr(input_item_ptr), my_body(body), my_feeder_ptr(feeder_ptr), parent_wait_context(wait_context) 20049e08aacStbbdev {} 20149e08aacStbbdev 20249e08aacStbbdev void finalize() { 20349e08aacStbbdev parent_wait_context.release(); 20449e08aacStbbdev } 20549e08aacStbbdev 20649e08aacStbbdev task* execute(execution_data&) override { 207*b15aabb3Stbbdev parallel_for_each_operator_selector<Body>::call(my_body, *item_ptr, my_feeder_ptr); 20849e08aacStbbdev finalize(); 20949e08aacStbbdev return nullptr; 21049e08aacStbbdev } 21149e08aacStbbdev 21249e08aacStbbdev task* cancel(execution_data&) override { 21349e08aacStbbdev finalize(); 21449e08aacStbbdev return nullptr; 21549e08aacStbbdev } 21649e08aacStbbdev 21749e08aacStbbdev Iterator item_ptr; 218*b15aabb3Stbbdev const Body& my_body; 219*b15aabb3Stbbdev feeder_impl<Body, Item>* my_feeder_ptr; 22049e08aacStbbdev wait_context& parent_wait_context; 221*b15aabb3Stbbdev }; // class for_each_iteration_task 222*b15aabb3Stbbdev 223*b15aabb3Stbbdev // Helper to get the type of the iterator to the internal sequence of copies 224*b15aabb3Stbbdev // If the element can be passed to the body as an rvalue - this iterator should be move_iterator 225*b15aabb3Stbbdev template <typename Body, typename Item, typename = void> 226*b15aabb3Stbbdev struct input_iteration_task_iterator_helper { 227*b15aabb3Stbbdev // For input iterators we pass const lvalue reference to the body 228*b15aabb3Stbbdev // It is prohibited to take non-constant lvalue references for input iterators 229*b15aabb3Stbbdev using type = const Item*; 230*b15aabb3Stbbdev }; 231*b15aabb3Stbbdev 232*b15aabb3Stbbdev template <typename Body, typename Item> 233*b15aabb3Stbbdev struct input_iteration_task_iterator_helper<Body, Item, 234*b15aabb3Stbbdev tbb::detail::void_t<decltype(parallel_for_each_operator_selector<Body>::call(std::declval<const Body&>(), 235*b15aabb3Stbbdev std::declval<Item&&>(), 236*b15aabb3Stbbdev std::declval<feeder_impl<Body, Item>*>()))>> 237*b15aabb3Stbbdev { 238*b15aabb3Stbbdev using type = std::move_iterator<Item*>; 239*b15aabb3Stbbdev }; 24049e08aacStbbdev 24149e08aacStbbdev /** Split one block task to several(max_block_size) iteration tasks for input iterators 24249e08aacStbbdev @ingroup algorithms **/ 24349e08aacStbbdev template <typename Body, typename Item> 24449e08aacStbbdev struct input_block_handling_task : public task { 24549e08aacStbbdev static constexpr size_t max_block_size = 4; 24649e08aacStbbdev 24749e08aacStbbdev using feeder_type = feeder_impl<Body, Item>; 248*b15aabb3Stbbdev using iteration_task_iterator_type = typename input_iteration_task_iterator_helper<Body, Item>::type; 249*b15aabb3Stbbdev using iteration_task = for_each_iteration_task<iteration_task_iterator_type, Body, Item>; 25049e08aacStbbdev 251*b15aabb3Stbbdev input_block_handling_task(wait_context& root_wait_context, task_group_context& e_context, 252*b15aabb3Stbbdev const Body& body, feeder_impl<Body, Item>* feeder_ptr, small_object_allocator& alloc) 253*b15aabb3Stbbdev :my_size(0), my_wait_context(0), my_root_wait_context(root_wait_context), 254*b15aabb3Stbbdev my_execution_context(e_context), my_allocator(alloc) 25549e08aacStbbdev { 256*b15aabb3Stbbdev auto item_it = block_iteration_space.begin(); 25749e08aacStbbdev for (auto* it = task_pool.begin(); it != task_pool.end(); ++it) { 258*b15aabb3Stbbdev new (it) iteration_task(iteration_task_iterator_type(item_it++), body, feeder_ptr, my_wait_context); 25949e08aacStbbdev } 26049e08aacStbbdev } 26149e08aacStbbdev 26249e08aacStbbdev void finalize(const execution_data& ed) { 263*b15aabb3Stbbdev my_root_wait_context.release(); 26449e08aacStbbdev my_allocator.delete_object(this, ed); 26549e08aacStbbdev } 26649e08aacStbbdev 26749e08aacStbbdev task* execute(execution_data& ed) override { 26849e08aacStbbdev __TBB_ASSERT( my_size > 0, "Negative size was passed to task"); 26949e08aacStbbdev for (std::size_t counter = 1; counter < my_size; ++counter) { 27049e08aacStbbdev my_wait_context.reserve(); 271*b15aabb3Stbbdev spawn(*(task_pool.begin() + counter), my_execution_context); 27249e08aacStbbdev } 27349e08aacStbbdev my_wait_context.reserve(); 274*b15aabb3Stbbdev execute_and_wait(*task_pool.begin(), my_execution_context, 275*b15aabb3Stbbdev my_wait_context, my_execution_context); 27649e08aacStbbdev 27749e08aacStbbdev // deallocate current task after children execution 27849e08aacStbbdev finalize(ed); 27949e08aacStbbdev return nullptr; 28049e08aacStbbdev } 28149e08aacStbbdev 28249e08aacStbbdev task* cancel(execution_data& ed) override { 28349e08aacStbbdev finalize(ed); 28449e08aacStbbdev return nullptr; 28549e08aacStbbdev } 28649e08aacStbbdev 28749e08aacStbbdev ~input_block_handling_task() { 28849e08aacStbbdev for(std::size_t counter = 0; counter < max_block_size; ++counter) { 28949e08aacStbbdev (task_pool.begin() + counter)->~iteration_task(); 29049e08aacStbbdev (block_iteration_space.begin() + counter)->~Item(); 29149e08aacStbbdev } 29249e08aacStbbdev } 29349e08aacStbbdev 29449e08aacStbbdev aligned_space<Item, max_block_size> block_iteration_space; 29549e08aacStbbdev aligned_space<iteration_task, max_block_size> task_pool; 29649e08aacStbbdev std::size_t my_size; 29749e08aacStbbdev wait_context my_wait_context; 298*b15aabb3Stbbdev wait_context& my_root_wait_context; 299*b15aabb3Stbbdev task_group_context& my_execution_context; 30049e08aacStbbdev small_object_allocator my_allocator; 301*b15aabb3Stbbdev }; // class input_block_handling_task 30249e08aacStbbdev 30349e08aacStbbdev /** Split one block task to several(max_block_size) iteration tasks for forward iterators 30449e08aacStbbdev @ingroup algorithms **/ 30549e08aacStbbdev template <typename Iterator, typename Body, typename Item> 30649e08aacStbbdev struct forward_block_handling_task : public task { 30749e08aacStbbdev static constexpr size_t max_block_size = 4; 30849e08aacStbbdev 30949e08aacStbbdev using iteration_task = for_each_iteration_task<Iterator, Body, Item>; 31049e08aacStbbdev 311*b15aabb3Stbbdev forward_block_handling_task(Iterator first, std::size_t size, 312*b15aabb3Stbbdev wait_context& w_context, task_group_context& e_context, 313*b15aabb3Stbbdev const Body& body, feeder_impl<Body, Item>* feeder_ptr, 314*b15aabb3Stbbdev small_object_allocator& alloc) 315*b15aabb3Stbbdev : my_size(size), my_wait_context(0), my_root_wait_context(w_context), 316*b15aabb3Stbbdev my_execution_context(e_context), my_allocator(alloc) 31749e08aacStbbdev { 31849e08aacStbbdev auto* task_it = task_pool.begin(); 31949e08aacStbbdev for (std::size_t i = 0; i < size; i++) { 320*b15aabb3Stbbdev new (task_it++) iteration_task(first, body, feeder_ptr, my_wait_context); 32149e08aacStbbdev ++first; 32249e08aacStbbdev } 32349e08aacStbbdev } 32449e08aacStbbdev 32549e08aacStbbdev void finalize(const execution_data& ed) { 326*b15aabb3Stbbdev my_root_wait_context.release(); 32749e08aacStbbdev my_allocator.delete_object(this, ed); 32849e08aacStbbdev } 32949e08aacStbbdev 33049e08aacStbbdev task* execute(execution_data& ed) override { 33149e08aacStbbdev __TBB_ASSERT( my_size > 0, "Negative size was passed to task"); 33249e08aacStbbdev for(std::size_t counter = 1; counter < my_size; ++counter) { 33349e08aacStbbdev my_wait_context.reserve(); 334*b15aabb3Stbbdev spawn(*(task_pool.begin() + counter), my_execution_context); 33549e08aacStbbdev } 33649e08aacStbbdev my_wait_context.reserve(); 337*b15aabb3Stbbdev execute_and_wait(*task_pool.begin(), my_execution_context, 338*b15aabb3Stbbdev my_wait_context, my_execution_context); 33949e08aacStbbdev 34049e08aacStbbdev // deallocate current task after children execution 34149e08aacStbbdev finalize(ed); 34249e08aacStbbdev return nullptr; 34349e08aacStbbdev } 34449e08aacStbbdev 34549e08aacStbbdev task* cancel(execution_data& ed) override { 34649e08aacStbbdev finalize(ed); 34749e08aacStbbdev return nullptr; 34849e08aacStbbdev } 34949e08aacStbbdev 35049e08aacStbbdev ~forward_block_handling_task() { 35149e08aacStbbdev for(std::size_t counter = 0; counter < my_size; ++counter) { 35249e08aacStbbdev (task_pool.begin() + counter)->~iteration_task(); 35349e08aacStbbdev } 35449e08aacStbbdev } 35549e08aacStbbdev 35649e08aacStbbdev aligned_space<iteration_task, max_block_size> task_pool; 35749e08aacStbbdev std::size_t my_size; 35849e08aacStbbdev wait_context my_wait_context; 359*b15aabb3Stbbdev wait_context& my_root_wait_context; 360*b15aabb3Stbbdev task_group_context& my_execution_context; 36149e08aacStbbdev small_object_allocator my_allocator; 36249e08aacStbbdev }; // class forward_block_handling_task 36349e08aacStbbdev 36449e08aacStbbdev /** Body for parallel_for algorithm. 36549e08aacStbbdev * Allows to redirect operations under random access iterators range to the parallel_for algorithm. 36649e08aacStbbdev @ingroup algorithms **/ 36749e08aacStbbdev template <typename Iterator, typename Body, typename Item> 36849e08aacStbbdev class parallel_for_body_wrapper { 36949e08aacStbbdev Iterator my_first; 370*b15aabb3Stbbdev const Body& my_body; 371*b15aabb3Stbbdev feeder_impl<Body, Item>* my_feeder_ptr; 37249e08aacStbbdev public: 373*b15aabb3Stbbdev parallel_for_body_wrapper(Iterator first, const Body& body, feeder_impl<Body, Item>* feeder_ptr) 374*b15aabb3Stbbdev : my_first(first), my_body(body), my_feeder_ptr(feeder_ptr) {} 37549e08aacStbbdev 37649e08aacStbbdev void operator()(tbb::blocked_range<std::size_t> range) const { 37749e08aacStbbdev #if __INTEL_COMPILER 37849e08aacStbbdev #pragma ivdep 37949e08aacStbbdev #endif 38049e08aacStbbdev for (std::size_t count = range.begin(); count != range.end(); count++) { 381*b15aabb3Stbbdev parallel_for_each_operator_selector<Body>::call(my_body, *(my_first + count), 382*b15aabb3Stbbdev my_feeder_ptr); 38349e08aacStbbdev } 38449e08aacStbbdev } 38549e08aacStbbdev }; // class parallel_for_body_wrapper 38649e08aacStbbdev 38749e08aacStbbdev 38849e08aacStbbdev /** Helper for getting iterators tag including inherited custom tags 38949e08aacStbbdev @ingroup algorithms */ 39049e08aacStbbdev template<typename It> 39149e08aacStbbdev using tag = typename std::iterator_traits<It>::iterator_category; 39249e08aacStbbdev 39349e08aacStbbdev template<typename It> 39449e08aacStbbdev using iterator_tag_dispatch = typename 39549e08aacStbbdev std::conditional< 39649e08aacStbbdev std::is_base_of<std::random_access_iterator_tag, tag<It>>::value, 39749e08aacStbbdev std::random_access_iterator_tag, 39849e08aacStbbdev typename std::conditional< 39949e08aacStbbdev std::is_base_of<std::forward_iterator_tag, tag<It>>::value, 40049e08aacStbbdev std::forward_iterator_tag, 40149e08aacStbbdev std::input_iterator_tag 40249e08aacStbbdev >::type 40349e08aacStbbdev >::type; 40449e08aacStbbdev 405*b15aabb3Stbbdev template <typename Body, typename Iterator, typename Item> 406*b15aabb3Stbbdev using feeder_is_required = tbb::detail::void_t<decltype(std::declval<const Body>()(std::declval<typename std::iterator_traits<Iterator>::reference>(), 407*b15aabb3Stbbdev std::declval<feeder<Item>&>()))>; 408*b15aabb3Stbbdev 409*b15aabb3Stbbdev // Creates feeder object only if the body can accept it 410*b15aabb3Stbbdev template <typename Iterator, typename Body, typename Item, typename = void> 411*b15aabb3Stbbdev struct feeder_holder { 412*b15aabb3Stbbdev feeder_holder( wait_context&, task_group_context&, const Body& ) {} 413*b15aabb3Stbbdev 414*b15aabb3Stbbdev feeder_impl<Body, Item>* feeder_ptr() { return nullptr; } 415*b15aabb3Stbbdev }; // class feeder_holder 416*b15aabb3Stbbdev 417*b15aabb3Stbbdev template <typename Iterator, typename Body, typename Item> 418*b15aabb3Stbbdev class feeder_holder<Iterator, Body, Item, feeder_is_required<Body, Iterator, Item>> { 419*b15aabb3Stbbdev public: 420*b15aabb3Stbbdev feeder_holder( wait_context& w_context, task_group_context& context, const Body& body ) 421*b15aabb3Stbbdev : my_feeder(body, w_context, context) {} 422*b15aabb3Stbbdev 423*b15aabb3Stbbdev feeder_impl<Body, Item>* feeder_ptr() { return &my_feeder; } 424*b15aabb3Stbbdev private: 425*b15aabb3Stbbdev feeder_impl<Body, Item> my_feeder; 426*b15aabb3Stbbdev }; // class feeder_holder 427*b15aabb3Stbbdev 428*b15aabb3Stbbdev template <typename Iterator, typename Body, typename Item> 429*b15aabb3Stbbdev class for_each_root_task_base : public task { 430*b15aabb3Stbbdev public: 431*b15aabb3Stbbdev for_each_root_task_base(Iterator first, Iterator last, const Body& body, wait_context& w_context, task_group_context& e_context) 432*b15aabb3Stbbdev : my_first(first), my_last(last), my_wait_context(w_context), my_execution_context(e_context), 433*b15aabb3Stbbdev my_body(body), my_feeder_holder(my_wait_context, my_execution_context, my_body) 434*b15aabb3Stbbdev { 435*b15aabb3Stbbdev my_wait_context.reserve(); 436*b15aabb3Stbbdev } 437*b15aabb3Stbbdev private: 438*b15aabb3Stbbdev task* cancel(execution_data&) override { 439*b15aabb3Stbbdev this->my_wait_context.release(); 440*b15aabb3Stbbdev return nullptr; 441*b15aabb3Stbbdev } 442*b15aabb3Stbbdev protected: 443*b15aabb3Stbbdev Iterator my_first; 444*b15aabb3Stbbdev Iterator my_last; 445*b15aabb3Stbbdev wait_context& my_wait_context; 446*b15aabb3Stbbdev task_group_context& my_execution_context; 447*b15aabb3Stbbdev const Body& my_body; 448*b15aabb3Stbbdev feeder_holder<Iterator, Body, Item> my_feeder_holder; 449*b15aabb3Stbbdev }; // class for_each_root_task_base 450*b15aabb3Stbbdev 45149e08aacStbbdev /** parallel_for_each algorithm root task - most generic version 45249e08aacStbbdev * Splits input range to blocks 45349e08aacStbbdev @ingroup algorithms **/ 45449e08aacStbbdev template <typename Iterator, typename Body, typename Item, typename IteratorTag = iterator_tag_dispatch<Iterator>> 455*b15aabb3Stbbdev class for_each_root_task : public for_each_root_task_base<Iterator, Body, Item> 45649e08aacStbbdev { 457*b15aabb3Stbbdev using base_type = for_each_root_task_base<Iterator, Body, Item>; 458*b15aabb3Stbbdev public: 459*b15aabb3Stbbdev using base_type::base_type; 46049e08aacStbbdev private: 46149e08aacStbbdev task* execute(execution_data& ed) override { 46249e08aacStbbdev using block_handling_type = input_block_handling_task<Body, Item>; 46349e08aacStbbdev 464*b15aabb3Stbbdev if (this->my_first == this->my_last) { 465*b15aabb3Stbbdev this->my_wait_context.release(); 46649e08aacStbbdev return nullptr; 46749e08aacStbbdev } 46849e08aacStbbdev 469*b15aabb3Stbbdev this->my_wait_context.reserve(); 47049e08aacStbbdev small_object_allocator alloc{}; 471*b15aabb3Stbbdev auto block_handling_task = alloc.new_object<block_handling_type>(ed, this->my_wait_context, this->my_execution_context, 472*b15aabb3Stbbdev this->my_body, this->my_feeder_holder.feeder_ptr(), 473*b15aabb3Stbbdev alloc); 47449e08aacStbbdev 47549e08aacStbbdev auto* block_iterator = block_handling_task->block_iteration_space.begin(); 476*b15aabb3Stbbdev for (; !(this->my_first == this->my_last) && block_handling_task->my_size < block_handling_type::max_block_size; ++this->my_first) { 47749e08aacStbbdev // Move semantics are automatically used when supported by the iterator 478*b15aabb3Stbbdev new (block_iterator++) Item(*this->my_first); 47949e08aacStbbdev ++block_handling_task->my_size; 48049e08aacStbbdev } 48149e08aacStbbdev 48249e08aacStbbdev // Do not access this after spawn to avoid races 483*b15aabb3Stbbdev spawn(*this, this->my_execution_context); 48449e08aacStbbdev return block_handling_task; 48549e08aacStbbdev } 48649e08aacStbbdev }; // class for_each_root_task - most generic implementation 48749e08aacStbbdev 48849e08aacStbbdev /** parallel_for_each algorithm root task - forward iterator based specialization 48949e08aacStbbdev * Splits input range to blocks 49049e08aacStbbdev @ingroup algorithms **/ 49149e08aacStbbdev template <typename Iterator, typename Body, typename Item> 492*b15aabb3Stbbdev class for_each_root_task<Iterator, Body, Item, std::forward_iterator_tag> 493*b15aabb3Stbbdev : public for_each_root_task_base<Iterator, Body, Item> 49449e08aacStbbdev { 495*b15aabb3Stbbdev using base_type = for_each_root_task_base<Iterator, Body, Item>; 496*b15aabb3Stbbdev public: 497*b15aabb3Stbbdev using base_type::base_type; 49849e08aacStbbdev private: 49949e08aacStbbdev task* execute(execution_data& ed) override { 50049e08aacStbbdev using block_handling_type = forward_block_handling_task<Iterator, Body, Item>; 501*b15aabb3Stbbdev if (this->my_first == this->my_last) { 502*b15aabb3Stbbdev this->my_wait_context.release(); 50349e08aacStbbdev return nullptr; 50449e08aacStbbdev } 50549e08aacStbbdev 50649e08aacStbbdev std::size_t block_size{0}; 507*b15aabb3Stbbdev Iterator first_block_element = this->my_first; 508*b15aabb3Stbbdev for (; !(this->my_first == this->my_last) && block_size < block_handling_type::max_block_size; ++this->my_first) { 50949e08aacStbbdev ++block_size; 51049e08aacStbbdev } 51149e08aacStbbdev 512*b15aabb3Stbbdev this->my_wait_context.reserve(); 51349e08aacStbbdev small_object_allocator alloc{}; 514*b15aabb3Stbbdev auto block_handling_task = alloc.new_object<block_handling_type>(ed, first_block_element, block_size, 515*b15aabb3Stbbdev this->my_wait_context, this->my_execution_context, 516*b15aabb3Stbbdev this->my_body, this->my_feeder_holder.feeder_ptr(), alloc); 51749e08aacStbbdev 51849e08aacStbbdev // Do not access this after spawn to avoid races 519*b15aabb3Stbbdev spawn(*this, this->my_execution_context); 52049e08aacStbbdev return block_handling_task; 52149e08aacStbbdev } 52249e08aacStbbdev }; // class for_each_root_task - forward iterator based specialization 52349e08aacStbbdev 52449e08aacStbbdev /** parallel_for_each algorithm root task - random access iterator based specialization 52549e08aacStbbdev * Splits input range to blocks 52649e08aacStbbdev @ingroup algorithms **/ 52749e08aacStbbdev template <typename Iterator, typename Body, typename Item> 528*b15aabb3Stbbdev class for_each_root_task<Iterator, Body, Item, std::random_access_iterator_tag> 529*b15aabb3Stbbdev : public for_each_root_task_base<Iterator, Body, Item> 53049e08aacStbbdev { 531*b15aabb3Stbbdev using base_type = for_each_root_task_base<Iterator, Body, Item>; 532*b15aabb3Stbbdev public: 533*b15aabb3Stbbdev using base_type::base_type; 53449e08aacStbbdev private: 53549e08aacStbbdev task* execute(execution_data&) override { 53649e08aacStbbdev tbb::parallel_for( 537*b15aabb3Stbbdev tbb::blocked_range<std::size_t>(0, std::distance(this->my_first, this->my_last)), 538*b15aabb3Stbbdev parallel_for_body_wrapper<Iterator, Body, Item>(this->my_first, this->my_body, this->my_feeder_holder.feeder_ptr()) 539*b15aabb3Stbbdev , this->my_execution_context 54049e08aacStbbdev ); 54149e08aacStbbdev 542*b15aabb3Stbbdev this->my_wait_context.release(); 54349e08aacStbbdev return nullptr; 54449e08aacStbbdev } 54549e08aacStbbdev }; // class for_each_root_task - random access iterator based specialization 54649e08aacStbbdev 54749e08aacStbbdev /** Helper for getting item type. If item type can be deduced from feeder - got it from feeder, 54849e08aacStbbdev if feeder is generic - got item type from range. 54949e08aacStbbdev @ingroup algorithms */ 55049e08aacStbbdev template<typename Body, typename Item, typename FeederArg> 55149e08aacStbbdev auto feeder_argument_parser(void (Body::*)(Item, feeder<FeederArg>&) const) -> FeederArg; 55249e08aacStbbdev 55349e08aacStbbdev template<typename Body, typename> 55449e08aacStbbdev decltype(feeder_argument_parser<Body>(&Body::operator())) get_item_type_impl(int); // for (T, feeder<T>) 55549e08aacStbbdev template<typename Body, typename Item> Item get_item_type_impl(...); // stub 55649e08aacStbbdev 55749e08aacStbbdev template <typename Body, typename Item> 55849e08aacStbbdev using get_item_type = decltype(get_item_type_impl<Body, Item>(0)); 55949e08aacStbbdev 56049e08aacStbbdev /** Implements parallel iteration over a range. 56149e08aacStbbdev @ingroup algorithms */ 56249e08aacStbbdev template<typename Iterator, typename Body> 56349e08aacStbbdev void run_parallel_for_each( Iterator first, Iterator last, const Body& body, task_group_context& context) 56449e08aacStbbdev { 56549e08aacStbbdev if (!(first == last)) { 566*b15aabb3Stbbdev using ItemType = get_item_type<Body, typename std::iterator_traits<Iterator>::value_type>; 567*b15aabb3Stbbdev wait_context w_context(0); 56849e08aacStbbdev 569*b15aabb3Stbbdev for_each_root_task<Iterator, Body, ItemType> root_task(first, last, body, w_context, context); 57049e08aacStbbdev 571*b15aabb3Stbbdev execute_and_wait(root_task, context, w_context, context); 57249e08aacStbbdev } 57349e08aacStbbdev } 57449e08aacStbbdev 57549e08aacStbbdev /** \page parallel_for_each_body_req Requirements on parallel_for_each body 57649e08aacStbbdev Class \c Body implementing the concept of parallel_for_each body must define: 57749e08aacStbbdev - \code 57849e08aacStbbdev B::operator()( 57949e08aacStbbdev cv_item_type item, 58049e08aacStbbdev feeder<item_type>& feeder 58149e08aacStbbdev ) const 58249e08aacStbbdev 58349e08aacStbbdev OR 58449e08aacStbbdev 58549e08aacStbbdev B::operator()( cv_item_type& item ) const 58649e08aacStbbdev \endcode Process item. 58749e08aacStbbdev May be invoked concurrently for the same \c this but different \c item. 58849e08aacStbbdev 58949e08aacStbbdev - \code item_type( const item_type& ) \endcode 59049e08aacStbbdev Copy a work item. 59149e08aacStbbdev - \code ~item_type() \endcode Destroy a work item 59249e08aacStbbdev **/ 59349e08aacStbbdev 59449e08aacStbbdev /** \name parallel_for_each 59549e08aacStbbdev See also requirements on \ref parallel_for_each_body_req "parallel_for_each Body". **/ 59649e08aacStbbdev //@{ 59749e08aacStbbdev //! Parallel iteration over a range, with optional addition of more work. 59849e08aacStbbdev /** @ingroup algorithms */ 59949e08aacStbbdev template<typename Iterator, typename Body> 60049e08aacStbbdev void parallel_for_each(Iterator first, Iterator last, const Body& body) { 60149e08aacStbbdev task_group_context context(PARALLEL_FOR_EACH); 60249e08aacStbbdev run_parallel_for_each<Iterator, Body>(first, last, body, context); 60349e08aacStbbdev } 60449e08aacStbbdev 60549e08aacStbbdev template<typename Range, typename Body> 60649e08aacStbbdev void parallel_for_each(Range& rng, const Body& body) { 60749e08aacStbbdev parallel_for_each(std::begin(rng), std::end(rng), body); 60849e08aacStbbdev } 60949e08aacStbbdev 61049e08aacStbbdev template<typename Range, typename Body> 61149e08aacStbbdev void parallel_for_each(const Range& rng, const Body& body) { 61249e08aacStbbdev parallel_for_each(std::begin(rng), std::end(rng), body); 61349e08aacStbbdev } 61449e08aacStbbdev 61549e08aacStbbdev //! Parallel iteration over a range, with optional addition of more work and user-supplied context 61649e08aacStbbdev /** @ingroup algorithms */ 61749e08aacStbbdev template<typename Iterator, typename Body> 61849e08aacStbbdev void parallel_for_each(Iterator first, Iterator last, const Body& body, task_group_context& context) { 61949e08aacStbbdev run_parallel_for_each<Iterator, Body>(first, last, body, context); 62049e08aacStbbdev } 62149e08aacStbbdev 62249e08aacStbbdev template<typename Range, typename Body> 62349e08aacStbbdev void parallel_for_each(Range& rng, const Body& body, task_group_context& context) { 62449e08aacStbbdev parallel_for_each(std::begin(rng), std::end(rng), body, context); 62549e08aacStbbdev } 62649e08aacStbbdev 62749e08aacStbbdev template<typename Range, typename Body> 62849e08aacStbbdev void parallel_for_each(const Range& rng, const Body& body, task_group_context& context) { 62949e08aacStbbdev parallel_for_each(std::begin(rng), std::end(rng), body, context); 63049e08aacStbbdev } 63149e08aacStbbdev 632*b15aabb3Stbbdev } // namespace d2 63349e08aacStbbdev } // namespace detail 63449e08aacStbbdev //! @endcond 63549e08aacStbbdev //@} 63649e08aacStbbdev 63749e08aacStbbdev inline namespace v1 { 638*b15aabb3Stbbdev using detail::d2::parallel_for_each; 63949e08aacStbbdev using detail::d1::feeder; 64049e08aacStbbdev } // namespace v1 64149e08aacStbbdev 64249e08aacStbbdev } // namespace tbb 64349e08aacStbbdev 64449e08aacStbbdev #endif /* __TBB_parallel_for_each_H */ 645