]> https://gitweb.dealii.org/ - dealii.git/commitdiff
parallel_for() for transform with taskflow 17516/head
authorTimo Heister <timo.heister@gmail.com>
Tue, 13 Aug 2024 21:14:49 +0000 (17:14 -0400)
committerTimo Heister <timo.heister@gmail.com>
Fri, 8 Nov 2024 20:47:46 +0000 (15:47 -0500)
include/deal.II/base/parallel.h
include/deal.II/base/synchronous_iterator.h

index 06f7c511fd95be0061d15ba856c7be21a6f3ec4a..d66abdd01a49ad7a1948e9014d9012a8c20178b4 100644 (file)
 #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>
@@ -73,6 +80,37 @@ namespace parallel
     };
 #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
     /**
@@ -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<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);
@@ -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<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>;
@@ -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<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>;
@@ -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
   }
 
index 71a71813e0b90343e57e4f35c9478c5456031a7c..be7af821b3602840b1037dd72b1c7642dd4f4cce 100644 (file)
@@ -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<I1, I2, I3, I4> &t, const unsigned int n)
   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.
@@ -223,8 +273,6 @@ advance_by_one(std::tuple<I1, I2, I3, I4> &t)
   ++std::get<3>(t);
 }
 
-
-
 /**
  * Advance the elements of this iterator by $n$.
  *
@@ -240,18 +288,44 @@ operator+(const SynchronousIterators<Iterators> &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 <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,

In the beginning the Universe was created. This has made a lot of people very angry and has been widely regarded as a bad move.

Douglas Adams


Typeset in Trocchi and Trocchi Bold Sans Serif.