Reference documentation for deal.II version Git 1dc1051882 2021-04-22 23:57:03 +0200
\(\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 - 2020 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
31 # include <tbb/pipeline.h>
32 # endif
33 
34 # include <functional>
35 # include <iterator>
36 # include <memory>
37 # include <utility>
38 # include <vector>
39 
41 
42 
43 
156 namespace WorkStream
157 {
162  namespace internal
163  {
164 # ifdef DEAL_II_WITH_TBB
165 
179  namespace tbb_no_coloring
180  {
184  template <typename Iterator, typename ScratchData, typename CopyData>
185  class IteratorRangeToItemStream : public tbb::filter
186  {
187  public:
194  struct ItemType
195  {
202  {
203  std::unique_ptr<ScratchData> scratch_data;
205 
210  : currently_in_use(false)
211  {}
212 
213  ScratchDataObject(ScratchData *p, const bool in_use)
214  : scratch_data(p)
215  , currently_in_use(in_use)
216  {}
217 
218  // Provide a copy constructor that actually doesn't copy the
219  // internal state. This makes handling ScratchAndCopyDataObjects
220  // easier to handle with STL containers.
222  : currently_in_use(false)
223  {}
224 
225  ScratchDataObject(ScratchDataObject &&o) noexcept = default;
226  };
227 
228 
233  using ScratchDataList = std::list<ScratchDataObject>;
234 
239  std::vector<Iterator> work_items;
240 
246  std::vector<CopyData> copy_datas;
247 
253  unsigned int n_items;
254 
287 
292  const ScratchData *sample_scratch_data;
293 
299 
300 
306  : n_items(0)
307  , scratch_data(nullptr)
308  , sample_scratch_data(nullptr)
309  , currently_in_use(false)
310  {}
311  };
312 
313 
320  const Iterator & end,
321  const unsigned int buffer_size,
322  const unsigned int chunk_size,
323  const ScratchData &sample_scratch_data,
324  const CopyData & sample_copy_data)
325  : tbb::filter(/*is_serial=*/true)
326  , remaining_iterator_range(begin, end)
327  , item_buffer(buffer_size)
328  , sample_scratch_data(sample_scratch_data)
329  , chunk_size(chunk_size)
330  {
331  // initialize the elements of the ring buffer
332  for (unsigned int element = 0; element < item_buffer.size();
333  ++element)
334  {
335  Assert(item_buffer[element].n_items == 0, ExcInternalError());
336 
337  item_buffer[element].work_items.resize(
338  chunk_size, remaining_iterator_range.second);
339  item_buffer[element].scratch_data = &thread_local_scratch;
340  item_buffer[element].sample_scratch_data = &sample_scratch_data;
341  item_buffer[element].copy_datas.resize(chunk_size,
342  sample_copy_data);
343  item_buffer[element].currently_in_use = false;
344  }
345  }
346 
347 
351  virtual void *
352  operator()(void *) override
353  {
354  // find first unused item. we know that there must be one
355  // because we have set the maximal number of tokens in flight
356  // and have set the ring buffer to have exactly this size. so
357  // if this function is called, we know that less than the
358  // maximal number of items in currently in flight
359  //
360  // note that we need not lock access to this array since
361  // the current stage is run sequentially and we can therefore
362  // enter the following block only once at any given time.
363  // thus, there can be no race condition between checking that
364  // a flag is false and setting it to true. (there may be
365  // another thread where we release items and set 'false'
366  // flags to 'true', but that too does not produce any
367  // problems)
368  ItemType *current_item = nullptr;
369  for (unsigned int i = 0; i < item_buffer.size(); ++i)
370  if (item_buffer[i].currently_in_use == false)
371  {
372  item_buffer[i].currently_in_use = true;
373  current_item = &item_buffer[i];
374  break;
375  }
376  Assert(current_item != nullptr,
377  ExcMessage("This can't be. There must be a free item!"));
378 
379  // initialize the next item. it may
380  // consist of at most chunk_size
381  // elements
382  current_item->n_items = 0;
383  while ((remaining_iterator_range.first !=
384  remaining_iterator_range.second) &&
385  (current_item->n_items < chunk_size))
386  {
387  current_item->work_items[current_item->n_items] =
389 
390  ++remaining_iterator_range.first;
391  ++current_item->n_items;
392  }
393 
394  if (current_item->n_items == 0)
395  // there were no items
396  // left. terminate the pipeline
397  return nullptr;
398  else
399  return current_item;
400  }
401 
402  private:
407  std::pair<Iterator, Iterator> remaining_iterator_range;
408 
412  std::vector<ItemType> item_buffer;
413 
446 
452  const ScratchData &sample_scratch_data;
453 
460  const unsigned int chunk_size;
461  };
462 
463 
464 
470  template <typename Iterator, typename ScratchData, typename CopyData>
471  class TBBWorker : public tbb::filter
472  {
473  public:
480  const std::function<void(const Iterator &, ScratchData &, CopyData &)>
481  & worker,
482  bool copier_exist = true)
483  : tbb::filter(/* is_serial= */ false)
484  , worker(worker)
485  , copier_exist(copier_exist)
486  {}
487 
488 
492  void *
493  operator()(void *item) override
494  {
495  // first unpack the current item
496  using ItemType =
497  typename IteratorRangeToItemStream<Iterator,
498  ScratchData,
499  CopyData>::ItemType;
500 
501  ItemType *current_item = static_cast<ItemType *>(item);
502 
503  // we need to find an unused scratch data object in the list that
504  // corresponds to the current thread and then mark it as used. if
505  // we can't find one, create one
506  //
507  // as discussed in the discussion of the documentation of the
508  // IteratorRangeToItemStream::scratch_data variable, there is no
509  // need to synchronize access to this variable using a mutex
510  // as long as we have no yield-point in between. this means that
511  // we can't take an iterator into the list now and expect it to
512  // still be valid after calling the worker, but we at least do
513  // not have to lock the following section
514  ScratchData *scratch_data = nullptr;
515  {
516  typename ItemType::ScratchDataList &scratch_data_list =
517  current_item->scratch_data->get();
518 
519  // see if there is an unused object. if so, grab it and mark
520  // it as used
521  for (typename ItemType::ScratchDataList::iterator p =
522  scratch_data_list.begin();
523  p != scratch_data_list.end();
524  ++p)
525  if (p->currently_in_use == false)
526  {
527  scratch_data = p->scratch_data.get();
528  p->currently_in_use = true;
529  break;
530  }
531 
532  // if no object was found, create one and mark it as used
533  if (scratch_data == nullptr)
534  {
535  scratch_data =
536  new ScratchData(*current_item->sample_scratch_data);
537 
538  typename ItemType::ScratchDataList::value_type
539  new_scratch_object(scratch_data, true);
540  scratch_data_list.push_back(std::move(new_scratch_object));
541  }
542  }
543 
544  // then call the worker function on each element of the chunk we were
545  // given. since these worker functions are called on separate threads,
546  // nothing good can happen if they throw an exception and we are best
547  // off catching it and showing an error message
548  for (unsigned int i = 0; i < current_item->n_items; ++i)
549  {
550  try
551  {
552  if (worker)
553  worker(current_item->work_items[i],
554  *scratch_data,
555  current_item->copy_datas[i]);
556  }
557  catch (const std::exception &exc)
558  {
560  }
561  catch (...)
562  {
564  }
565  }
566 
567  // finally mark the scratch object as unused again. as above, there
568  // is no need to lock anything here since the object we work on
569  // is thread-local
570  {
571  typename ItemType::ScratchDataList &scratch_data_list =
572  current_item->scratch_data->get();
573 
574  for (typename ItemType::ScratchDataList::iterator p =
575  scratch_data_list.begin();
576  p != scratch_data_list.end();
577  ++p)
578  if (p->scratch_data.get() == scratch_data)
579  {
580  Assert(p->currently_in_use == true, ExcInternalError());
581  p->currently_in_use = false;
582  }
583  }
584 
585  // if there is no copier, mark current item as usable again
586  if (copier_exist == false)
587  current_item->currently_in_use = false;
588 
589 
590  // then return the original pointer
591  // to the now modified object
592  return item;
593  }
594 
595 
596  private:
601  const std::function<void(const Iterator &, ScratchData &, CopyData &)>
603 
609  };
610 
611 
612 
618  template <typename Iterator, typename ScratchData, typename CopyData>
619  class TBBCopier : public tbb::filter
620  {
621  public:
628  TBBCopier(const std::function<void(const CopyData &)> &copier)
629  : tbb::filter(/*is_serial=*/true)
630  , copier(copier)
631  {}
632 
633 
637  void *
638  operator()(void *item) override
639  {
640  // first unpack the current item
641  using ItemType =
642  typename IteratorRangeToItemStream<Iterator,
643  ScratchData,
644  CopyData>::ItemType;
645 
646  ItemType *current_item = static_cast<ItemType *>(item);
647 
648  // initiate copying data. for the same reasons as in the worker class
649  // above, catch exceptions rather than letting it propagate into
650  // unknown territories
651  for (unsigned int i = 0; i < current_item->n_items; ++i)
652  {
653  try
654  {
655  if (copier)
656  copier(current_item->copy_datas[i]);
657  }
658  catch (const std::exception &exc)
659  {
661  }
662  catch (...)
663  {
665  }
666  }
667 
668  // mark current item as usable again
669  current_item->currently_in_use = false;
670 
671 
672  // return an invalid item since we are at the end of the
673  // pipeline
674  return nullptr;
675  }
676 
677 
678  private:
682  const std::function<void(const CopyData &)> copier;
683  };
684 
685  template <typename Worker,
686  typename Copier,
687  typename Iterator,
688  typename ScratchData,
689  typename CopyData>
690  void
691  run(const Iterator & begin,
692  const typename identity<Iterator>::type &end,
693  Worker worker,
694  Copier copier,
695  const ScratchData & sample_scratch_data,
696  const CopyData & sample_copy_data,
697  const unsigned int queue_length,
698  const unsigned int chunk_size)
699  {
700  // create the three stages of the pipeline
702  iterator_range_to_item_stream(begin,
703  end,
704  queue_length,
705  chunk_size,
706  sample_scratch_data,
707  sample_copy_data);
708 
709  TBBWorker<Iterator, ScratchData, CopyData> worker_filter(worker);
710  TBBCopier<Iterator, ScratchData, CopyData> copier_filter(copier);
711 
712  // now create a pipeline from these stages
713  tbb::pipeline assembly_line;
714  assembly_line.add_filter(iterator_range_to_item_stream);
715  assembly_line.add_filter(worker_filter);
716  assembly_line.add_filter(copier_filter);
717 
718  // and run it
719  assembly_line.run(queue_length);
720 
721  assembly_line.clear();
722  }
723 
724  } // namespace tbb_no_coloring
725 # endif // DEAL_II_WITH_TBB
726 
727 
734  namespace sequential
735  {
739  template <typename Worker,
740  typename Copier,
741  typename Iterator,
742  typename ScratchData,
743  typename CopyData>
744  void
745  run(const Iterator & begin,
746  const typename identity<Iterator>::type &end,
747  Worker worker,
748  Copier copier,
749  const ScratchData & sample_scratch_data,
750  const CopyData & sample_copy_data)
751  {
752  // need to copy the sample since it is marked const
753  ScratchData scratch_data = sample_scratch_data;
754  CopyData copy_data = sample_copy_data; // NOLINT
755 
756  // Optimization: Check if the functions are not the zero function. To
757  // check zero-ness, create a C++ function out of it:
758  const bool have_worker =
759  (static_cast<const std::function<
760  void(const Iterator &, ScratchData &, CopyData &)> &>(worker)) !=
761  nullptr;
762  const bool have_copier =
763  (static_cast<const std::function<void(const CopyData &)> &>(
764  copier)) != nullptr;
765 
766  // Finally loop over all items and perform the necessary work:
767  for (Iterator i = begin; i != end; ++i)
768  {
769  if (have_worker)
770  worker(i, scratch_data, copy_data);
771  if (have_copier)
772  copier(copy_data);
773  }
774  }
775 
776 
777 
781  template <typename Worker,
782  typename Copier,
783  typename Iterator,
784  typename ScratchData,
785  typename CopyData>
786  void
787  run(const std::vector<std::vector<Iterator>> &colored_iterators,
788  Worker worker,
789  Copier copier,
790  const ScratchData & sample_scratch_data,
791  const CopyData & sample_copy_data)
792  {
793  // need to copy the sample since it is marked const
794  ScratchData scratch_data = sample_scratch_data;
795  CopyData copy_data = sample_copy_data; // NOLINT
796 
797  // Optimization: Check if the functions are not the zero function. To
798  // check zero-ness, create a C++ function out of it:
799  const bool have_worker =
800  (static_cast<const std::function<
801  void(const Iterator &, ScratchData &, CopyData &)> &>(worker)) !=
802  nullptr;
803  const bool have_copier =
804  (static_cast<const std::function<void(const CopyData &)> &>(
805  copier)) != nullptr;
806 
807  // Finally loop over all items and perform the necessary work:
808  for (unsigned int color = 0; color < colored_iterators.size(); ++color)
809  if (colored_iterators[color].size() > 0)
810  for (auto &it : colored_iterators[color])
811  {
812  if (have_worker)
813  worker(it, scratch_data, copy_data);
814  if (have_copier)
815  copier(copy_data);
816  }
817  }
818 
819  } // namespace sequential
820 
821 
822 
823 # ifdef DEAL_II_WITH_TBB
824 
831  namespace tbb_colored
832  {
838  template <typename Iterator, typename ScratchData, typename CopyData>
840  {
841  std::unique_ptr<ScratchData> scratch_data;
842  std::unique_ptr<CopyData> copy_data;
844 
849  : currently_in_use(false)
850  {}
851 
852  ScratchAndCopyDataObjects(std::unique_ptr<ScratchData> &&p,
853  std::unique_ptr<CopyData> && q,
854  const bool in_use)
855  : scratch_data(std::move(p))
856  , copy_data(std::move(q))
857  , currently_in_use(in_use)
858  {}
859 
860  // Provide a copy constructor that actually doesn't copy the
861  // internal state. This makes handling ScratchAndCopyDataObjects
862  // easier to handle with STL containers.
864  : currently_in_use(false)
865  {}
866  };
867 
868 
869 
875  template <typename Iterator, typename ScratchData, typename CopyData>
877  {
878  public:
883  const std::function<void(const Iterator &, ScratchData &, CopyData &)>
884  & worker,
885  const std::function<void(const CopyData &)> &copier,
886  const ScratchData & sample_scratch_data,
887  const CopyData & sample_copy_data)
888  : worker(worker)
889  , copier(copier)
890  , sample_scratch_data(sample_scratch_data)
891  , sample_copy_data(sample_copy_data)
892  {}
893 
894 
899  void
900  operator()(const tbb::blocked_range<
901  typename std::vector<Iterator>::const_iterator> &range)
902  {
903  // we need to find an unused scratch and corresponding copy
904  // data object in the list that corresponds to the current
905  // thread and then mark it as used. If we can't find one,
906  // create one as discussed in the discussion of the documentation
907  // of the IteratorRangeToItemStream::scratch_data variable,
908  // there is no need to synchronize access to this variable
909  // using a mutex as long as we have no yield-point in between.
910  // This means that we can't take an iterator into the list
911  // now and expect it to still be valid after calling the worker,
912  // but we at least do not have to lock the following section.
913  ScratchData *scratch_data = nullptr;
914  CopyData * copy_data = nullptr;
915  {
916  ScratchAndCopyDataList &scratch_and_copy_data_list = data.get();
917 
918  // see if there is an unused object. if so, grab it and mark
919  // it as used
920  for (typename ScratchAndCopyDataList::iterator p =
921  scratch_and_copy_data_list.begin();
922  p != scratch_and_copy_data_list.end();
923  ++p)
924  if (p->currently_in_use == false)
925  {
926  scratch_data = p->scratch_data.get();
927  copy_data = p->copy_data.get();
928  p->currently_in_use = true;
929  break;
930  }
931 
932  // if no element in the list was found, create one and mark it as
933  // used
934  if (scratch_data == nullptr)
935  {
936  Assert(copy_data == nullptr, ExcInternalError());
937 
938  scratch_and_copy_data_list.emplace_back(
939  std::make_unique<ScratchData>(sample_scratch_data),
940  std::make_unique<CopyData>(sample_copy_data),
941  true);
942  scratch_data =
943  scratch_and_copy_data_list.back().scratch_data.get();
944  copy_data = scratch_and_copy_data_list.back().copy_data.get();
945  }
946  }
947 
948  // then call the worker and copier functions on each
949  // element of the chunk we were given.
950  for (typename std::vector<Iterator>::const_iterator p = range.begin();
951  p != range.end();
952  ++p)
953  {
954  try
955  {
956  if (worker)
957  worker(*p, *scratch_data, *copy_data);
958  if (copier)
959  copier(*copy_data);
960  }
961  catch (const std::exception &exc)
962  {
964  }
965  catch (...)
966  {
968  }
969  }
970 
971  // finally mark the scratch object as unused again. as above, there
972  // is no need to lock anything here since the object we work on
973  // is thread-local
974  {
975  ScratchAndCopyDataList &scratch_and_copy_data_list = data.get();
976 
977  for (typename ScratchAndCopyDataList::iterator p =
978  scratch_and_copy_data_list.begin();
979  p != scratch_and_copy_data_list.end();
980  ++p)
981  if (p->scratch_data.get() == scratch_data)
982  {
983  Assert(p->currently_in_use == true, ExcInternalError());
984  p->currently_in_use = false;
985  }
986  }
987  }
988 
989  private:
990  using ScratchAndCopyDataObjects = typename tbb_colored::
991  ScratchAndCopyDataObjects<Iterator, ScratchData, CopyData>;
992 
997  using ScratchAndCopyDataList = std::list<ScratchAndCopyDataObjects>;
998 
1000 
1005  const std::function<void(const Iterator &, ScratchData &, CopyData &)>
1007 
1012  const std::function<void(const CopyData &)> copier;
1013 
1017  const ScratchData &sample_scratch_data;
1018  const CopyData & sample_copy_data;
1019  };
1020 
1024  template <typename Worker,
1025  typename Copier,
1026  typename Iterator,
1027  typename ScratchData,
1028  typename CopyData>
1029  void
1030  run(const std::vector<std::vector<Iterator>> &colored_iterators,
1031  Worker worker,
1032  Copier copier,
1033  const ScratchData & sample_scratch_data,
1034  const CopyData & sample_copy_data,
1035  const unsigned int chunk_size)
1036  {
1037  // loop over the various colors of what we're given
1038  for (unsigned int color = 0; color < colored_iterators.size(); ++color)
1039  if (colored_iterators[color].size() > 0)
1040  {
1041  using WorkerAndCopier = internal::tbb_colored::
1042  WorkerAndCopier<Iterator, ScratchData, CopyData>;
1043 
1044  using RangeType = typename std::vector<Iterator>::const_iterator;
1045 
1046  WorkerAndCopier worker_and_copier(worker,
1047  copier,
1048  sample_scratch_data,
1049  sample_copy_data);
1050 
1052  colored_iterators[color].begin(),
1053  colored_iterators[color].end(),
1054  [&worker_and_copier](
1055  const tbb::blocked_range<
1056  typename std::vector<Iterator>::const_iterator> &range) {
1057  worker_and_copier(range);
1058  },
1059  chunk_size);
1060  }
1061  }
1062 
1063  } // namespace tbb_colored
1064 # endif // DEAL_II_WITH_TBB
1065 
1066 
1067  } // namespace internal
1068 
1069 
1070 
1118  template <typename Worker,
1119  typename Copier,
1120  typename Iterator,
1121  typename ScratchData,
1122  typename CopyData>
1123  void
1124  run(const std::vector<std::vector<Iterator>> &colored_iterators,
1125  Worker worker,
1126  Copier copier,
1127  const ScratchData & sample_scratch_data,
1128  const CopyData & sample_copy_data,
1129  const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1130  const unsigned int chunk_size = 8);
1131 
1132 
1182  template <typename Worker,
1183  typename Copier,
1184  typename Iterator,
1185  typename ScratchData,
1186  typename CopyData>
1187  void
1188  run(const Iterator & begin,
1189  const typename identity<Iterator>::type &end,
1190  Worker worker,
1191  Copier copier,
1192  const ScratchData & sample_scratch_data,
1193  const CopyData & sample_copy_data,
1194  const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1195  const unsigned int chunk_size = 8)
1196  {
1197  Assert(queue_length > 0,
1198  ExcMessage("The queue length must be at least one, and preferably "
1199  "larger than the number of processors on this system."));
1200  (void)queue_length; // removes -Wunused-parameter warning in optimized mode
1201  Assert(chunk_size > 0, ExcMessage("The chunk_size must be at least one."));
1202  (void)chunk_size; // removes -Wunused-parameter warning in optimized mode
1203 
1204  // If no work then skip. (only use operator!= for iterators since we may
1205  // not have an equality comparison operator)
1206  if (!(begin != end))
1207  return;
1208 
1209  if (MultithreadInfo::n_threads() > 1)
1210  {
1211 # ifdef DEAL_II_WITH_TBB
1212  if (static_cast<const std::function<void(const CopyData &)> &>(copier))
1213  {
1214  // If we have a copier, run the algorithm:
1216  end,
1217  worker,
1218  copier,
1219  sample_scratch_data,
1220  sample_copy_data,
1221  queue_length,
1222  chunk_size);
1223  }
1224  else
1225  {
1226  // There is no copier function. in this case, we have an
1227  // embarrassingly parallel problem where we can
1228  // essentially apply parallel_for. because parallel_for
1229  // requires subdividing the range for which operator- is
1230  // necessary between iterators, it is often inefficient to
1231  // apply it directly to cell ranges and similar iterator
1232  // types for which operator- is expensive or, in fact,
1233  // nonexistent. rather, in that case, we simply copy the
1234  // iterators into a large array and use operator- on
1235  // iterators to this array of iterators.
1236  //
1237  // instead of duplicating code, this is essentially the
1238  // same situation we have in the colored implementation below, so we
1239  // just defer to that place
1240  std::vector<std::vector<Iterator>> all_iterators(1);
1241  for (Iterator p = begin; p != end; ++p)
1242  all_iterators[0].push_back(p);
1243 
1244  run(all_iterators,
1245  worker,
1246  copier,
1247  sample_scratch_data,
1248  sample_copy_data,
1249  queue_length,
1250  chunk_size);
1251  }
1252 
1253  // exit this function to not run the sequential version below:
1254  return;
1255 # endif
1256  }
1257 
1258  // no TBB installed or we are requested to run sequentially:
1260  begin, end, worker, copier, sample_scratch_data, sample_copy_data);
1261  }
1262 
1263 
1264 
1272  template <typename Worker,
1273  typename Copier,
1274  typename IteratorRangeType,
1275  typename ScratchData,
1276  typename CopyData,
1277  typename = typename std::enable_if<
1279  void
1280  run(IteratorRangeType iterator_range,
1281  Worker worker,
1282  Copier copier,
1283  const ScratchData &sample_scratch_data,
1284  const CopyData & sample_copy_data,
1285  const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1286  const unsigned int chunk_size = 8)
1287  {
1288  // Call the function above
1289  run(iterator_range.begin(),
1290  iterator_range.end(),
1291  worker,
1292  copier,
1294  sample_copy_data,
1295  queue_length,
1296  chunk_size);
1297  }
1298 
1299 
1300 
1304  template <typename Worker,
1305  typename Copier,
1306  typename Iterator,
1307  typename ScratchData,
1308  typename CopyData>
1309  void
1310  run(const IteratorRange<Iterator> &iterator_range,
1311  Worker worker,
1312  Copier copier,
1313  const ScratchData & sample_scratch_data,
1314  const CopyData & sample_copy_data,
1315  const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1316  const unsigned int chunk_size = 8)
1317  {
1318  // Call the function above
1319  run(iterator_range.begin(),
1320  iterator_range.end(),
1321  worker,
1322  copier,
1324  sample_copy_data,
1325  queue_length,
1326  chunk_size);
1327  }
1328 
1329 
1330 
1331  template <typename Worker,
1332  typename Copier,
1333  typename Iterator,
1334  typename ScratchData,
1335  typename CopyData>
1336  void
1337  run(const std::vector<std::vector<Iterator>> &colored_iterators,
1338  Worker worker,
1339  Copier copier,
1340  const ScratchData & sample_scratch_data,
1341  const CopyData & sample_copy_data,
1342  const unsigned int queue_length,
1343  const unsigned int chunk_size)
1344  {
1345  Assert(queue_length > 0,
1346  ExcMessage("The queue length must be at least one, and preferably "
1347  "larger than the number of processors on this system."));
1348  (void)queue_length; // removes -Wunused-parameter warning in optimized mode
1349  Assert(chunk_size > 0, ExcMessage("The chunk_size must be at least one."));
1350  (void)chunk_size; // removes -Wunused-parameter warning in optimized mode
1351 
1352 
1353  if (MultithreadInfo::n_threads() > 1)
1354  {
1355 # ifdef DEAL_II_WITH_TBB
1356  internal::tbb_colored::run(colored_iterators,
1357  worker,
1358  copier,
1359  sample_scratch_data,
1360  sample_copy_data,
1361  chunk_size);
1362 
1363  // exit this function to not run the sequential version below:
1364  return;
1365 # endif
1366  }
1367 
1368  // run all colors sequentially:
1369  {
1370  internal::sequential::run(colored_iterators,
1371  worker,
1372  copier,
1373  sample_scratch_data,
1374  sample_copy_data);
1375  }
1376  }
1377 
1378 
1379 
1421  template <typename MainClass,
1422  typename Iterator,
1423  typename ScratchData,
1424  typename CopyData>
1425  void
1426  run(const Iterator & begin,
1427  const typename identity<Iterator>::type &end,
1428  MainClass & main_object,
1429  void (MainClass::*worker)(const Iterator &, ScratchData &, CopyData &),
1430  void (MainClass::*copier)(const CopyData &),
1431  const ScratchData &sample_scratch_data,
1432  const CopyData & sample_copy_data,
1433  const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1434  const unsigned int chunk_size = 8)
1435  {
1436  // forward to the other function
1437  run(begin,
1438  end,
1439  [&main_object, worker](const Iterator &iterator,
1440  ScratchData & scratch_data,
1441  CopyData & copy_data) {
1442  (main_object.*worker)(iterator, scratch_data, copy_data);
1443  },
1444  [&main_object, copier](const CopyData &copy_data) {
1445  (main_object.*copier)(copy_data);
1446  },
1448  sample_copy_data,
1449  queue_length,
1450  chunk_size);
1451  }
1452 
1453 
1454  template <typename MainClass,
1455  typename Iterator,
1456  typename ScratchData,
1457  typename CopyData>
1458  void
1460  const IteratorOverIterators<typename identity<Iterator>::type> &end,
1461  MainClass &main_object,
1462  void (MainClass::*worker)(const Iterator &, ScratchData &, CopyData &),
1463  void (MainClass::*copier)(const CopyData &),
1464  const ScratchData &sample_scratch_data,
1465  const CopyData & sample_copy_data,
1466  const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1467  const unsigned int chunk_size = 8)
1468  {
1469  // forward to the other function
1470  run(begin,
1471  end,
1472  [&main_object, worker](const Iterator &iterator,
1473  ScratchData & scratch_data,
1474  CopyData & copy_data) {
1475  (main_object.*worker)(iterator, scratch_data, copy_data);
1476  },
1477  [&main_object, copier](const CopyData &copy_data) {
1478  (main_object.*copier)(copy_data);
1479  },
1481  sample_copy_data,
1482  queue_length,
1483  chunk_size);
1484  }
1485 
1486 
1487 
1495  template <typename MainClass,
1496  typename IteratorRangeType,
1497  typename ScratchData,
1498  typename CopyData,
1499  typename = typename std::enable_if<
1501  void
1502  run(IteratorRangeType iterator_range,
1503  MainClass & main_object,
1504  void (MainClass::*worker)(
1506  ScratchData &,
1507  CopyData &),
1508  void (MainClass::*copier)(const CopyData &),
1509  const ScratchData &sample_scratch_data,
1510  const CopyData & sample_copy_data,
1511  const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1512  const unsigned int chunk_size = 8)
1513  {
1514  // Call the function above
1515  run(std::begin(iterator_range),
1516  std::end(iterator_range),
1517  main_object,
1518  worker,
1519  copier,
1520  sample_scratch_data,
1521  sample_copy_data,
1522  queue_length,
1523  chunk_size);
1524  }
1525 
1526 
1527 
1531  template <typename MainClass,
1532  typename Iterator,
1533  typename ScratchData,
1534  typename CopyData>
1535  void
1537  MainClass & main_object,
1538  void (MainClass::*worker)(const Iterator &, ScratchData &, CopyData &),
1539  void (MainClass::*copier)(const CopyData &),
1540  const ScratchData &sample_scratch_data,
1541  const CopyData & sample_copy_data,
1542  const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1543  const unsigned int chunk_size = 8)
1544  {
1545  // Call the function above
1546  run(std::begin(iterator_range),
1547  std::end(iterator_range),
1548  main_object,
1549  worker,
1550  copier,
1551  sample_scratch_data,
1552  sample_copy_data,
1553  queue_length,
1554  chunk_size);
1555  }
1556 
1557 } // namespace WorkStream
1558 
1559 
1560 
1562 
1563 
1564 
1565 //---------------------------- work_stream.h ---------------------------
1566 // end of #ifndef dealii_work_stream_h
1567 #endif
1568 //---------------------------- work_stream.h ---------------------------
TBBWorker(const std::function< void(const Iterator &, ScratchData &, CopyData &)> &worker, bool copier_exist=true)
Definition: work_stream.h:479
ScratchAndCopyDataObjects(const ScratchAndCopyDataObjects &)
Definition: work_stream.h:863
Threads::ThreadLocalStorage< typename ItemType::ScratchDataList > thread_local_scratch
Definition: work_stream.h:445
const std::function< void(const Iterator &, ScratchData &, CopyData &)> worker
Definition: work_stream.h:602
const std::function< void(const Iterator &, ScratchData &, CopyData &)> worker
Definition: work_stream.h:1006
IteratorOverIterators begin()
void parallel_for(Iterator x_begin, Iterator x_end, const Functor &functor, const unsigned int grainsize)
Definition: parallel.h:158
const std::function< void(const CopyData &)> copier
Definition: work_stream.h:682
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:882
const std::function< void(const CopyData &)> copier
Definition: work_stream.h:1012
STL namespace.
static ::ExceptionBase & ExcMessage(std::string arg1)
Threads::ThreadLocalStorage< ScratchDataList > * scratch_data
Definition: work_stream.h:286
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:319
#define Assert(cond, exc)
Definition: exceptions.h:1473
#define DEAL_II_NAMESPACE_CLOSE
Definition: config.h:396
VectorType::value_type * end(VectorType &V)
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:1030
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:745
ScratchAndCopyDataObjects(std::unique_ptr< ScratchData > &&p, std::unique_ptr< CopyData > &&q, const bool in_use)
Definition: work_stream.h:852
typename tbb_colored::ScratchAndCopyDataObjects< Iterator, ScratchData, CopyData > ScratchAndCopyDataObjects
Definition: work_stream.h:991
#define DEAL_II_NAMESPACE_OPEN
Definition: config.h:395
VectorType::value_type * begin(VectorType &V)
std::list< ScratchAndCopyDataObjects > ScratchAndCopyDataList
Definition: work_stream.h:997
Threads::ThreadLocalStorage< ScratchAndCopyDataList > data
Definition: work_stream.h:999
TBBCopier(const std::function< void(const CopyData &)> &copier)
Definition: work_stream.h:628
static unsigned int n_threads()
void handle_std_exception(const std::exception &exc)
void operator()(const tbb::blocked_range< typename std::vector< Iterator >::const_iterator > &range)
Definition: work_stream.h:900
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:691
static ::ExceptionBase & ExcInternalError()
IteratorOverIterators end() const