149e08aacStbbdev /* 2*aa247fa8SIlya Isaev Copyright (c) 2005-2022 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 { 35478de5b1Stbbdev #if __TBB_CPP20_CONCEPTS_PRESENT 36478de5b1Stbbdev namespace d1 { 37478de5b1Stbbdev template <typename Item> 38478de5b1Stbbdev class feeder; 39478de5b1Stbbdev 40478de5b1Stbbdev } // namespace d1 41478de5b1Stbbdev inline namespace d0 { 42478de5b1Stbbdev 43478de5b1Stbbdev template <typename Body, typename ItemType, typename FeederItemType> 44478de5b1Stbbdev concept parallel_for_each_body = requires( const std::remove_reference_t<Body>& body, ItemType&& item ) { 45478de5b1Stbbdev body(std::forward<ItemType>(item)); 46478de5b1Stbbdev } || 47478de5b1Stbbdev requires( const std::remove_reference_t<Body>& body, ItemType&& item, 48478de5b1Stbbdev tbb::detail::d1::feeder<FeederItemType>& feeder ) { 49478de5b1Stbbdev body(std::forward<ItemType>(item), feeder); 50478de5b1Stbbdev }; 51478de5b1Stbbdev 52478de5b1Stbbdev } // namespace d0 53478de5b1Stbbdev #endif // __TBB_CPP20_CONCEPTS_PRESENT 54b15aabb3Stbbdev namespace d2 { 5549e08aacStbbdev template<typename Body, typename Item> class feeder_impl; 56b15aabb3Stbbdev } // namespace d2 5749e08aacStbbdev 58b15aabb3Stbbdev namespace d1 { 5949e08aacStbbdev //! Class the user supplied algorithm body uses to add new tasks 6049e08aacStbbdev template<typename Item> 6149e08aacStbbdev class feeder { 6249e08aacStbbdev feeder() {} 6349e08aacStbbdev feeder(const feeder&) = delete; 6449e08aacStbbdev void operator=( const feeder&) = delete; 6549e08aacStbbdev 6649e08aacStbbdev virtual ~feeder () {} 6749e08aacStbbdev virtual void internal_add_copy(const Item& item) = 0; 6849e08aacStbbdev virtual void internal_add_move(Item&& item) = 0; 6949e08aacStbbdev 70478de5b1Stbbdev template<typename Body_, typename Item_> friend class d2::feeder_impl; 7149e08aacStbbdev public: 7249e08aacStbbdev //! Add a work item to a running parallel_for_each. 7349e08aacStbbdev void add(const Item& item) {internal_add_copy(item);} 7449e08aacStbbdev void add(Item&& item) {internal_add_move(std::move(item));} 7549e08aacStbbdev }; 7649e08aacStbbdev 77b15aabb3Stbbdev } // namespace d1 78b15aabb3Stbbdev 79b15aabb3Stbbdev namespace d2 { 80b15aabb3Stbbdev using namespace tbb::detail::d1; 8149e08aacStbbdev /** Selects one of the two possible forms of function call member operator. 8249e08aacStbbdev @ingroup algorithms **/ 83b15aabb3Stbbdev template<class Body> 84b15aabb3Stbbdev struct parallel_for_each_operator_selector { 8549e08aacStbbdev public: 8649e08aacStbbdev template<typename ItemArg, typename FeederArg> 87b15aabb3Stbbdev static auto call(const Body& body, ItemArg&& item, FeederArg*) 8849e08aacStbbdev -> decltype(body(std::forward<ItemArg>(item)), void()) { 8949e08aacStbbdev #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) 9049e08aacStbbdev // Suppression of Microsoft non-standard extension warnings 9149e08aacStbbdev #pragma warning (push) 9249e08aacStbbdev #pragma warning (disable: 4239) 9349e08aacStbbdev #endif 9449e08aacStbbdev 9549e08aacStbbdev body(std::forward<ItemArg>(item)); 9649e08aacStbbdev 9749e08aacStbbdev #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) 9849e08aacStbbdev #pragma warning (push) 9949e08aacStbbdev #endif 10049e08aacStbbdev } 10149e08aacStbbdev 10249e08aacStbbdev template<typename ItemArg, typename FeederArg> 103b15aabb3Stbbdev static auto call(const Body& body, ItemArg&& item, FeederArg* feeder) 104b15aabb3Stbbdev -> decltype(body(std::forward<ItemArg>(item), *feeder), void()) { 10549e08aacStbbdev #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) 10649e08aacStbbdev // Suppression of Microsoft non-standard extension warnings 10749e08aacStbbdev #pragma warning (push) 10849e08aacStbbdev #pragma warning (disable: 4239) 10949e08aacStbbdev #endif 110b15aabb3Stbbdev __TBB_ASSERT(feeder, "Feeder was not created but should be"); 11149e08aacStbbdev 112b15aabb3Stbbdev body(std::forward<ItemArg>(item), *feeder); 11349e08aacStbbdev 11449e08aacStbbdev #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) 11549e08aacStbbdev #pragma warning (push) 11649e08aacStbbdev #endif 11749e08aacStbbdev } 11849e08aacStbbdev }; 11949e08aacStbbdev 12049e08aacStbbdev template<typename Body, typename Item> 12149e08aacStbbdev struct feeder_item_task: public task { 12249e08aacStbbdev using feeder_type = feeder_impl<Body, Item>; 12349e08aacStbbdev 12449e08aacStbbdev template <typename ItemType> 12549e08aacStbbdev feeder_item_task(ItemType&& input_item, feeder_type& feeder, small_object_allocator& alloc) : 12649e08aacStbbdev item(std::forward<ItemType>(input_item)), 12749e08aacStbbdev my_feeder(feeder), 12849e08aacStbbdev my_allocator(alloc) 12949e08aacStbbdev {} 13049e08aacStbbdev 13149e08aacStbbdev void finalize(const execution_data& ed) { 13249e08aacStbbdev my_feeder.my_wait_context.release(); 13349e08aacStbbdev my_allocator.delete_object(this, ed); 13449e08aacStbbdev } 13549e08aacStbbdev 136b15aabb3Stbbdev //! Hack for resolve ambiguity between calls to the body with and without moving the stored copy 137b15aabb3Stbbdev //! Executing body with moving the copy should have higher priority 138b15aabb3Stbbdev using first_priority = int; 139b15aabb3Stbbdev using second_priority = double; 140b15aabb3Stbbdev 141b15aabb3Stbbdev template <typename BodyType, typename ItemType, typename FeederType> 142b15aabb3Stbbdev static auto call(const BodyType& call_body, ItemType& call_item, FeederType& call_feeder, first_priority) 143b15aabb3Stbbdev -> decltype(parallel_for_each_operator_selector<Body>::call(call_body, std::move(call_item), &call_feeder), void()) 144b15aabb3Stbbdev { 145b15aabb3Stbbdev parallel_for_each_operator_selector<Body>::call(call_body, std::move(call_item), &call_feeder); 146b15aabb3Stbbdev } 147b15aabb3Stbbdev 148b15aabb3Stbbdev template <typename BodyType, typename ItemType, typename FeederType> 149b15aabb3Stbbdev static void call(const BodyType& call_body, ItemType& call_item, FeederType& call_feeder, second_priority) { 150b15aabb3Stbbdev parallel_for_each_operator_selector<Body>::call(call_body, call_item, &call_feeder); 151b15aabb3Stbbdev } 152b15aabb3Stbbdev 15349e08aacStbbdev task* execute(execution_data& ed) override { 154b15aabb3Stbbdev call(my_feeder.my_body, item, my_feeder, first_priority{}); 15549e08aacStbbdev finalize(ed); 15649e08aacStbbdev return nullptr; 15749e08aacStbbdev } 15849e08aacStbbdev 15949e08aacStbbdev task* cancel(execution_data& ed) override { 16049e08aacStbbdev finalize(ed); 16149e08aacStbbdev return nullptr; 16249e08aacStbbdev } 16349e08aacStbbdev 16449e08aacStbbdev Item item; 16549e08aacStbbdev feeder_type& my_feeder; 16649e08aacStbbdev small_object_allocator my_allocator; 16749e08aacStbbdev }; // class feeder_item_task 16849e08aacStbbdev 16949e08aacStbbdev /** Implements new task adding procedure. 17049e08aacStbbdev @ingroup algorithms **/ 171b15aabb3Stbbdev template<typename Body, typename Item> 17249e08aacStbbdev class feeder_impl : public feeder<Item> { 17349e08aacStbbdev // Avoiding use of copy constructor in a virtual method if the type does not support it 17449e08aacStbbdev void internal_add_copy_impl(std::true_type, const Item& item) { 17549e08aacStbbdev using feeder_task = feeder_item_task<Body, Item>; 17649e08aacStbbdev small_object_allocator alloc; 17749e08aacStbbdev auto task = alloc.new_object<feeder_task>(item, *this, alloc); 17849e08aacStbbdev 17949e08aacStbbdev my_wait_context.reserve(); 18049e08aacStbbdev spawn(*task, my_execution_context); 18149e08aacStbbdev } 18249e08aacStbbdev 18349e08aacStbbdev void internal_add_copy_impl(std::false_type, const Item&) { 18449e08aacStbbdev __TBB_ASSERT(false, "Overloading for r-value reference doesn't work or it's not movable and not copyable object"); 18549e08aacStbbdev } 18649e08aacStbbdev 18749e08aacStbbdev void internal_add_copy(const Item& item) override { 18849e08aacStbbdev internal_add_copy_impl(typename std::is_copy_constructible<Item>::type(), item); 18949e08aacStbbdev } 19049e08aacStbbdev 19149e08aacStbbdev void internal_add_move(Item&& item) override { 19249e08aacStbbdev using feeder_task = feeder_item_task<Body, Item>; 19349e08aacStbbdev small_object_allocator alloc{}; 19449e08aacStbbdev auto task = alloc.new_object<feeder_task>(std::move(item), *this, alloc); 19549e08aacStbbdev 19649e08aacStbbdev my_wait_context.reserve(); 19749e08aacStbbdev spawn(*task, my_execution_context); 19849e08aacStbbdev } 19949e08aacStbbdev public: 200b15aabb3Stbbdev feeder_impl(const Body& body, wait_context& w_context, task_group_context &context) 201b15aabb3Stbbdev : my_body(body), 202b15aabb3Stbbdev my_wait_context(w_context) 20349e08aacStbbdev , my_execution_context(context) 20449e08aacStbbdev {} 20549e08aacStbbdev 20649e08aacStbbdev const Body& my_body; 207b15aabb3Stbbdev wait_context& my_wait_context; 20849e08aacStbbdev task_group_context& my_execution_context; 20949e08aacStbbdev }; // class feeder_impl 21049e08aacStbbdev 21149e08aacStbbdev /** Execute computation under one element of the range 21249e08aacStbbdev @ingroup algorithms **/ 21349e08aacStbbdev template<typename Iterator, typename Body, typename Item> 21449e08aacStbbdev struct for_each_iteration_task: public task { 21549e08aacStbbdev using feeder_type = feeder_impl<Body, Item>; 21649e08aacStbbdev 217b15aabb3Stbbdev for_each_iteration_task(Iterator input_item_ptr, const Body& body, feeder_impl<Body, Item>* feeder_ptr, wait_context& wait_context) : 218b15aabb3Stbbdev item_ptr(input_item_ptr), my_body(body), my_feeder_ptr(feeder_ptr), parent_wait_context(wait_context) 21949e08aacStbbdev {} 22049e08aacStbbdev 22149e08aacStbbdev void finalize() { 22249e08aacStbbdev parent_wait_context.release(); 22349e08aacStbbdev } 22449e08aacStbbdev 22549e08aacStbbdev task* execute(execution_data&) override { 226b15aabb3Stbbdev parallel_for_each_operator_selector<Body>::call(my_body, *item_ptr, my_feeder_ptr); 22749e08aacStbbdev finalize(); 22849e08aacStbbdev return nullptr; 22949e08aacStbbdev } 23049e08aacStbbdev 23149e08aacStbbdev task* cancel(execution_data&) override { 23249e08aacStbbdev finalize(); 23349e08aacStbbdev return nullptr; 23449e08aacStbbdev } 23549e08aacStbbdev 23649e08aacStbbdev Iterator item_ptr; 237b15aabb3Stbbdev const Body& my_body; 238b15aabb3Stbbdev feeder_impl<Body, Item>* my_feeder_ptr; 23949e08aacStbbdev wait_context& parent_wait_context; 240b15aabb3Stbbdev }; // class for_each_iteration_task 241b15aabb3Stbbdev 242b15aabb3Stbbdev // Helper to get the type of the iterator to the internal sequence of copies 243b15aabb3Stbbdev // If the element can be passed to the body as an rvalue - this iterator should be move_iterator 244b15aabb3Stbbdev template <typename Body, typename Item, typename = void> 245b15aabb3Stbbdev struct input_iteration_task_iterator_helper { 246b15aabb3Stbbdev // For input iterators we pass const lvalue reference to the body 247b15aabb3Stbbdev // It is prohibited to take non-constant lvalue references for input iterators 248b15aabb3Stbbdev using type = const Item*; 249b15aabb3Stbbdev }; 250b15aabb3Stbbdev 251b15aabb3Stbbdev template <typename Body, typename Item> 252b15aabb3Stbbdev struct input_iteration_task_iterator_helper<Body, Item, 253b15aabb3Stbbdev tbb::detail::void_t<decltype(parallel_for_each_operator_selector<Body>::call(std::declval<const Body&>(), 254b15aabb3Stbbdev std::declval<Item&&>(), 255b15aabb3Stbbdev std::declval<feeder_impl<Body, Item>*>()))>> 256b15aabb3Stbbdev { 257b15aabb3Stbbdev using type = std::move_iterator<Item*>; 258b15aabb3Stbbdev }; 25949e08aacStbbdev 26049e08aacStbbdev /** Split one block task to several(max_block_size) iteration tasks for input iterators 26149e08aacStbbdev @ingroup algorithms **/ 26249e08aacStbbdev template <typename Body, typename Item> 26349e08aacStbbdev struct input_block_handling_task : public task { 26449e08aacStbbdev static constexpr size_t max_block_size = 4; 26549e08aacStbbdev 26649e08aacStbbdev using feeder_type = feeder_impl<Body, Item>; 267b15aabb3Stbbdev using iteration_task_iterator_type = typename input_iteration_task_iterator_helper<Body, Item>::type; 268b15aabb3Stbbdev using iteration_task = for_each_iteration_task<iteration_task_iterator_type, Body, Item>; 26949e08aacStbbdev 270b15aabb3Stbbdev input_block_handling_task(wait_context& root_wait_context, task_group_context& e_context, 271b15aabb3Stbbdev const Body& body, feeder_impl<Body, Item>* feeder_ptr, small_object_allocator& alloc) 272b15aabb3Stbbdev :my_size(0), my_wait_context(0), my_root_wait_context(root_wait_context), 273b15aabb3Stbbdev my_execution_context(e_context), my_allocator(alloc) 27449e08aacStbbdev { 275b15aabb3Stbbdev auto item_it = block_iteration_space.begin(); 27649e08aacStbbdev for (auto* it = task_pool.begin(); it != task_pool.end(); ++it) { 277b15aabb3Stbbdev new (it) iteration_task(iteration_task_iterator_type(item_it++), body, feeder_ptr, my_wait_context); 27849e08aacStbbdev } 27949e08aacStbbdev } 28049e08aacStbbdev 28149e08aacStbbdev void finalize(const execution_data& ed) { 282b15aabb3Stbbdev my_root_wait_context.release(); 28349e08aacStbbdev my_allocator.delete_object(this, ed); 28449e08aacStbbdev } 28549e08aacStbbdev 28649e08aacStbbdev task* execute(execution_data& ed) override { 28749e08aacStbbdev __TBB_ASSERT( my_size > 0, "Negative size was passed to task"); 28849e08aacStbbdev for (std::size_t counter = 1; counter < my_size; ++counter) { 28949e08aacStbbdev my_wait_context.reserve(); 290b15aabb3Stbbdev spawn(*(task_pool.begin() + counter), my_execution_context); 29149e08aacStbbdev } 29249e08aacStbbdev my_wait_context.reserve(); 293b15aabb3Stbbdev execute_and_wait(*task_pool.begin(), my_execution_context, 294b15aabb3Stbbdev my_wait_context, my_execution_context); 29549e08aacStbbdev 29649e08aacStbbdev // deallocate current task after children execution 29749e08aacStbbdev finalize(ed); 29849e08aacStbbdev return nullptr; 29949e08aacStbbdev } 30049e08aacStbbdev 30149e08aacStbbdev task* cancel(execution_data& ed) override { 30249e08aacStbbdev finalize(ed); 30349e08aacStbbdev return nullptr; 30449e08aacStbbdev } 30549e08aacStbbdev 30649e08aacStbbdev ~input_block_handling_task() { 30749e08aacStbbdev for(std::size_t counter = 0; counter < max_block_size; ++counter) { 30849e08aacStbbdev (task_pool.begin() + counter)->~iteration_task(); 309*aa247fa8SIlya Isaev if (counter < my_size) { 31049e08aacStbbdev (block_iteration_space.begin() + counter)->~Item(); 31149e08aacStbbdev } 31249e08aacStbbdev } 313*aa247fa8SIlya Isaev } 31449e08aacStbbdev 31549e08aacStbbdev aligned_space<Item, max_block_size> block_iteration_space; 31649e08aacStbbdev aligned_space<iteration_task, max_block_size> task_pool; 31749e08aacStbbdev std::size_t my_size; 31849e08aacStbbdev wait_context my_wait_context; 319b15aabb3Stbbdev wait_context& my_root_wait_context; 320b15aabb3Stbbdev task_group_context& my_execution_context; 32149e08aacStbbdev small_object_allocator my_allocator; 322b15aabb3Stbbdev }; // class input_block_handling_task 32349e08aacStbbdev 32449e08aacStbbdev /** Split one block task to several(max_block_size) iteration tasks for forward iterators 32549e08aacStbbdev @ingroup algorithms **/ 32649e08aacStbbdev template <typename Iterator, typename Body, typename Item> 32749e08aacStbbdev struct forward_block_handling_task : public task { 32849e08aacStbbdev static constexpr size_t max_block_size = 4; 32949e08aacStbbdev 33049e08aacStbbdev using iteration_task = for_each_iteration_task<Iterator, Body, Item>; 33149e08aacStbbdev 332b15aabb3Stbbdev forward_block_handling_task(Iterator first, std::size_t size, 333b15aabb3Stbbdev wait_context& w_context, task_group_context& e_context, 334b15aabb3Stbbdev const Body& body, feeder_impl<Body, Item>* feeder_ptr, 335b15aabb3Stbbdev small_object_allocator& alloc) 336b15aabb3Stbbdev : my_size(size), my_wait_context(0), my_root_wait_context(w_context), 337b15aabb3Stbbdev my_execution_context(e_context), my_allocator(alloc) 33849e08aacStbbdev { 33949e08aacStbbdev auto* task_it = task_pool.begin(); 34049e08aacStbbdev for (std::size_t i = 0; i < size; i++) { 341b15aabb3Stbbdev new (task_it++) iteration_task(first, body, feeder_ptr, my_wait_context); 34249e08aacStbbdev ++first; 34349e08aacStbbdev } 34449e08aacStbbdev } 34549e08aacStbbdev 34649e08aacStbbdev void finalize(const execution_data& ed) { 347b15aabb3Stbbdev my_root_wait_context.release(); 34849e08aacStbbdev my_allocator.delete_object(this, ed); 34949e08aacStbbdev } 35049e08aacStbbdev 35149e08aacStbbdev task* execute(execution_data& ed) override { 35249e08aacStbbdev __TBB_ASSERT( my_size > 0, "Negative size was passed to task"); 35349e08aacStbbdev for(std::size_t counter = 1; counter < my_size; ++counter) { 35449e08aacStbbdev my_wait_context.reserve(); 355b15aabb3Stbbdev spawn(*(task_pool.begin() + counter), my_execution_context); 35649e08aacStbbdev } 35749e08aacStbbdev my_wait_context.reserve(); 358b15aabb3Stbbdev execute_and_wait(*task_pool.begin(), my_execution_context, 359b15aabb3Stbbdev my_wait_context, my_execution_context); 36049e08aacStbbdev 36149e08aacStbbdev // deallocate current task after children execution 36249e08aacStbbdev finalize(ed); 36349e08aacStbbdev return nullptr; 36449e08aacStbbdev } 36549e08aacStbbdev 36649e08aacStbbdev task* cancel(execution_data& ed) override { 36749e08aacStbbdev finalize(ed); 36849e08aacStbbdev return nullptr; 36949e08aacStbbdev } 37049e08aacStbbdev 37149e08aacStbbdev ~forward_block_handling_task() { 37249e08aacStbbdev for(std::size_t counter = 0; counter < my_size; ++counter) { 37349e08aacStbbdev (task_pool.begin() + counter)->~iteration_task(); 37449e08aacStbbdev } 37549e08aacStbbdev } 37649e08aacStbbdev 37749e08aacStbbdev aligned_space<iteration_task, max_block_size> task_pool; 37849e08aacStbbdev std::size_t my_size; 37949e08aacStbbdev wait_context my_wait_context; 380b15aabb3Stbbdev wait_context& my_root_wait_context; 381b15aabb3Stbbdev task_group_context& my_execution_context; 38249e08aacStbbdev small_object_allocator my_allocator; 38349e08aacStbbdev }; // class forward_block_handling_task 38449e08aacStbbdev 38549e08aacStbbdev /** Body for parallel_for algorithm. 38649e08aacStbbdev * Allows to redirect operations under random access iterators range to the parallel_for algorithm. 38749e08aacStbbdev @ingroup algorithms **/ 38849e08aacStbbdev template <typename Iterator, typename Body, typename Item> 38949e08aacStbbdev class parallel_for_body_wrapper { 39049e08aacStbbdev Iterator my_first; 391b15aabb3Stbbdev const Body& my_body; 392b15aabb3Stbbdev feeder_impl<Body, Item>* my_feeder_ptr; 39349e08aacStbbdev public: 394b15aabb3Stbbdev parallel_for_body_wrapper(Iterator first, const Body& body, feeder_impl<Body, Item>* feeder_ptr) 395b15aabb3Stbbdev : my_first(first), my_body(body), my_feeder_ptr(feeder_ptr) {} 39649e08aacStbbdev 39749e08aacStbbdev void operator()(tbb::blocked_range<std::size_t> range) const { 39849e08aacStbbdev #if __INTEL_COMPILER 39949e08aacStbbdev #pragma ivdep 40049e08aacStbbdev #endif 40149e08aacStbbdev for (std::size_t count = range.begin(); count != range.end(); count++) { 402b15aabb3Stbbdev parallel_for_each_operator_selector<Body>::call(my_body, *(my_first + count), 403b15aabb3Stbbdev my_feeder_ptr); 40449e08aacStbbdev } 40549e08aacStbbdev } 40649e08aacStbbdev }; // class parallel_for_body_wrapper 40749e08aacStbbdev 40849e08aacStbbdev 40949e08aacStbbdev /** Helper for getting iterators tag including inherited custom tags 41049e08aacStbbdev @ingroup algorithms */ 41149e08aacStbbdev template<typename It> 41249e08aacStbbdev using tag = typename std::iterator_traits<It>::iterator_category; 41349e08aacStbbdev 41449e08aacStbbdev template<typename It> 41549e08aacStbbdev using iterator_tag_dispatch = typename 41649e08aacStbbdev std::conditional< 41749e08aacStbbdev std::is_base_of<std::random_access_iterator_tag, tag<It>>::value, 41849e08aacStbbdev std::random_access_iterator_tag, 41949e08aacStbbdev typename std::conditional< 42049e08aacStbbdev std::is_base_of<std::forward_iterator_tag, tag<It>>::value, 42149e08aacStbbdev std::forward_iterator_tag, 42249e08aacStbbdev std::input_iterator_tag 42349e08aacStbbdev >::type 42449e08aacStbbdev >::type; 42549e08aacStbbdev 426b15aabb3Stbbdev template <typename Body, typename Iterator, typename Item> 427b15aabb3Stbbdev using feeder_is_required = tbb::detail::void_t<decltype(std::declval<const Body>()(std::declval<typename std::iterator_traits<Iterator>::reference>(), 428b15aabb3Stbbdev std::declval<feeder<Item>&>()))>; 429b15aabb3Stbbdev 430b15aabb3Stbbdev // Creates feeder object only if the body can accept it 431b15aabb3Stbbdev template <typename Iterator, typename Body, typename Item, typename = void> 432b15aabb3Stbbdev struct feeder_holder { 433b15aabb3Stbbdev feeder_holder( wait_context&, task_group_context&, const Body& ) {} 434b15aabb3Stbbdev 435b15aabb3Stbbdev feeder_impl<Body, Item>* feeder_ptr() { return nullptr; } 436b15aabb3Stbbdev }; // class feeder_holder 437b15aabb3Stbbdev 438b15aabb3Stbbdev template <typename Iterator, typename Body, typename Item> 439b15aabb3Stbbdev class feeder_holder<Iterator, Body, Item, feeder_is_required<Body, Iterator, Item>> { 440b15aabb3Stbbdev public: 441b15aabb3Stbbdev feeder_holder( wait_context& w_context, task_group_context& context, const Body& body ) 442b15aabb3Stbbdev : my_feeder(body, w_context, context) {} 443b15aabb3Stbbdev 444b15aabb3Stbbdev feeder_impl<Body, Item>* feeder_ptr() { return &my_feeder; } 445b15aabb3Stbbdev private: 446b15aabb3Stbbdev feeder_impl<Body, Item> my_feeder; 447b15aabb3Stbbdev }; // class feeder_holder 448b15aabb3Stbbdev 449b15aabb3Stbbdev template <typename Iterator, typename Body, typename Item> 450b15aabb3Stbbdev class for_each_root_task_base : public task { 451b15aabb3Stbbdev public: 452b15aabb3Stbbdev for_each_root_task_base(Iterator first, Iterator last, const Body& body, wait_context& w_context, task_group_context& e_context) 453b15aabb3Stbbdev : my_first(first), my_last(last), my_wait_context(w_context), my_execution_context(e_context), 454b15aabb3Stbbdev my_body(body), my_feeder_holder(my_wait_context, my_execution_context, my_body) 455b15aabb3Stbbdev { 456b15aabb3Stbbdev my_wait_context.reserve(); 457b15aabb3Stbbdev } 458b15aabb3Stbbdev private: 459b15aabb3Stbbdev task* cancel(execution_data&) override { 460b15aabb3Stbbdev this->my_wait_context.release(); 461b15aabb3Stbbdev return nullptr; 462b15aabb3Stbbdev } 463b15aabb3Stbbdev protected: 464b15aabb3Stbbdev Iterator my_first; 465b15aabb3Stbbdev Iterator my_last; 466b15aabb3Stbbdev wait_context& my_wait_context; 467b15aabb3Stbbdev task_group_context& my_execution_context; 468b15aabb3Stbbdev const Body& my_body; 469b15aabb3Stbbdev feeder_holder<Iterator, Body, Item> my_feeder_holder; 470b15aabb3Stbbdev }; // class for_each_root_task_base 471b15aabb3Stbbdev 47249e08aacStbbdev /** parallel_for_each algorithm root task - most generic version 47349e08aacStbbdev * Splits input range to blocks 47449e08aacStbbdev @ingroup algorithms **/ 47549e08aacStbbdev template <typename Iterator, typename Body, typename Item, typename IteratorTag = iterator_tag_dispatch<Iterator>> 476b15aabb3Stbbdev class for_each_root_task : public for_each_root_task_base<Iterator, Body, Item> 47749e08aacStbbdev { 478b15aabb3Stbbdev using base_type = for_each_root_task_base<Iterator, Body, Item>; 479b15aabb3Stbbdev public: 480b15aabb3Stbbdev using base_type::base_type; 48149e08aacStbbdev private: 48249e08aacStbbdev task* execute(execution_data& ed) override { 48349e08aacStbbdev using block_handling_type = input_block_handling_task<Body, Item>; 48449e08aacStbbdev 485b15aabb3Stbbdev if (this->my_first == this->my_last) { 486b15aabb3Stbbdev this->my_wait_context.release(); 48749e08aacStbbdev return nullptr; 48849e08aacStbbdev } 48949e08aacStbbdev 490b15aabb3Stbbdev this->my_wait_context.reserve(); 49149e08aacStbbdev small_object_allocator alloc{}; 492b15aabb3Stbbdev auto block_handling_task = alloc.new_object<block_handling_type>(ed, this->my_wait_context, this->my_execution_context, 493b15aabb3Stbbdev this->my_body, this->my_feeder_holder.feeder_ptr(), 494b15aabb3Stbbdev alloc); 49549e08aacStbbdev 49649e08aacStbbdev auto* block_iterator = block_handling_task->block_iteration_space.begin(); 497b15aabb3Stbbdev for (; !(this->my_first == this->my_last) && block_handling_task->my_size < block_handling_type::max_block_size; ++this->my_first) { 49849e08aacStbbdev // Move semantics are automatically used when supported by the iterator 499b15aabb3Stbbdev new (block_iterator++) Item(*this->my_first); 50049e08aacStbbdev ++block_handling_task->my_size; 50149e08aacStbbdev } 50249e08aacStbbdev 50349e08aacStbbdev // Do not access this after spawn to avoid races 504b15aabb3Stbbdev spawn(*this, this->my_execution_context); 50549e08aacStbbdev return block_handling_task; 50649e08aacStbbdev } 50749e08aacStbbdev }; // class for_each_root_task - most generic implementation 50849e08aacStbbdev 50949e08aacStbbdev /** parallel_for_each algorithm root task - forward iterator based specialization 51049e08aacStbbdev * Splits input range to blocks 51149e08aacStbbdev @ingroup algorithms **/ 51249e08aacStbbdev template <typename Iterator, typename Body, typename Item> 513b15aabb3Stbbdev class for_each_root_task<Iterator, Body, Item, std::forward_iterator_tag> 514b15aabb3Stbbdev : public for_each_root_task_base<Iterator, Body, Item> 51549e08aacStbbdev { 516b15aabb3Stbbdev using base_type = for_each_root_task_base<Iterator, Body, Item>; 517b15aabb3Stbbdev public: 518b15aabb3Stbbdev using base_type::base_type; 51949e08aacStbbdev private: 52049e08aacStbbdev task* execute(execution_data& ed) override { 52149e08aacStbbdev using block_handling_type = forward_block_handling_task<Iterator, Body, Item>; 522b15aabb3Stbbdev if (this->my_first == this->my_last) { 523b15aabb3Stbbdev this->my_wait_context.release(); 52449e08aacStbbdev return nullptr; 52549e08aacStbbdev } 52649e08aacStbbdev 52749e08aacStbbdev std::size_t block_size{0}; 528b15aabb3Stbbdev Iterator first_block_element = this->my_first; 529b15aabb3Stbbdev for (; !(this->my_first == this->my_last) && block_size < block_handling_type::max_block_size; ++this->my_first) { 53049e08aacStbbdev ++block_size; 53149e08aacStbbdev } 53249e08aacStbbdev 533b15aabb3Stbbdev this->my_wait_context.reserve(); 53449e08aacStbbdev small_object_allocator alloc{}; 535b15aabb3Stbbdev auto block_handling_task = alloc.new_object<block_handling_type>(ed, first_block_element, block_size, 536b15aabb3Stbbdev this->my_wait_context, this->my_execution_context, 537b15aabb3Stbbdev this->my_body, this->my_feeder_holder.feeder_ptr(), alloc); 53849e08aacStbbdev 53949e08aacStbbdev // Do not access this after spawn to avoid races 540b15aabb3Stbbdev spawn(*this, this->my_execution_context); 54149e08aacStbbdev return block_handling_task; 54249e08aacStbbdev } 54349e08aacStbbdev }; // class for_each_root_task - forward iterator based specialization 54449e08aacStbbdev 54549e08aacStbbdev /** parallel_for_each algorithm root task - random access iterator based specialization 54649e08aacStbbdev * Splits input range to blocks 54749e08aacStbbdev @ingroup algorithms **/ 54849e08aacStbbdev template <typename Iterator, typename Body, typename Item> 549b15aabb3Stbbdev class for_each_root_task<Iterator, Body, Item, std::random_access_iterator_tag> 550b15aabb3Stbbdev : public for_each_root_task_base<Iterator, Body, Item> 55149e08aacStbbdev { 552b15aabb3Stbbdev using base_type = for_each_root_task_base<Iterator, Body, Item>; 553b15aabb3Stbbdev public: 554b15aabb3Stbbdev using base_type::base_type; 55549e08aacStbbdev private: 55649e08aacStbbdev task* execute(execution_data&) override { 55749e08aacStbbdev tbb::parallel_for( 558b15aabb3Stbbdev tbb::blocked_range<std::size_t>(0, std::distance(this->my_first, this->my_last)), 559b15aabb3Stbbdev parallel_for_body_wrapper<Iterator, Body, Item>(this->my_first, this->my_body, this->my_feeder_holder.feeder_ptr()) 560b15aabb3Stbbdev , this->my_execution_context 56149e08aacStbbdev ); 56249e08aacStbbdev 563b15aabb3Stbbdev this->my_wait_context.release(); 56449e08aacStbbdev return nullptr; 56549e08aacStbbdev } 56649e08aacStbbdev }; // class for_each_root_task - random access iterator based specialization 56749e08aacStbbdev 56849e08aacStbbdev /** Helper for getting item type. If item type can be deduced from feeder - got it from feeder, 56949e08aacStbbdev if feeder is generic - got item type from range. 57049e08aacStbbdev @ingroup algorithms */ 57149e08aacStbbdev template<typename Body, typename Item, typename FeederArg> 57249e08aacStbbdev auto feeder_argument_parser(void (Body::*)(Item, feeder<FeederArg>&) const) -> FeederArg; 57349e08aacStbbdev 57449e08aacStbbdev template<typename Body, typename> 57549e08aacStbbdev decltype(feeder_argument_parser<Body>(&Body::operator())) get_item_type_impl(int); // for (T, feeder<T>) 57649e08aacStbbdev template<typename Body, typename Item> Item get_item_type_impl(...); // stub 57749e08aacStbbdev 57849e08aacStbbdev template <typename Body, typename Item> 57949e08aacStbbdev using get_item_type = decltype(get_item_type_impl<Body, Item>(0)); 58049e08aacStbbdev 581478de5b1Stbbdev #if __TBB_CPP20_CONCEPTS_PRESENT 582478de5b1Stbbdev template <typename Body, typename ItemType> 583478de5b1Stbbdev using feeder_item_type = std::remove_cvref_t<get_item_type<Body, ItemType>>; 584478de5b1Stbbdev 585478de5b1Stbbdev template <typename Body, typename Iterator> 586478de5b1Stbbdev concept parallel_for_each_iterator_body = 587478de5b1Stbbdev parallel_for_each_body<Body, iterator_reference_type<Iterator>, feeder_item_type<Body, iterator_reference_type<Iterator>>>; 588478de5b1Stbbdev 589478de5b1Stbbdev template <typename Body, typename Range> 590478de5b1Stbbdev concept parallel_for_each_range_body = 591478de5b1Stbbdev parallel_for_each_body<Body, range_reference_type<Range>, feeder_item_type<Body, range_reference_type<Range>>>; 592478de5b1Stbbdev #endif 593478de5b1Stbbdev 59449e08aacStbbdev /** Implements parallel iteration over a range. 59549e08aacStbbdev @ingroup algorithms */ 59649e08aacStbbdev template<typename Iterator, typename Body> 59749e08aacStbbdev void run_parallel_for_each( Iterator first, Iterator last, const Body& body, task_group_context& context) 59849e08aacStbbdev { 59949e08aacStbbdev if (!(first == last)) { 600b15aabb3Stbbdev using ItemType = get_item_type<Body, typename std::iterator_traits<Iterator>::value_type>; 601b15aabb3Stbbdev wait_context w_context(0); 60249e08aacStbbdev 603b15aabb3Stbbdev for_each_root_task<Iterator, Body, ItemType> root_task(first, last, body, w_context, context); 60449e08aacStbbdev 605b15aabb3Stbbdev execute_and_wait(root_task, context, w_context, context); 60649e08aacStbbdev } 60749e08aacStbbdev } 60849e08aacStbbdev 60949e08aacStbbdev /** \page parallel_for_each_body_req Requirements on parallel_for_each body 61049e08aacStbbdev Class \c Body implementing the concept of parallel_for_each body must define: 61149e08aacStbbdev - \code 61249e08aacStbbdev B::operator()( 61349e08aacStbbdev cv_item_type item, 61449e08aacStbbdev feeder<item_type>& feeder 61549e08aacStbbdev ) const 61649e08aacStbbdev 61749e08aacStbbdev OR 61849e08aacStbbdev 61949e08aacStbbdev B::operator()( cv_item_type& item ) const 62049e08aacStbbdev \endcode Process item. 62149e08aacStbbdev May be invoked concurrently for the same \c this but different \c item. 62249e08aacStbbdev 62349e08aacStbbdev - \code item_type( const item_type& ) \endcode 62449e08aacStbbdev Copy a work item. 62549e08aacStbbdev - \code ~item_type() \endcode Destroy a work item 62649e08aacStbbdev **/ 62749e08aacStbbdev 62849e08aacStbbdev /** \name parallel_for_each 62949e08aacStbbdev See also requirements on \ref parallel_for_each_body_req "parallel_for_each Body". **/ 63049e08aacStbbdev //@{ 63149e08aacStbbdev //! Parallel iteration over a range, with optional addition of more work. 63249e08aacStbbdev /** @ingroup algorithms */ 63349e08aacStbbdev template<typename Iterator, typename Body> 634478de5b1Stbbdev __TBB_requires(std::input_iterator<Iterator> && parallel_for_each_iterator_body<Body, Iterator>) 63549e08aacStbbdev void parallel_for_each(Iterator first, Iterator last, const Body& body) { 63649e08aacStbbdev task_group_context context(PARALLEL_FOR_EACH); 63749e08aacStbbdev run_parallel_for_each<Iterator, Body>(first, last, body, context); 63849e08aacStbbdev } 63949e08aacStbbdev 64049e08aacStbbdev template<typename Range, typename Body> 641478de5b1Stbbdev __TBB_requires(container_based_sequence<Range, std::input_iterator_tag> && parallel_for_each_range_body<Body, Range>) 64249e08aacStbbdev void parallel_for_each(Range& rng, const Body& body) { 64349e08aacStbbdev parallel_for_each(std::begin(rng), std::end(rng), body); 64449e08aacStbbdev } 64549e08aacStbbdev 64649e08aacStbbdev template<typename Range, typename Body> 647478de5b1Stbbdev __TBB_requires(container_based_sequence<Range, std::input_iterator_tag> && parallel_for_each_range_body<Body, Range>) 64849e08aacStbbdev void parallel_for_each(const Range& rng, const Body& body) { 64949e08aacStbbdev parallel_for_each(std::begin(rng), std::end(rng), body); 65049e08aacStbbdev } 65149e08aacStbbdev 65249e08aacStbbdev //! Parallel iteration over a range, with optional addition of more work and user-supplied context 65349e08aacStbbdev /** @ingroup algorithms */ 65449e08aacStbbdev template<typename Iterator, typename Body> 655478de5b1Stbbdev __TBB_requires(std::input_iterator<Iterator> && parallel_for_each_iterator_body<Body, Iterator>) 65649e08aacStbbdev void parallel_for_each(Iterator first, Iterator last, const Body& body, task_group_context& context) { 65749e08aacStbbdev run_parallel_for_each<Iterator, Body>(first, last, body, context); 65849e08aacStbbdev } 65949e08aacStbbdev 66049e08aacStbbdev template<typename Range, typename Body> 661478de5b1Stbbdev __TBB_requires(container_based_sequence<Range, std::input_iterator_tag> && parallel_for_each_range_body<Body, Range>) 66249e08aacStbbdev void parallel_for_each(Range& rng, const Body& body, task_group_context& context) { 66349e08aacStbbdev parallel_for_each(std::begin(rng), std::end(rng), body, context); 66449e08aacStbbdev } 66549e08aacStbbdev 66649e08aacStbbdev template<typename Range, typename Body> 667478de5b1Stbbdev __TBB_requires(container_based_sequence<Range, std::input_iterator_tag> && parallel_for_each_range_body<Body, Range>) 66849e08aacStbbdev void parallel_for_each(const Range& rng, const Body& body, task_group_context& context) { 66949e08aacStbbdev parallel_for_each(std::begin(rng), std::end(rng), body, context); 67049e08aacStbbdev } 67149e08aacStbbdev 672b15aabb3Stbbdev } // namespace d2 67349e08aacStbbdev } // namespace detail 67449e08aacStbbdev //! @endcond 67549e08aacStbbdev //@} 67649e08aacStbbdev 67749e08aacStbbdev inline namespace v1 { 678b15aabb3Stbbdev using detail::d2::parallel_for_each; 67949e08aacStbbdev using detail::d1::feeder; 68049e08aacStbbdev } // namespace v1 68149e08aacStbbdev 68249e08aacStbbdev } // namespace tbb 68349e08aacStbbdev 68449e08aacStbbdev #endif /* __TBB_parallel_for_each_H */ 685