Reference documentation for deal.II version GIT eebce32b03 2022-06-26 16:25:02+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
work_stream.h
Go to the documentation of this file.
1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 2008 - 2022 by the deal.II authors
4 //
5 // This file is part of the deal.II library.
6 //
7 // The deal.II library is free software; you can use it, redistribute
8 // it, and/or modify it under the terms of the GNU Lesser General
9 // Public License as published by the Free Software Foundation; either
10 // version 2.1 of the License, or (at your option) any later version.
11 // The full text of the license can be found in the file LICENSE.md at
12 // the top level directory of deal.II.
13 //
14 // ---------------------------------------------------------------------
15 
16 #ifndef dealii_work_stream_h
17 # define dealii_work_stream_h
18 
19 
20 # include <deal.II/base/config.h>
21 
25 # include <deal.II/base/parallel.h>
29 
30 # ifdef DEAL_II_WITH_TBB
32 # ifdef DEAL_II_TBB_WITH_ONEAPI
33 # include <tbb/parallel_pipeline.h>
34 # else
35 # include <tbb/pipeline.h>
36 # endif
38 # endif
39 
40 # include <functional>
41 # include <iterator>
42 # include <memory>
43 # include <utility>
44 # include <vector>
45 
47 
48 
49 
162 namespace WorkStream
163 {
168  namespace internal
169  {
170 # ifdef DEAL_II_WITH_TBB
185  namespace tbb_no_coloring
186  {
190  template <typename Iterator, typename ScratchData, typename CopyData>
192  {
193  public:
200  struct ItemType
201  {
208  {
209  std::unique_ptr<ScratchData> scratch_data;
211 
216  : currently_in_use(false)
217  {}
218 
219  ScratchDataObject(ScratchData *p, const bool in_use)
220  : scratch_data(p)
221  , currently_in_use(in_use)
222  {}
223 
224  // Provide a copy constructor that actually doesn't copy the
225  // internal state. This makes handling ScratchAndCopyDataObjects
226  // easier to handle with STL containers.
228  : currently_in_use(false)
229  {}
230 
231  ScratchDataObject(ScratchDataObject &&o) noexcept = default;
232  };
233 
234 
239  using ScratchDataList = std::list<ScratchDataObject>;
240 
245  std::vector<Iterator> iterators;
246 
252  std::vector<CopyData> copy_datas;
253 
259  unsigned int n_iterators;
260 
293 
298  const ScratchData *sample_scratch_data;
299 
305 
306 
312  : n_iterators(0)
313  , scratch_data(nullptr)
314  , sample_scratch_data(nullptr)
315  , currently_in_use(false)
316  {}
317  };
318 
319 
326  const Iterator & end,
327  const unsigned int buffer_size,
328  const unsigned int chunk_size,
329  const ScratchData &sample_scratch_data,
330  const CopyData & sample_copy_data)
332  , item_buffer(buffer_size)
335  {
336  // initialize the elements of the ring buffer
337  for (auto &item : item_buffer)
338  {
339  Assert(item.n_iterators == 0, ExcInternalError());
340 
341  item.iterators.resize(chunk_size,
342  remaining_iterator_range.second);
343  item.scratch_data = &thread_local_scratch;
344  item.sample_scratch_data = &sample_scratch_data;
345  item.copy_datas.resize(chunk_size, sample_copy_data);
346  item.currently_in_use = false;
347  }
348  }
349 
350 
354  ItemType *
356  {
357  // find first unused item. we know that there must be one
358  // because we have set the maximal number of tokens in flight
359  // and have set the ring buffer to have exactly this size. so
360  // if this function is called, we know that less than the
361  // maximal number of items in currently in flight
362  //
363  // note that we need not lock access to this array since
364  // the current stage is run sequentially and we can therefore
365  // enter the following block only once at any given time.
366  // thus, there can be no race condition between checking that
367  // a flag is false and setting it to true. (there may be
368  // another thread where we release items and set 'false'
369  // flags to 'true', but that too does not produce any
370  // problems)
371  ItemType *current_item = nullptr;
372  for (unsigned int i = 0; i < item_buffer.size(); ++i)
373  if (item_buffer[i].currently_in_use == false)
374  {
375  item_buffer[i].currently_in_use = true;
376  current_item = &item_buffer[i];
377  break;
378  }
379  Assert(current_item != nullptr,
380  ExcMessage("This can't be. There must be a free item!"));
381 
382  // initialize the next item. it may
383  // consist of at most chunk_size
384  // elements
385  current_item->n_iterators = 0;
386  while ((remaining_iterator_range.first !=
387  remaining_iterator_range.second) &&
388  (current_item->n_iterators < chunk_size))
389  {
390  current_item->iterators[current_item->n_iterators] =
392 
393  ++remaining_iterator_range.first;
394  ++current_item->n_iterators;
395  }
396 
397  if (current_item->n_iterators == 0)
398  // there were no items
399  // left. terminate the pipeline
400  return nullptr;
401  else
402  return current_item;
403  }
404 
405  private:
410  std::pair<Iterator, Iterator> remaining_iterator_range;
411 
415  std::vector<ItemType> item_buffer;
416 
449 
455  const ScratchData &sample_scratch_data;
456 
463  const unsigned int chunk_size;
464  };
465 
466 
467 
468  template <typename Worker,
469  typename Copier,
470  typename Iterator,
471  typename ScratchData,
472  typename CopyData>
473  void
474  run(const Iterator & begin,
475  const typename identity<Iterator>::type &end,
476  Worker worker,
477  Copier copier,
478  const ScratchData & sample_scratch_data,
479  const CopyData & sample_copy_data,
480  const unsigned int queue_length,
481  const unsigned int chunk_size)
482  {
483  using ItemType = typename IteratorRangeToItemStream<Iterator,
484  ScratchData,
485  CopyData>::ItemType;
486 
487  // Create the three stages of the pipeline:
488 
489  //
490  // ----- Stage 1 -----
491  //
492  // The first stage is the one that provides us with chunks of data
493  // to work on (the stream of "items"). This stage runs sequentially.
495  iterator_range_to_item_stream(begin,
496  end,
497  queue_length,
498  chunk_size,
499  sample_scratch_data,
500  sample_copy_data);
501  auto tbb_item_stream_filter = tbb::make_filter<void, ItemType *>(
502 # ifdef DEAL_II_TBB_WITH_ONEAPI
503  tbb::filter_mode::serial_in_order,
504 # else
506 # endif
507  [&](tbb::flow_control &fc) -> ItemType * {
508  if (const auto item = iterator_range_to_item_stream.get_item())
509  return item;
510  else
511  {
512  fc.stop();
513  return nullptr;
514  }
515  });
516 
517  //
518  // ----- Stage 2 -----
519  //
520  // The second stage is the one that does the actual work. This is the
521  // stage that runs in parallel
522  auto tbb_worker_filter = tbb::make_filter<ItemType *, ItemType *>(
523 # ifdef DEAL_II_TBB_WITH_ONEAPI
524  tbb::filter_mode::parallel,
525 # else
526  tbb::filter::parallel,
527 # endif
528  [worker =
529  std::function<void(const Iterator &, ScratchData &, CopyData &)>(
530  worker),
531  copier_exists =
532  static_cast<bool>(std::function<void(const CopyData &)>(copier))](
533  ItemType *current_item) {
534  // we need to find an unused scratch data object in the list that
535  // corresponds to the current thread and then mark it as used. if
536  // we can't find one, create one
537  //
538  // as discussed in the discussion of the documentation of the
539  // IteratorRangeToItemStream::scratch_data variable, there is no
540  // need to synchronize access to this variable using a mutex
541  // as long as we have no yield-point in between. this means that
542  // we can't take an iterator into the list now and expect it to
543  // still be valid after calling the worker, but we at least do
544  // not have to lock the following section
545  ScratchData *scratch_data = nullptr;
546  {
547  // see if there is an unused object. if so, grab it and mark
548  // it as used
549  for (auto &p : current_item->scratch_data->get())
550  if (p.currently_in_use == false)
551  {
552  scratch_data = p.scratch_data.get();
553  p.currently_in_use = true;
554 
555  break;
556  }
557 
558  // if no object was found, create one and mark it as used
559  if (scratch_data == nullptr)
560  {
561  scratch_data =
562  new ScratchData(*current_item->sample_scratch_data);
563  current_item->scratch_data->get().emplace_back(scratch_data,
564  true);
565  }
566  }
567 
568  // then call the worker function on each element of the chunk we
569  // were given. since these worker functions are called on separate
570  // threads, nothing good can happen if they throw an exception and
571  // we are best off catching it and showing an error message
572  for (unsigned int i = 0; i < current_item->n_iterators; ++i)
573  {
574  try
575  {
576  if (worker)
577  worker(current_item->iterators[i],
578  *scratch_data,
579  current_item->copy_datas[i]);
580  }
581  catch (const std::exception &exc)
582  {
584  }
585  catch (...)
586  {
588  }
589  }
590 
591  // finally mark the scratch object as unused again. as above, there
592  // is no need to lock anything here since the object we work on
593  // is thread-local
594  for (auto &p : current_item->scratch_data->get())
595  if (p.scratch_data.get() == scratch_data)
596  {
597  Assert(p.currently_in_use == true, ExcInternalError());
598  p.currently_in_use = false;
599 
600  break;
601  }
602 
603  // if there is no copier, mark current item as usable again
604  if (copier_exists == false)
605  current_item->currently_in_use = false;
606 
607 
608  // Then return the original pointer
609  // to the now modified object. The copier will work on it next.
610  return current_item;
611  });
612 
613 
614  //
615  // ----- Stage 3 -----
616  //
617  // The last stage is the one that copies data from the CopyData objects
618  // to the final destination. This stage runs sequentially again.
619  auto tbb_copier_filter = tbb::make_filter<ItemType *, void>(
620 # ifdef DEAL_II_TBB_WITH_ONEAPI
621  tbb::filter_mode::serial_in_order,
622 # else
624 # endif
625  [copier = std::function<void(const CopyData &)>(copier)](
626  ItemType *current_item) {
627  if (copier)
628  {
629  // Initiate copying data. For the same reasons as in the worker
630  // class above, catch exceptions rather than letting them
631  // propagate into unknown territories:
632  for (unsigned int i = 0; i < current_item->n_iterators; ++i)
633  {
634  try
635  {
636  copier(current_item->copy_datas[i]);
637  }
638  catch (const std::exception &exc)
639  {
641  }
642  catch (...)
643  {
645  }
646  }
647  }
648  // mark current item as usable again
649  current_item->currently_in_use = false;
650  });
651 
652 
653  //
654  // ----- The pipeline -----
655  //
656  // Now create a pipeline from these stages and execute it:
657  tbb::parallel_pipeline(queue_length,
658  tbb_item_stream_filter & tbb_worker_filter &
659  tbb_copier_filter);
660  }
661 
662  } // namespace tbb_no_coloring
663 # endif // DEAL_II_WITH_TBB
664 
665 
672  namespace sequential
673  {
677  template <typename Worker,
678  typename Copier,
679  typename Iterator,
680  typename ScratchData,
681  typename CopyData>
682  void
683  run(const Iterator & begin,
684  const typename identity<Iterator>::type &end,
685  Worker worker,
686  Copier copier,
687  const ScratchData & sample_scratch_data,
688  const CopyData & sample_copy_data)
689  {
690  // need to copy the sample since it is marked const
691  ScratchData scratch_data = sample_scratch_data;
692  CopyData copy_data = sample_copy_data; // NOLINT
693 
694  // Optimization: Check if the functions are not the zero function. To
695  // check zero-ness, create a C++ function out of it:
696  const bool have_worker =
697  (static_cast<const std::function<
698  void(const Iterator &, ScratchData &, CopyData &)> &>(worker)) !=
699  nullptr;
700  const bool have_copier =
701  (static_cast<const std::function<void(const CopyData &)> &>(
702  copier)) != nullptr;
703 
704  // Finally loop over all items and perform the necessary work:
705  for (Iterator i = begin; i != end; ++i)
706  {
707  if (have_worker)
708  worker(i, scratch_data, copy_data);
709  if (have_copier)
710  copier(copy_data);
711  }
712  }
713 
714 
715 
719  template <typename Worker,
720  typename Copier,
721  typename Iterator,
722  typename ScratchData,
723  typename CopyData>
724  void
725  run(const std::vector<std::vector<Iterator>> &colored_iterators,
726  Worker worker,
727  Copier copier,
728  const ScratchData & sample_scratch_data,
729  const CopyData & sample_copy_data)
730  {
731  // need to copy the sample since it is marked const
732  ScratchData scratch_data = sample_scratch_data;
733  CopyData copy_data = sample_copy_data; // NOLINT
734 
735  // Optimization: Check if the functions are not the zero function. To
736  // check zero-ness, create a C++ function out of it:
737  const bool have_worker =
738  (static_cast<const std::function<
739  void(const Iterator &, ScratchData &, CopyData &)> &>(worker)) !=
740  nullptr;
741  const bool have_copier =
742  (static_cast<const std::function<void(const CopyData &)> &>(
743  copier)) != nullptr;
744 
745  // Finally loop over all items and perform the necessary work:
746  for (unsigned int color = 0; color < colored_iterators.size(); ++color)
747  if (colored_iterators[color].size() > 0)
748  for (auto &it : colored_iterators[color])
749  {
750  if (have_worker)
751  worker(it, scratch_data, copy_data);
752  if (have_copier)
753  copier(copy_data);
754  }
755  }
756 
757  } // namespace sequential
758 
759 
760 
761 # ifdef DEAL_II_WITH_TBB
769  namespace tbb_colored
770  {
776  template <typename Iterator, typename ScratchData, typename CopyData>
778  {
779  std::unique_ptr<ScratchData> scratch_data;
780  std::unique_ptr<CopyData> copy_data;
782 
787  : currently_in_use(false)
788  {}
789 
790  ScratchAndCopyDataObjects(std::unique_ptr<ScratchData> &&p,
791  std::unique_ptr<CopyData> && q,
792  const bool in_use)
793  : scratch_data(std::move(p))
794  , copy_data(std::move(q))
795  , currently_in_use(in_use)
796  {}
797 
798  // Provide a copy constructor that actually doesn't copy the
799  // internal state. This makes handling ScratchAndCopyDataObjects
800  // easier to handle with STL containers.
802  : currently_in_use(false)
803  {}
804  };
805 
806 
807 
813  template <typename Iterator, typename ScratchData, typename CopyData>
815  {
816  public:
821  const std::function<void(const Iterator &, ScratchData &, CopyData &)>
822  & worker,
823  const std::function<void(const CopyData &)> &copier,
824  const ScratchData & sample_scratch_data,
825  const CopyData & sample_copy_data)
826  : worker(worker)
827  , copier(copier)
830  {}
831 
832 
837  void
838  operator()(const tbb::blocked_range<
839  typename std::vector<Iterator>::const_iterator> &range)
840  {
841  // we need to find an unused scratch and corresponding copy
842  // data object in the list that corresponds to the current
843  // thread and then mark it as used. If we can't find one,
844  // create one as discussed in the discussion of the documentation
845  // of the IteratorRangeToItemStream::scratch_data variable,
846  // there is no need to synchronize access to this variable
847  // using a mutex as long as we have no yield-point in between.
848  // This means that we can't take an iterator into the list
849  // now and expect it to still be valid after calling the worker,
850  // but we at least do not have to lock the following section.
851  ScratchData *scratch_data = nullptr;
852  CopyData * copy_data = nullptr;
853  {
854  ScratchAndCopyDataList &scratch_and_copy_data_list = data.get();
855 
856  // see if there is an unused object. if so, grab it and mark
857  // it as used
858  for (typename ScratchAndCopyDataList::iterator p =
859  scratch_and_copy_data_list.begin();
860  p != scratch_and_copy_data_list.end();
861  ++p)
862  if (p->currently_in_use == false)
863  {
864  scratch_data = p->scratch_data.get();
865  copy_data = p->copy_data.get();
866  p->currently_in_use = true;
867  break;
868  }
869 
870  // if no element in the list was found, create one and mark it as
871  // used
872  if (scratch_data == nullptr)
873  {
874  Assert(copy_data == nullptr, ExcInternalError());
875 
876  scratch_and_copy_data_list.emplace_back(
877  std::make_unique<ScratchData>(sample_scratch_data),
878  std::make_unique<CopyData>(sample_copy_data),
879  true);
880  scratch_data =
881  scratch_and_copy_data_list.back().scratch_data.get();
882  copy_data = scratch_and_copy_data_list.back().copy_data.get();
883  }
884  }
885 
886  // then call the worker and copier functions on each
887  // element of the chunk we were given.
888  for (typename std::vector<Iterator>::const_iterator p = range.begin();
889  p != range.end();
890  ++p)
891  {
892  try
893  {
894  if (worker)
895  worker(*p, *scratch_data, *copy_data);
896  if (copier)
897  copier(*copy_data);
898  }
899  catch (const std::exception &exc)
900  {
902  }
903  catch (...)
904  {
906  }
907  }
908 
909  // finally mark the scratch object as unused again. as above, there
910  // is no need to lock anything here since the object we work on
911  // is thread-local
912  {
913  ScratchAndCopyDataList &scratch_and_copy_data_list = data.get();
914 
915  for (typename ScratchAndCopyDataList::iterator p =
916  scratch_and_copy_data_list.begin();
917  p != scratch_and_copy_data_list.end();
918  ++p)
919  if (p->scratch_data.get() == scratch_data)
920  {
921  Assert(p->currently_in_use == true, ExcInternalError());
922  p->currently_in_use = false;
923  }
924  }
925  }
926 
927  private:
928  using ScratchAndCopyDataObjects = typename tbb_colored::
929  ScratchAndCopyDataObjects<Iterator, ScratchData, CopyData>;
930 
935  using ScratchAndCopyDataList = std::list<ScratchAndCopyDataObjects>;
936 
938 
943  const std::function<void(const Iterator &, ScratchData &, CopyData &)>
945 
950  const std::function<void(const CopyData &)> copier;
951 
955  const ScratchData &sample_scratch_data;
956  const CopyData & sample_copy_data;
957  };
958 
962  template <typename Worker,
963  typename Copier,
964  typename Iterator,
965  typename ScratchData,
966  typename CopyData>
967  void
968  run(const std::vector<std::vector<Iterator>> &colored_iterators,
969  Worker worker,
970  Copier copier,
971  const ScratchData & sample_scratch_data,
972  const CopyData & sample_copy_data,
973  const unsigned int chunk_size)
974  {
975  // loop over the various colors of what we're given
976  for (unsigned int color = 0; color < colored_iterators.size(); ++color)
977  if (colored_iterators[color].size() > 0)
978  {
979  using WorkerAndCopier = internal::tbb_colored::
980  WorkerAndCopier<Iterator, ScratchData, CopyData>;
981 
982  using RangeType = typename std::vector<Iterator>::const_iterator;
983 
984  WorkerAndCopier worker_and_copier(worker,
985  copier,
986  sample_scratch_data,
987  sample_copy_data);
988 
990  colored_iterators[color].begin(),
991  colored_iterators[color].end(),
992  [&worker_and_copier](
993  const tbb::blocked_range<
994  typename std::vector<Iterator>::const_iterator> &range) {
995  worker_and_copier(range);
996  },
997  chunk_size);
998  }
999  }
1000 
1001  } // namespace tbb_colored
1002 # endif // DEAL_II_WITH_TBB
1003 
1004 
1005  } // namespace internal
1006 
1007 
1008 
1056  template <typename Worker,
1057  typename Copier,
1058  typename Iterator,
1059  typename ScratchData,
1060  typename CopyData>
1061  void
1062  run(const std::vector<std::vector<Iterator>> &colored_iterators,
1063  Worker worker,
1064  Copier copier,
1065  const ScratchData & sample_scratch_data,
1066  const CopyData & sample_copy_data,
1067  const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1068  const unsigned int chunk_size = 8);
1069 
1070 
1120  template <typename Worker,
1121  typename Copier,
1122  typename Iterator,
1123  typename ScratchData,
1124  typename CopyData>
1125  void
1126  run(const Iterator & begin,
1127  const typename identity<Iterator>::type &end,
1128  Worker worker,
1129  Copier copier,
1130  const ScratchData & sample_scratch_data,
1131  const CopyData & sample_copy_data,
1132  const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1133  const unsigned int chunk_size = 8)
1134  {
1135  Assert(queue_length > 0,
1136  ExcMessage("The queue length must be at least one, and preferably "
1137  "larger than the number of processors on this system."));
1138  (void)queue_length; // removes -Wunused-parameter warning in optimized mode
1139  Assert(chunk_size > 0, ExcMessage("The chunk_size must be at least one."));
1140  (void)chunk_size; // removes -Wunused-parameter warning in optimized mode
1141 
1142  // If no work then skip. (only use operator!= for iterators since we may
1143  // not have an equality comparison operator)
1144  if (!(begin != end))
1145  return;
1146 
1147  if (MultithreadInfo::n_threads() > 1)
1148  {
1149 # ifdef DEAL_II_WITH_TBB
1150  if (static_cast<const std::function<void(const CopyData &)> &>(copier))
1151  {
1152  // If we have a copier, run the algorithm:
1154  end,
1155  worker,
1156  copier,
1157  sample_scratch_data,
1158  sample_copy_data,
1159  queue_length,
1160  chunk_size);
1161  }
1162  else
1163  {
1164  // There is no copier function. in this case, we have an
1165  // embarrassingly parallel problem where we can
1166  // essentially apply parallel_for. because parallel_for
1167  // requires subdividing the range for which operator- is
1168  // necessary between iterators, it is often inefficient to
1169  // apply it directly to cell ranges and similar iterator
1170  // types for which operator- is expensive or, in fact,
1171  // nonexistent. rather, in that case, we simply copy the
1172  // iterators into a large array and use operator- on
1173  // iterators to this array of iterators.
1174  //
1175  // instead of duplicating code, this is essentially the
1176  // same situation we have in the colored implementation below, so we
1177  // just defer to that place
1178  std::vector<std::vector<Iterator>> all_iterators(1);
1179  for (Iterator p = begin; p != end; ++p)
1180  all_iterators[0].push_back(p);
1181 
1182  run(all_iterators,
1183  worker,
1184  copier,
1185  sample_scratch_data,
1186  sample_copy_data,
1187  queue_length,
1188  chunk_size);
1189  }
1190 
1191  // exit this function to not run the sequential version below:
1192  return;
1193 # endif
1194  }
1195 
1196  // no TBB installed or we are requested to run sequentially:
1198  begin, end, worker, copier, sample_scratch_data, sample_copy_data);
1199  }
1200 
1201 
1202 
1210  template <typename Worker,
1211  typename Copier,
1212  typename IteratorRangeType,
1213  typename ScratchData,
1214  typename CopyData,
1215  typename = typename std::enable_if<
1216  has_begin_and_end<IteratorRangeType>>::type>
1217  void
1218  run(IteratorRangeType iterator_range,
1219  Worker worker,
1220  Copier copier,
1221  const ScratchData &sample_scratch_data,
1222  const CopyData & sample_copy_data,
1223  const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1224  const unsigned int chunk_size = 8)
1225  {
1226  // Call the function above
1227  run(iterator_range.begin(),
1228  iterator_range.end(),
1229  worker,
1230  copier,
1231  sample_scratch_data,
1232  sample_copy_data,
1233  queue_length,
1234  chunk_size);
1235  }
1236 
1237 
1238 
1242  template <typename Worker,
1243  typename Copier,
1244  typename Iterator,
1245  typename ScratchData,
1246  typename CopyData>
1247  void
1248  run(const IteratorRange<Iterator> &iterator_range,
1249  Worker worker,
1250  Copier copier,
1251  const ScratchData & sample_scratch_data,
1252  const CopyData & sample_copy_data,
1253  const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1254  const unsigned int chunk_size = 8)
1255  {
1256  // Call the function above
1257  run(iterator_range.begin(),
1258  iterator_range.end(),
1259  worker,
1260  copier,
1261  sample_scratch_data,
1262  sample_copy_data,
1263  queue_length,
1264  chunk_size);
1265  }
1266 
1267 
1268 
1269  template <typename Worker,
1270  typename Copier,
1271  typename Iterator,
1272  typename ScratchData,
1273  typename CopyData>
1274  void
1275  run(const std::vector<std::vector<Iterator>> &colored_iterators,
1276  Worker worker,
1277  Copier copier,
1278  const ScratchData & sample_scratch_data,
1279  const CopyData & sample_copy_data,
1280  const unsigned int queue_length,
1281  const unsigned int chunk_size)
1282  {
1283  Assert(queue_length > 0,
1284  ExcMessage("The queue length must be at least one, and preferably "
1285  "larger than the number of processors on this system."));
1286  (void)queue_length; // removes -Wunused-parameter warning in optimized mode
1287  Assert(chunk_size > 0, ExcMessage("The chunk_size must be at least one."));
1288  (void)chunk_size; // removes -Wunused-parameter warning in optimized mode
1289 
1290 
1291  if (MultithreadInfo::n_threads() > 1)
1292  {
1293 # ifdef DEAL_II_WITH_TBB
1294  internal::tbb_colored::run(colored_iterators,
1295  worker,
1296  copier,
1297  sample_scratch_data,
1298  sample_copy_data,
1299  chunk_size);
1300 
1301  // exit this function to not run the sequential version below:
1302  return;
1303 # endif
1304  }
1305 
1306  // run all colors sequentially:
1307  {
1308  internal::sequential::run(colored_iterators,
1309  worker,
1310  copier,
1311  sample_scratch_data,
1312  sample_copy_data);
1313  }
1314  }
1315 
1316 
1317 
1359  template <typename MainClass,
1360  typename Iterator,
1361  typename ScratchData,
1362  typename CopyData>
1363  void
1364  run(const Iterator & begin,
1365  const typename identity<Iterator>::type &end,
1366  MainClass & main_object,
1367  void (MainClass::*worker)(const Iterator &, ScratchData &, CopyData &),
1368  void (MainClass::*copier)(const CopyData &),
1369  const ScratchData &sample_scratch_data,
1370  const CopyData & sample_copy_data,
1371  const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1372  const unsigned int chunk_size = 8)
1373  {
1374  // forward to the other function
1375  run(
1376  begin,
1377  end,
1378  [&main_object, worker](const Iterator &iterator,
1379  ScratchData & scratch_data,
1380  CopyData & copy_data) {
1381  (main_object.*worker)(iterator, scratch_data, copy_data);
1382  },
1383  [&main_object, copier](const CopyData &copy_data) {
1384  (main_object.*copier)(copy_data);
1385  },
1386  sample_scratch_data,
1387  sample_copy_data,
1388  queue_length,
1389  chunk_size);
1390  }
1391 
1392 
1393  template <typename MainClass,
1394  typename Iterator,
1395  typename ScratchData,
1396  typename CopyData>
1397  void
1400  MainClass &main_object,
1401  void (MainClass::*worker)(const Iterator &, ScratchData &, CopyData &),
1402  void (MainClass::*copier)(const CopyData &),
1403  const ScratchData &sample_scratch_data,
1404  const CopyData & sample_copy_data,
1405  const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1406  const unsigned int chunk_size = 8)
1407  {
1408  // forward to the other function
1409  run(
1410  begin,
1411  end,
1412  [&main_object, worker](const Iterator &iterator,
1413  ScratchData & scratch_data,
1414  CopyData & copy_data) {
1415  (main_object.*worker)(iterator, scratch_data, copy_data);
1416  },
1417  [&main_object, copier](const CopyData &copy_data) {
1418  (main_object.*copier)(copy_data);
1419  },
1420  sample_scratch_data,
1421  sample_copy_data,
1422  queue_length,
1423  chunk_size);
1424  }
1425 
1426 
1427 
1435  template <typename MainClass,
1436  typename IteratorRangeType,
1437  typename ScratchData,
1438  typename CopyData,
1439  typename = typename std::enable_if<
1440  has_begin_and_end<IteratorRangeType>>::type>
1441  void
1442  run(IteratorRangeType iterator_range,
1443  MainClass & main_object,
1444  void (MainClass::*worker)(
1446  ScratchData &,
1447  CopyData &),
1448  void (MainClass::*copier)(const CopyData &),
1449  const ScratchData &sample_scratch_data,
1450  const CopyData & sample_copy_data,
1451  const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1452  const unsigned int chunk_size = 8)
1453  {
1454  // Call the function above
1455  run(std::begin(iterator_range),
1456  std::end(iterator_range),
1457  main_object,
1458  worker,
1459  copier,
1460  sample_scratch_data,
1461  sample_copy_data,
1462  queue_length,
1463  chunk_size);
1464  }
1465 
1466 
1467 
1471  template <typename MainClass,
1472  typename Iterator,
1473  typename ScratchData,
1474  typename CopyData>
1475  void
1477  MainClass & main_object,
1478  void (MainClass::*worker)(const Iterator &, ScratchData &, CopyData &),
1479  void (MainClass::*copier)(const CopyData &),
1480  const ScratchData &sample_scratch_data,
1481  const CopyData & sample_copy_data,
1482  const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1483  const unsigned int chunk_size = 8)
1484  {
1485  // Call the function above
1486  run(std::begin(iterator_range),
1487  std::end(iterator_range),
1488  main_object,
1489  worker,
1490  copier,
1491  sample_scratch_data,
1492  sample_copy_data,
1493  queue_length,
1494  chunk_size);
1495  }
1496 
1497 } // namespace WorkStream
1498 
1499 
1500 
1502 
1503 
1504 
1505 //---------------------------- work_stream.h ---------------------------
1506 // end of #ifndef dealii_work_stream_h
1507 #endif
1508 //---------------------------- work_stream.h ---------------------------
IteratorOverIterators end() const
IteratorOverIterators begin()
static unsigned int n_threads()
std::list< ScratchAndCopyDataObjects > ScratchAndCopyDataList
Definition: work_stream.h:935
WorkerAndCopier(const std::function< void(const Iterator &, ScratchData &, CopyData &)> &worker, const std::function< void(const CopyData &)> &copier, const ScratchData &sample_scratch_data, const CopyData &sample_copy_data)
Definition: work_stream.h:820
Threads::ThreadLocalStorage< ScratchAndCopyDataList > data
Definition: work_stream.h:937
void operator()(const tbb::blocked_range< typename std::vector< Iterator >::const_iterator > &range)
Definition: work_stream.h:838
const std::function< void(const Iterator &, ScratchData &, CopyData &)> worker
Definition: work_stream.h:944
typename tbb_colored::ScratchAndCopyDataObjects< Iterator, ScratchData, CopyData > ScratchAndCopyDataObjects
Definition: work_stream.h:929
const std::function< void(const CopyData &)> copier
Definition: work_stream.h:950
IteratorRangeToItemStream(const Iterator &begin, const Iterator &end, const unsigned int buffer_size, const unsigned int chunk_size, const ScratchData &sample_scratch_data, const CopyData &sample_copy_data)
Definition: work_stream.h:325
Threads::ThreadLocalStorage< typename ItemType::ScratchDataList > thread_local_scratch
Definition: work_stream.h:448
#define DEAL_II_NAMESPACE_OPEN
Definition: config.h:442
#define DEAL_II_DISABLE_EXTRA_DIAGNOSTICS
Definition: config.h:456
#define DEAL_II_NAMESPACE_CLOSE
Definition: config.h:443
#define DEAL_II_ENABLE_EXTRA_DIAGNOSTICS
Definition: config.h:495
static ::ExceptionBase & ExcInternalError()
#define Assert(cond, exc)
Definition: exceptions.h:1473
static ::ExceptionBase & ExcMessage(std::string arg1)
constexpr int chunk_size
Definition: cuda_size.h:35
void handle_std_exception(const std::exception &exc)
VectorType::value_type * begin(VectorType &V)
VectorType::value_type * end(VectorType &V)
std::vector< unsigned int > serial(const std::vector< unsigned int > &targets, const std::function< RequestType(const unsigned int)> &create_request, const std::function< AnswerType(const unsigned int, const RequestType &)> &answer_request, const std::function< void(const unsigned int, const AnswerType &)> &process_answer, const MPI_Comm &comm)
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)
Definition: work_stream.h:683
void run(const std::vector< std::vector< Iterator >> &colored_iterators, Worker worker, Copier copier, const ScratchData &sample_scratch_data, const CopyData &sample_copy_data, const unsigned int chunk_size)
Definition: work_stream.h:968
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)
Definition: work_stream.h:474
void run(const std::vector< std::vector< Iterator >> &colored_iterators, Worker worker, Copier copier, const ScratchData &sample_scratch_data, const CopyData &sample_copy_data, const unsigned int queue_length=2 *MultithreadInfo::n_threads(), const unsigned int chunk_size=8)
Definition: work_stream.h:1275
void parallel_for(Iterator x_begin, Iterator x_end, const Functor &functor, const unsigned int grainsize)
Definition: parallel.h:80
ScratchAndCopyDataObjects(const ScratchAndCopyDataObjects &)
Definition: work_stream.h:801
ScratchAndCopyDataObjects(std::unique_ptr< ScratchData > &&p, std::unique_ptr< CopyData > &&q, const bool in_use)
Definition: work_stream.h:790
Threads::ThreadLocalStorage< ScratchDataList > * scratch_data
Definition: work_stream.h:292