*/
namespace WorkStream
{
-# ifdef DEAL_II_WITH_TBB
-
+ /**
+ * The nested namespaces contain various implementations of the workstream
+ * algorithms.
+ */
namespace internal
{
+# ifdef DEAL_II_WITH_TBB
/**
* A namespace for the implementation of details of the WorkStream pattern
* and function. This namespace holds classes that deal with the second
* implementation described in the paper by Turcksin, Kronbichler and
* Bangerth (see
* @ref workstream_paper).
+ * Here, no coloring is provided, so copying is done sequentially using a
+ * TBB filter.
*
* Even though this implementation is slower than the third implementation
* discussed in that paper, we need to keep it around for two reasons: (i)
* a user may not give us a graph coloring, (ii) we want to use this
* implementation for colors that are just too small.
*/
- namespace Implementation2
+ namespace tbb_no_coloring
{
/**
* A class that creates a sequence of items from a range of iterators.
* can run in parallel.
*/
template <typename Iterator, typename ScratchData, typename CopyData>
- class Worker : public tbb::filter
+ class TBBWorker : public tbb::filter
{
public:
/**
* operate as well as a pointer to the function that will do the
* assembly.
*/
- Worker(
+ TBBWorker(
const std::function<void(const Iterator &, ScratchData &, CopyData &)>
& worker,
bool copier_exist = true)
* items are copied in the same order in which they are created.
*/
template <typename Iterator, typename ScratchData, typename CopyData>
- class Copier : public tbb::filter
+ class TBBCopier : public tbb::filter
{
public:
/**
* copying from the additional data object to the global matrix or
* similar.
*/
- Copier(const std::function<void(const CopyData &)> &copier)
+ TBBCopier(const std::function<void(const CopyData &)> &copier)
: tbb::filter(/*is_serial=*/true)
, copier(copier)
{}
const std::function<void(const CopyData &)> copier;
};
- } // namespace Implementation2
+ template <typename Worker,
+ typename Copier,
+ typename Iterator,
+ typename ScratchData,
+ typename CopyData>
+ void
+ run(const Iterator & begin,
+ const typename identity<Iterator>::type &end,
+ Worker worker,
+ Copier copier,
+ const ScratchData & sample_scratch_data,
+ const CopyData & sample_copy_data,
+ const unsigned int queue_length,
+ const unsigned int chunk_size)
+ {
+ // create the three stages of the pipeline
+ IteratorRangeToItemStream<Iterator, ScratchData, CopyData>
+ iterator_range_to_item_stream(begin,
+ end,
+ queue_length,
+ chunk_size,
+ sample_scratch_data,
+ sample_copy_data);
+
+ TBBWorker<Iterator, ScratchData, CopyData> worker_filter(worker);
+ TBBCopier<Iterator, ScratchData, CopyData> copier_filter(copier);
+
+ // now create a pipeline from these stages
+ tbb::pipeline assembly_line;
+ assembly_line.add_filter(iterator_range_to_item_stream);
+ assembly_line.add_filter(worker_filter);
+ assembly_line.add_filter(copier_filter);
+
+ // and run it
+ assembly_line.run(queue_length);
+
+ assembly_line.clear();
+ }
+
+ } // namespace tbb_no_coloring
+# endif // DEAL_II_WITH_TBB
+
+
+ /**
+ * A reference implementation without using multithreading to be used if we
+ * don't have multithreading support or if the user requests to run things
+ * sequentially. This is more efficient than using TBB or taskflow if we
+ * only have a single thread.
+ */
+ namespace sequential
+ {
+ /**
+ * Sequential version without colors.
+ */
+ template <typename Worker,
+ typename Copier,
+ typename Iterator,
+ typename ScratchData,
+ typename CopyData>
+ void
+ run(const Iterator & begin,
+ const typename identity<Iterator>::type &end,
+ Worker worker,
+ Copier copier,
+ const ScratchData & sample_scratch_data,
+ const CopyData & sample_copy_data)
+ {
+ // need to copy the sample since it is marked const
+ ScratchData scratch_data = sample_scratch_data;
+ CopyData copy_data = sample_copy_data; // NOLINT
+
+ // Optimization: Check if the functions are not the zero function. To
+ // check zero-ness, create a C++ function out of it:
+ const bool have_worker =
+ (static_cast<const std::function<
+ void(const Iterator &, ScratchData &, CopyData &)> &>(worker)) !=
+ nullptr;
+ const bool have_copier =
+ (static_cast<const std::function<void(const CopyData &)> &>(
+ copier)) != nullptr;
+
+ // Finally loop over all items and perform the necessary work:
+ for (Iterator i = begin; i != end; ++i)
+ {
+ if (have_worker)
+ worker(i, scratch_data, copy_data);
+ if (have_copier)
+ copier(copy_data);
+ }
+ }
+ } // namespace sequential
+
+# ifdef DEAL_II_WITH_TBB
/**
* A namespace for the implementation of details of the WorkStream pattern
* and function. This namespace holds classes that deal with the third
* Bangerth (see
* @ref workstream_paper).
*/
- namespace Implementation3
+ namespace tbb_colored
{
/**
* A structure that contains a pointer to scratch and copy data objects
}
private:
- using ScratchAndCopyDataObjects = typename Implementation3::
+ using ScratchAndCopyDataObjects = typename tbb_colored::
ScratchAndCopyDataObjects<Iterator, ScratchData, CopyData>;
/**
const ScratchData &sample_scratch_data;
const CopyData & sample_copy_data;
};
- } // namespace Implementation3
+ } // namespace tbb_colored
+# endif // DEAL_II_WITH_TBB
+
} // namespace internal
-# endif // DEAL_II_WITH_TBB
-
/**
* This is one of two main functions of the WorkStream concept, doing work
Assert(chunk_size > 0, ExcMessage("The chunk_size must be at least one."));
(void)chunk_size; // removes -Wunused-parameter warning in optimized mode
- // if no work then skip. (only use operator!= for iterators since we may
+ // If no work then skip. (only use operator!= for iterators since we may
// not have an equality comparison operator)
if (!(begin != end))
return;
- // we want to use TBB if we have support and if it is not disabled at
- // runtime:
-# ifdef DEAL_II_WITH_TBB
- if (MultithreadInfo::n_threads() == 1)
-# endif
+ if (MultithreadInfo::n_threads() > 1)
{
- // need to copy the sample since it is marked const
- ScratchData scratch_data = sample_scratch_data;
- CopyData copy_data = sample_copy_data; // NOLINT
-
- for (Iterator i = begin; i != end; ++i)
- {
- // need to check if the function is not the zero function. To
- // check zero-ness, create a C++ function out of it and check that
- if (static_cast<const std::function<
- void(const Iterator &, ScratchData &, CopyData &)> &>(worker))
- worker(i, scratch_data, copy_data);
- if (static_cast<const std::function<void(const CopyData &)> &>(
- copier))
- copier(copy_data);
- }
- }
# ifdef DEAL_II_WITH_TBB
- else // have TBB and use more than one thread
- {
- // Check that the copier exist
if (static_cast<const std::function<void(const CopyData &)> &>(copier))
{
- // create the three stages of the pipeline
- internal::Implementation2::
- IteratorRangeToItemStream<Iterator, ScratchData, CopyData>
- iterator_range_to_item_stream(begin,
- end,
- queue_length,
- chunk_size,
- sample_scratch_data,
- sample_copy_data);
-
- internal::Implementation2::Worker<Iterator, ScratchData, CopyData>
- worker_filter(worker);
- internal::Implementation2::Copier<Iterator, ScratchData, CopyData>
- copier_filter(copier);
-
- // now create a pipeline from these stages
- tbb::pipeline assembly_line;
- assembly_line.add_filter(iterator_range_to_item_stream);
- assembly_line.add_filter(worker_filter);
- assembly_line.add_filter(copier_filter);
-
- // and run it
- assembly_line.run(queue_length);
-
- assembly_line.clear();
+ // If we have a copier, run the algorithm:
+ internal::tbb_no_coloring::run(begin,
+ end,
+ worker,
+ copier,
+ sample_scratch_data,
+ sample_copy_data,
+ queue_length,
+ chunk_size);
}
else
{
- // there is no copier function. in this case, we have an
+ // There is no copier function. in this case, we have an
// embarrassingly parallel problem where we can
// essentially apply parallel_for. because parallel_for
// requires subdividing the range for which operator- is
// iterators to this array of iterators.
//
// instead of duplicating code, this is essentially the
- // same situation we have in Implementation3 below, so we
+ // same situation we have in the colored implementation below, so we
// just defer to that place
std::vector<std::vector<Iterator>> all_iterators(1);
for (Iterator p = begin; p != end; ++p)
queue_length,
chunk_size);
}
- }
+
+ // exit this function to not run the sequential version below:
+ return;
# endif
+ }
+
+ // no TBB installed or we are requested to run sequentially:
+ internal::sequential::run(
+ begin, end, worker, copier, sample_scratch_data, sample_copy_data);
}
}
- // Implementation 3:
+
template <typename Worker,
typename Copier,
typename Iterator,
Assert(chunk_size > 0, ExcMessage("The chunk_size must be at least one."));
(void)chunk_size; // removes -Wunused-parameter warning in optimized mode
- // we want to use TBB if we have support and if it is not disabled at
- // runtime:
-# ifdef DEAL_II_WITH_TBB
- if (MultithreadInfo::n_threads() == 1)
-# endif
- {
- // need to copy the sample since it is marked const
- ScratchData scratch_data = sample_scratch_data;
- CopyData copy_data = sample_copy_data; // NOLINT
- for (unsigned int color = 0; color < colored_iterators.size(); ++color)
- for (typename std::vector<Iterator>::const_iterator p =
- colored_iterators[color].begin();
- p != colored_iterators[color].end();
- ++p)
- {
- // need to check if the function is not the zero function. To
- // check zero-ness, create a C++ function out of it and check that
- if (static_cast<const std::function<void(
- const Iterator &, ScratchData &, CopyData &)> &>(worker))
- worker(*p, scratch_data, copy_data);
- if (static_cast<const std::function<void(const CopyData &)> &>(
- copier))
- copier(copy_data);
- }
- }
-# ifdef DEAL_II_WITH_TBB
- else // have TBB and use more than one thread
+ if (MultithreadInfo::n_threads() > 1)
{
+# ifdef DEAL_II_WITH_TBB
// loop over the various colors of what we're given
for (unsigned int color = 0; color < colored_iterators.size(); ++color)
if (colored_iterators[color].size() > 0)
{
- using WorkerAndCopier = internal::Implementation3::
+ using WorkerAndCopier = internal::tbb_colored::
WorkerAndCopier<Iterator, ScratchData, CopyData>;
using RangeType = typename std::vector<Iterator>::const_iterator;
},
chunk_size);
}
- }
+
+ // exit this function to not run the sequential version below:
+ return;
# endif
+ }
+
+ // run all colors sequentially:
+ {
+ for (unsigned int color = 0; color < colored_iterators.size(); ++color)
+ {
+ internal::sequential::run(*colored_iterators[color].begin(),
+ *colored_iterators[color].end(),
+ worker,
+ copier,
+ sample_scratch_data,
+ sample_copy_data);
+ }
+ }
}