From 755f64f375ee15c125d7b8abcb099f2e9350e2b7 Mon Sep 17 00:00:00 2001 From: Timo Heister Date: Tue, 7 Jul 2020 13:51:39 -0400 Subject: [PATCH] reshuffle namespaces in workstream class No big change in functionality, but needed for the transition to taskflow: - extract the TBB implementation into a separate namespace - extract the sequential version - sequential version caches empty worker/copier check - add a few comments part of #10391 --- include/deal.II/base/work_stream.h | 248 ++++++++++++++++++----------- 1 file changed, 152 insertions(+), 96 deletions(-) diff --git a/include/deal.II/base/work_stream.h b/include/deal.II/base/work_stream.h index 4293573566..e83099da19 100644 --- a/include/deal.II/base/work_stream.h +++ b/include/deal.II/base/work_stream.h @@ -155,23 +155,28 @@ DEAL_II_NAMESPACE_OPEN */ 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. @@ -463,7 +468,7 @@ namespace WorkStream * can run in parallel. */ template - class Worker : public tbb::filter + class TBBWorker : public tbb::filter { public: /** @@ -471,7 +476,7 @@ namespace WorkStream * operate as well as a pointer to the function that will do the * assembly. */ - Worker( + TBBWorker( const std::function & worker, bool copier_exist = true) @@ -611,7 +616,7 @@ namespace WorkStream * items are copied in the same order in which they are created. */ template - class Copier : public tbb::filter + class TBBCopier : public tbb::filter { public: /** @@ -620,7 +625,7 @@ namespace WorkStream * copying from the additional data object to the global matrix or * similar. */ - Copier(const std::function &copier) + TBBCopier(const std::function &copier) : tbb::filter(/*is_serial=*/true) , copier(copier) {} @@ -677,9 +682,101 @@ namespace WorkStream const std::function copier; }; - } // namespace Implementation2 + template + void + run(const Iterator & begin, + const typename identity::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_range_to_item_stream(begin, + end, + queue_length, + chunk_size, + sample_scratch_data, + sample_copy_data); + + TBBWorker worker_filter(worker); + TBBCopier 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 + void + run(const Iterator & begin, + const typename identity::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 &>(worker)) != + nullptr; + const bool have_copier = + (static_cast &>( + 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 @@ -687,7 +784,7 @@ namespace WorkStream * Bangerth (see * @ref workstream_paper). */ - namespace Implementation3 + namespace tbb_colored { /** * A structure that contains a pointer to scratch and copy data objects @@ -844,7 +941,7 @@ namespace WorkStream } private: - using ScratchAndCopyDataObjects = typename Implementation3:: + using ScratchAndCopyDataObjects = typename tbb_colored:: ScratchAndCopyDataObjects; /** @@ -874,13 +971,13 @@ namespace WorkStream 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 @@ -1015,68 +1112,29 @@ namespace WorkStream 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 &>(worker)) - worker(i, scratch_data, copy_data); - if (static_cast &>( - 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 &>(copier)) { - // create the three stages of the pipeline - internal::Implementation2:: - IteratorRangeToItemStream - iterator_range_to_item_stream(begin, - end, - queue_length, - chunk_size, - sample_scratch_data, - sample_copy_data); - - internal::Implementation2::Worker - worker_filter(worker); - internal::Implementation2::Copier - 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 @@ -1088,7 +1146,7 @@ namespace WorkStream // 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> all_iterators(1); for (Iterator p = begin; p != end; ++p) @@ -1102,8 +1160,15 @@ namespace WorkStream 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); } @@ -1173,7 +1238,7 @@ namespace WorkStream } - // Implementation 3: + template 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::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 &>(worker)) - worker(*p, scratch_data, copy_data); - if (static_cast &>( - 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; using RangeType = typename std::vector::const_iterator; @@ -1248,8 +1288,24 @@ namespace WorkStream }, 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); + } + } } -- 2.39.5