#include <memory>
#include <tuple>
+#ifdef DEAL_II_WITH_TASKFLOW
+# include <deal.II/base/multithread_info.h>
+
+# include <taskflow/algorithm/for_each.hpp>
+# include <taskflow/taskflow.hpp>
+#endif
+
#ifdef DEAL_II_WITH_TBB
# include <tbb/blocked_range.h>
# include <tbb/parallel_for.h>
};
#endif
+#ifdef DEAL_II_WITH_TASKFLOW
+ /**
+ * Internal function to do a parallel_for using taskflow
+ */
+ template <typename Iterator, typename Functor>
+ 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
/**
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<InputIterator, OutputIterator>;
+ using SyncIterators = SynchronousIterators<Iterators>;
+ 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<InputIterator, OutputIterator>;
using SyncIterators = SynchronousIterators<Iterators>;
Iterators x_begin(begin_in, out);
*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
}
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<InputIterator1, InputIterator2, OutputIterator>;
+ using SyncIterators = SynchronousIterators<Iterators>;
+ 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<InputIterator1, InputIterator2, OutputIterator>;
using SyncIterators = SynchronousIterators<Iterators>;
*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
}
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<InputIterator1, InputIterator2, InputIterator3, OutputIterator>;
+ using SyncIterators = SynchronousIterators<Iterators>;
+ 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<InputIterator1, InputIterator2, InputIterator3, OutputIterator>;
using SyncIterators = SynchronousIterators<Iterators>;
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
}
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.
std::advance(std::get<3>(t), n);
}
+/**
+ * Reverse a tuple of iterators by 1.
+ *
+ * @relatesalso SynchronousIterators
+ */
+template <typename I1, typename I2>
+inline void
+prev(std::tuple<I1, I2> &t)
+{
+ --std::get<0>(t);
+ --std::get<1>(t);
+}
+/**
+ * Reverse a tuple of iterators by 1.
+ *
+ * @relatesalso SynchronousIterators
+ */
+template <typename I1, typename I2, typename I3>
+inline void
+prev(std::tuple<I1, I2, I3> &t)
+{
+ --std::get<0>(t);
+ --std::get<1>(t);
+ --std::get<2>(t);
+}
+
+/**
+ * Reverse a tuple of iterators by 1.
+ *
+ * @relatesalso SynchronousIterators
+ */
+template <typename I1, typename I2, typename I3, typename I4>
+inline void
+prev(std::tuple<I1, I2, I3, I4> &t)
+{
+ --std::get<0>(t);
+ --std::get<1>(t);
+ --std::get<2>(t);
+ --std::get<3>(t);
+}
/**
* Advance a tuple of iterators by 1.
++std::get<3>(t);
}
-
-
/**
* Advance the elements of this iterator by $n$.
*
}
/**
- * Advance the elements of this iterator by 1.
+ * Advance the elements of this iterator by 1 (pre-increment).
*
* @relatesalso SynchronousIterators
*/
template <typename Iterators>
-inline SynchronousIterators<Iterators>
+inline SynchronousIterators<Iterators> &
operator++(SynchronousIterators<Iterators> &a)
{
dealii::advance_by_one(*a);
return a;
}
+/**
+ * Reverse the elements of this iterator by 1 (pre-decrement).
+ *
+ * @relatesalso SynchronousIterators
+ */
+template <typename Iterators>
+inline SynchronousIterators<Iterators> &
+operator--(SynchronousIterators<Iterators> &a)
+{
+ dealii::prev(*a);
+ return a;
+}
+
+/**
+ * Advance the elements of this iterator by 1 (post-increment).
+ *
+ * @relatesalso SynchronousIterators
+ */
+template <typename Iterators>
+inline SynchronousIterators<Iterators>
+operator++(SynchronousIterators<Iterators> &a, int)
+{
+ SynchronousIterators<Iterators> b = a;
+ dealii::advance_by_one(*a);
+ return b;
+}
/**
* Compare synch iterators for inequality. Since they march in synch,