From: Timo Heister Date: Tue, 13 Aug 2024 21:14:49 +0000 (-0400) Subject: parallel_for() for transform with taskflow X-Git-Url: https://gitweb.dealii.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F17516%2Fhead;p=dealii.git parallel_for() for transform with taskflow --- diff --git a/include/deal.II/base/parallel.h b/include/deal.II/base/parallel.h index 06f7c511fd..d66abdd01a 100644 --- a/include/deal.II/base/parallel.h +++ b/include/deal.II/base/parallel.h @@ -28,6 +28,13 @@ #include #include +#ifdef DEAL_II_WITH_TASKFLOW +# include + +# include +# include +#endif + #ifdef DEAL_II_WITH_TBB # include # include @@ -73,6 +80,37 @@ namespace parallel }; #endif +#ifdef DEAL_II_WITH_TASKFLOW + /** + * Internal function to do a parallel_for using taskflow + */ + template + void + taskflow_parallel_for(Iterator x_begin, + Iterator x_end, + const Functor &functor, + const unsigned int grainsize) + { + tf::Executor &executor = MultithreadInfo::get_taskflow_executor(); + tf::Taskflow taskflow; + + // TODO: We have several choices for the Partitioner and we should spend + // some time benchmarking them: + // 1. StaticPartitioner(grainsize): all work items have grainsize number + // of items + // 2. GuidedPartitioner(grainsize): + // "The size of a partition is proportional to the number of unassigned + // iterations divided by the number of workers, and the size will + // gradually decrease to the given chunk size." + // 3. GuidedPartitioner(0): The default. + taskflow.for_each( + x_begin, + x_end, + [&](const auto &item) { functor(item); }, + tf::StaticPartitioner(grainsize)); + executor.run(taskflow).wait(); + } +#endif #ifdef DEAL_II_WITH_TBB /** @@ -168,14 +206,21 @@ namespace parallel const Function &function, const unsigned int grainsize) { -#ifndef DEAL_II_WITH_TBB - // make sure we don't get compiler - // warnings about unused arguments - (void)grainsize; +#ifdef DEAL_II_WITH_TASKFLOW + using Iterators = std::tuple; + using SyncIterators = SynchronousIterators; + Iterators x_begin(begin_in, out); + Iterators x_end(end_in, OutputIterator()); - for (InputIterator in = begin_in; in != end_in;) - *out++ = function(*in++); -#else + internal::taskflow_parallel_for( + SyncIterators(x_begin), + SyncIterators(x_end), + [function](const auto &it) { + *std::get<1>(it) = function(*std::get<0>(it)); + }, + grainsize); + +#elif defined(DEAL_II_WITH_TBB) using Iterators = std::tuple; using SyncIterators = SynchronousIterators; Iterators x_begin(begin_in, out); @@ -188,6 +233,13 @@ namespace parallel *std::get<1>(p) = function(*std::get<0>(p)); }, grainsize); +#else + // make sure we don't get compiler + // warnings about unused arguments + (void)grainsize; + + for (InputIterator in = begin_in; in != end_in;) + *out++ = function(*in++); #endif } @@ -244,14 +296,22 @@ namespace parallel const Function &function, const unsigned int grainsize) { -#ifndef DEAL_II_WITH_TBB - // make sure we don't get compiler - // warnings about unused arguments - (void)grainsize; +#ifdef DEAL_II_WITH_TASKFLOW + using Iterators = + std::tuple; + using SyncIterators = SynchronousIterators; + Iterators x_begin(begin_in1, in2, out); + Iterators x_end(end_in1, InputIterator2(), OutputIterator()); - for (InputIterator1 in1 = begin_in1; in1 != end_in1;) - *out++ = function(*in1++, *in2++); -#else + internal::taskflow_parallel_for( + SyncIterators(x_begin), + SyncIterators(x_end), + [function](const auto &it) { + *std::get<2>(it) = function(*std::get<0>(it), *std::get<1>(it)); + }, + grainsize); + +#elif defined(DEAL_II_WITH_TBB) using Iterators = std::tuple; using SyncIterators = SynchronousIterators; @@ -265,6 +325,14 @@ namespace parallel *std::get<2>(p) = function(*std::get<0>(p), *std::get<1>(p)); }, grainsize); + +#else + // make sure we don't get compiler + // warnings about unused arguments + (void)grainsize; + + for (InputIterator1 in1 = begin_in1; in1 != end_in1;) + *out++ = function(*in1++, *in2++); #endif } @@ -327,14 +395,26 @@ namespace parallel const Function &function, const unsigned int grainsize) { -#ifndef DEAL_II_WITH_TBB - // make sure we don't get compiler - // warnings about unused arguments - (void)grainsize; +#ifdef DEAL_II_WITH_TASKFLOW + using Iterators = std:: + tuple; + using SyncIterators = SynchronousIterators; + Iterators x_begin(begin_in1, in2, in3, out); + Iterators x_end(end_in1, + InputIterator2(), + InputIterator3(), + OutputIterator()); - for (InputIterator1 in1 = begin_in1; in1 != end_in1;) - *out++ = function(*in1++, *in2++, *in3++); -#else + internal::taskflow_parallel_for( + SyncIterators(x_begin), + SyncIterators(x_end), + [function](const auto &it) { + *std::get<3>(it) = + function(*std::get<0>(it), *std::get<1>(it), *std::get<2>(it)); + }, + grainsize); + +#elif defined(DEAL_II_WITH_TBB) using Iterators = std:: tuple; using SyncIterators = SynchronousIterators; @@ -352,6 +432,13 @@ namespace parallel function(*std::get<0>(p), *std::get<1>(p), *std::get<2>(p)); }, grainsize); +#else + // make sure we don't get compiler + // warnings about unused arguments + (void)grainsize; + + for (OutputIterator1 in1 = begin_in1; in1 != end_in1;) + *out++ = function(*in1++, *in2++, *in3++); #endif } diff --git a/include/deal.II/base/synchronous_iterator.h b/include/deal.II/base/synchronous_iterator.h index 71a71813e0..be7af821b3 100644 --- a/include/deal.II/base/synchronous_iterator.h +++ b/include/deal.II/base/synchronous_iterator.h @@ -67,6 +67,16 @@ struct SynchronousIterators Iterators & operator*(); + /** + * The following traits are necessary for std::distance() to + * work with SynchronousIterators. + */ + using difference_type = std::size_t; + using iterator_category = std::bidirectional_iterator_tag; + using value_type = Iterators; + using pointer = Iterators *; + using reference = Iterators &; + private: /** * Storage for the iterators represented by the current class. @@ -179,7 +189,47 @@ advance(std::tuple &t, const unsigned int n) std::advance(std::get<3>(t), n); } +/** + * Reverse a tuple of iterators by 1. + * + * @relatesalso SynchronousIterators + */ +template +inline void +prev(std::tuple &t) +{ + --std::get<0>(t); + --std::get<1>(t); +} +/** + * Reverse a tuple of iterators by 1. + * + * @relatesalso SynchronousIterators + */ +template +inline void +prev(std::tuple &t) +{ + --std::get<0>(t); + --std::get<1>(t); + --std::get<2>(t); +} + +/** + * Reverse a tuple of iterators by 1. + * + * @relatesalso SynchronousIterators + */ +template +inline void +prev(std::tuple &t) +{ + --std::get<0>(t); + --std::get<1>(t); + --std::get<2>(t); + --std::get<3>(t); +} /** * Advance a tuple of iterators by 1. @@ -223,8 +273,6 @@ advance_by_one(std::tuple &t) ++std::get<3>(t); } - - /** * Advance the elements of this iterator by $n$. * @@ -240,18 +288,44 @@ operator+(const SynchronousIterators &a, const std::size_t n) } /** - * Advance the elements of this iterator by 1. + * Advance the elements of this iterator by 1 (pre-increment). * * @relatesalso SynchronousIterators */ template -inline SynchronousIterators +inline SynchronousIterators & operator++(SynchronousIterators &a) { dealii::advance_by_one(*a); return a; } +/** + * Reverse the elements of this iterator by 1 (pre-decrement). + * + * @relatesalso SynchronousIterators + */ +template +inline SynchronousIterators & +operator--(SynchronousIterators &a) +{ + dealii::prev(*a); + return a; +} + +/** + * Advance the elements of this iterator by 1 (post-increment). + * + * @relatesalso SynchronousIterators + */ +template +inline SynchronousIterators +operator++(SynchronousIterators &a, int) +{ + SynchronousIterators b = a; + dealii::advance_by_one(*a); + return b; +} /** * Compare synch iterators for inequality. Since they march in synch,