Reference documentation for deal.II version 9.2.0
\(\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_THREADS
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 
157 namespace WorkStream
158 {
159 # ifdef DEAL_II_WITH_THREADS
160 
161  namespace internal
162  {
175  namespace Implementation2
176  {
180  template <typename Iterator, typename ScratchData, typename CopyData>
181  class IteratorRangeToItemStream : public tbb::filter
182  {
183  public:
190  struct ItemType
191  {
198  {
199  std::unique_ptr<ScratchData> scratch_data;
201 
206  : currently_in_use(false)
207  {}
208 
209  ScratchDataObject(ScratchData *p, const bool in_use)
210  : scratch_data(p)
211  , currently_in_use(in_use)
212  {}
213 
214  ScratchDataObject(ScratchDataObject &&o) noexcept = default;
215  };
216 
217 
222  using ScratchDataList = std::list<ScratchDataObject>;
223 
228  std::vector<Iterator> work_items;
229 
235  std::vector<CopyData> copy_datas;
236 
242  unsigned int n_items;
243 
276 
281  const ScratchData *sample_scratch_data;
282 
288 
289 
295  : n_items(0)
296  , scratch_data(nullptr)
297  , sample_scratch_data(nullptr)
298  , currently_in_use(false)
299  {}
300  };
301 
302 
309  const Iterator & end,
310  const unsigned int buffer_size,
311  const unsigned int chunk_size,
312  const ScratchData &sample_scratch_data,
313  const CopyData & sample_copy_data)
314  : tbb::filter(/*is_serial=*/true)
315  , remaining_iterator_range(begin, end)
316  , item_buffer(buffer_size)
317  , sample_scratch_data(sample_scratch_data)
318  , chunk_size(chunk_size)
319  {
320  // initialize the elements of the ring buffer
321  for (unsigned int element = 0; element < item_buffer.size();
322  ++element)
323  {
324  Assert(item_buffer[element].n_items == 0, ExcInternalError());
325 
326  item_buffer[element].work_items.resize(
327  chunk_size, remaining_iterator_range.second);
328  item_buffer[element].scratch_data = &thread_local_scratch;
329  item_buffer[element].sample_scratch_data = &sample_scratch_data;
330  item_buffer[element].copy_datas.resize(chunk_size,
331  sample_copy_data);
332  item_buffer[element].currently_in_use = false;
333  }
334  }
335 
336 
340  virtual void *
341  operator()(void *) override
342  {
343  // find first unused item. we know that there must be one
344  // because we have set the maximal number of tokens in flight
345  // and have set the ring buffer to have exactly this size. so
346  // if this function is called, we know that less than the
347  // maximal number of items in currently in flight
348  //
349  // note that we need not lock access to this array since
350  // the current stage is run sequentially and we can therefore
351  // enter the following block only once at any given time.
352  // thus, there can be no race condition between checking that
353  // a flag is false and setting it to true. (there may be
354  // another thread where we release items and set 'false'
355  // flags to 'true', but that too does not produce any
356  // problems)
357  ItemType *current_item = nullptr;
358  for (unsigned int i = 0; i < item_buffer.size(); ++i)
359  if (item_buffer[i].currently_in_use == false)
360  {
361  item_buffer[i].currently_in_use = true;
362  current_item = &item_buffer[i];
363  break;
364  }
365  Assert(current_item != nullptr,
366  ExcMessage("This can't be. There must be a free item!"));
367 
368  // initialize the next item. it may
369  // consist of at most chunk_size
370  // elements
371  current_item->n_items = 0;
372  while ((remaining_iterator_range.first !=
373  remaining_iterator_range.second) &&
374  (current_item->n_items < chunk_size))
375  {
376  current_item->work_items[current_item->n_items] =
378 
379  ++remaining_iterator_range.first;
380  ++current_item->n_items;
381  }
382 
383  if (current_item->n_items == 0)
384  // there were no items
385  // left. terminate the pipeline
386  return nullptr;
387  else
388  return current_item;
389  }
390 
391  private:
396  std::pair<Iterator, Iterator> remaining_iterator_range;
397 
401  std::vector<ItemType> item_buffer;
402 
435 
441  const ScratchData &sample_scratch_data;
442 
449  const unsigned int chunk_size;
450  };
451 
452 
453 
459  template <typename Iterator, typename ScratchData, typename CopyData>
460  class Worker : public tbb::filter
461  {
462  public:
469  const std::function<void(const Iterator &, ScratchData &, CopyData &)>
470  & worker,
471  bool copier_exist = true)
472  : tbb::filter(/* is_serial= */ false)
473  , worker(worker)
474  , copier_exist(copier_exist)
475  {}
476 
477 
481  void *
482  operator()(void *item) override
483  {
484  // first unpack the current item
485  using ItemType =
487  ScratchData,
488  CopyData>::ItemType;
489 
490  ItemType *current_item = static_cast<ItemType *>(item);
491 
492  // we need to find an unused scratch data object in the list that
493  // corresponds to the current thread and then mark it as used. if
494  // we can't find one, create one
495  //
496  // as discussed in the discussion of the documentation of the
497  // IteratorRangeToItemStream::scratch_data variable, there is no
498  // need to synchronize access to this variable using a mutex
499  // as long as we have no yield-point in between. this means that
500  // we can't take an iterator into the list now and expect it to
501  // still be valid after calling the worker, but we at least do
502  // not have to lock the following section
503  ScratchData *scratch_data = nullptr;
504  {
505  typename ItemType::ScratchDataList &scratch_data_list =
506  current_item->scratch_data->get();
507 
508  // see if there is an unused object. if so, grab it and mark
509  // it as used
510  for (typename ItemType::ScratchDataList::iterator p =
511  scratch_data_list.begin();
512  p != scratch_data_list.end();
513  ++p)
514  if (p->currently_in_use == false)
515  {
516  scratch_data = p->scratch_data.get();
517  p->currently_in_use = true;
518  break;
519  }
520 
521  // if no object was found, create one and mark it as used
522  if (scratch_data == nullptr)
523  {
524  scratch_data =
525  new ScratchData(*current_item->sample_scratch_data);
526 
527  typename ItemType::ScratchDataList::value_type
528  new_scratch_object(scratch_data, true);
529  scratch_data_list.push_back(std::move(new_scratch_object));
530  }
531  }
532 
533  // then call the worker function on each element of the chunk we were
534  // given. since these worker functions are called on separate threads,
535  // nothing good can happen if they throw an exception and we are best
536  // off catching it and showing an error message
537  for (unsigned int i = 0; i < current_item->n_items; ++i)
538  {
539  try
540  {
541  if (worker)
542  worker(current_item->work_items[i],
543  *scratch_data,
544  current_item->copy_datas[i]);
545  }
546  catch (const std::exception &exc)
547  {
549  }
550  catch (...)
551  {
553  }
554  }
555 
556  // finally mark the scratch object as unused again. as above, there
557  // is no need to lock anything here since the object we work on
558  // is thread-local
559  {
560  typename ItemType::ScratchDataList &scratch_data_list =
561  current_item->scratch_data->get();
562 
563  for (typename ItemType::ScratchDataList::iterator p =
564  scratch_data_list.begin();
565  p != scratch_data_list.end();
566  ++p)
567  if (p->scratch_data.get() == scratch_data)
568  {
569  Assert(p->currently_in_use == true, ExcInternalError());
570  p->currently_in_use = false;
571  }
572  }
573 
574  // if there is no copier, mark current item as usable again
575  if (copier_exist == false)
576  current_item->currently_in_use = false;
577 
578 
579  // then return the original pointer
580  // to the now modified object
581  return item;
582  }
583 
584 
585  private:
590  const std::function<void(const Iterator &, ScratchData &, CopyData &)>
592 
598  };
599 
600 
601 
607  template <typename Iterator, typename ScratchData, typename CopyData>
608  class Copier : public tbb::filter
609  {
610  public:
617  Copier(const std::function<void(const CopyData &)> &copier)
618  : tbb::filter(/*is_serial=*/true)
619  , copier(copier)
620  {}
621 
622 
626  void *
627  operator()(void *item) override
628  {
629  // first unpack the current item
630  using ItemType =
632  ScratchData,
633  CopyData>::ItemType;
634 
635  ItemType *current_item = static_cast<ItemType *>(item);
636 
637  // initiate copying data. for the same reasons as in the worker class
638  // above, catch exceptions rather than letting it propagate into
639  // unknown territories
640  for (unsigned int i = 0; i < current_item->n_items; ++i)
641  {
642  try
643  {
644  if (copier)
645  copier(current_item->copy_datas[i]);
646  }
647  catch (const std::exception &exc)
648  {
650  }
651  catch (...)
652  {
654  }
655  }
656 
657  // mark current item as usable again
658  current_item->currently_in_use = false;
659 
660 
661  // return an invalid item since we are at the end of the
662  // pipeline
663  return nullptr;
664  }
665 
666 
667  private:
671  const std::function<void(const CopyData &)> copier;
672  };
673 
674  } // namespace Implementation2
675 
676 
684  namespace Implementation3
685  {
691  template <typename Iterator, typename ScratchData, typename CopyData>
693  {
694  std::unique_ptr<ScratchData> scratch_data;
695  std::unique_ptr<CopyData> copy_data;
697 
702  : currently_in_use(false)
703  {}
704 
706  CopyData * q,
707  const bool in_use)
708  : scratch_data(p)
709  , copy_data(q)
710  , currently_in_use(in_use)
711  {}
712 
713  // TODO: when we push back an object to the list of scratch objects, in
714  // Worker::operator(), we first create an object and then copy
715  // it to the end of this list. this involves having two objects
716  // of the current type having pointers to it, each with their own
717  // currently_in_use flag. there is probably little harm in this
718  // because the original one goes out of scope right away again, but
719  // it's certainly awkward. one way to avoid this would be to use
720  // unique_ptr but we'd need to figure out a way to use it in
721  // non-C++11 mode
723  : scratch_data(o.scratch_data)
724  , copy_data(o.copy_data)
725  , currently_in_use(o.currently_in_use)
726  {}
727  };
728 
729 
730 
736  template <typename Iterator, typename ScratchData, typename CopyData>
738  {
739  public:
744  const std::function<void(const Iterator &, ScratchData &, CopyData &)>
745  & worker,
746  const std::function<void(const CopyData &)> &copier,
747  const ScratchData & sample_scratch_data,
748  const CopyData & sample_copy_data)
749  : worker(worker)
750  , copier(copier)
751  , sample_scratch_data(sample_scratch_data)
752  , sample_copy_data(sample_copy_data)
753  {}
754 
755 
760  void
761  operator()(const tbb::blocked_range<
762  typename std::vector<Iterator>::const_iterator> &range)
763  {
764  // we need to find an unused scratch and corresponding copy
765  // data object in the list that corresponds to the current
766  // thread and then mark it as used. If we can't find one,
767  // create one as discussed in the discussion of the documentation
768  // of the IteratorRangeToItemStream::scratch_data variable,
769  // there is no need to synchronize access to this variable
770  // using a mutex as long as we have no yield-point in between.
771  // This means that we can't take an iterator into the list
772  // now and expect it to still be valid after calling the worker,
773  // but we at least do not have to lock the following section.
774  ScratchData *scratch_data = nullptr;
775  CopyData * copy_data = nullptr;
776  {
777  ScratchAndCopyDataList &scratch_and_copy_data_list = data.get();
778 
779  // see if there is an unused object. if so, grab it and mark
780  // it as used
781  for (typename ScratchAndCopyDataList::iterator p =
782  scratch_and_copy_data_list.begin();
783  p != scratch_and_copy_data_list.end();
784  ++p)
785  if (p->currently_in_use == false)
786  {
787  scratch_data = p->scratch_data.get();
788  copy_data = p->copy_data.get();
789  p->currently_in_use = true;
790  break;
791  }
792 
793  // if no element in the list was found, create one and mark it as
794  // used
795  if (scratch_data == nullptr)
796  {
797  Assert(copy_data == nullptr, ExcInternalError());
798  scratch_data = new ScratchData(sample_scratch_data);
799  copy_data = new CopyData(sample_copy_data);
800 
801  scratch_and_copy_data_list.emplace_back(scratch_data,
802  copy_data,
803  true);
804  }
805  }
806 
807  // then call the worker and copier functions on each
808  // element of the chunk we were given.
809  for (typename std::vector<Iterator>::const_iterator p = range.begin();
810  p != range.end();
811  ++p)
812  {
813  try
814  {
815  if (worker)
816  worker(*p, *scratch_data, *copy_data);
817  if (copier)
818  copier(*copy_data);
819  }
820  catch (const std::exception &exc)
821  {
823  }
824  catch (...)
825  {
827  }
828  }
829 
830  // finally mark the scratch object as unused again. as above, there
831  // is no need to lock anything here since the object we work on
832  // is thread-local
833  {
834  ScratchAndCopyDataList &scratch_and_copy_data_list = data.get();
835 
836  for (typename ScratchAndCopyDataList::iterator p =
837  scratch_and_copy_data_list.begin();
838  p != scratch_and_copy_data_list.end();
839  ++p)
840  if (p->scratch_data.get() == scratch_data)
841  {
842  Assert(p->currently_in_use == true, ExcInternalError());
843  p->currently_in_use = false;
844  }
845  }
846  }
847 
848  private:
849  using ScratchAndCopyDataObjects = typename Implementation3::
850  ScratchAndCopyDataObjects<Iterator, ScratchData, CopyData>;
851 
856  using ScratchAndCopyDataList = std::list<ScratchAndCopyDataObjects>;
857 
859 
864  const std::function<void(const Iterator &, ScratchData &, CopyData &)>
866 
871  const std::function<void(const CopyData &)> copier;
872 
876  const ScratchData &sample_scratch_data;
877  const CopyData & sample_copy_data;
878  };
879  } // namespace Implementation3
880 
881  } // namespace internal
882 
883 
884 # endif // DEAL_II_WITH_THREADS
885 
886 
934  template <typename Worker,
935  typename Copier,
936  typename Iterator,
937  typename ScratchData,
938  typename CopyData>
939  void
940  run(const std::vector<std::vector<Iterator>> &colored_iterators,
941  Worker worker,
942  Copier copier,
943  const ScratchData & sample_scratch_data,
944  const CopyData & sample_copy_data,
945  const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
946  const unsigned int chunk_size = 8);
947 
948 
998  template <typename Worker,
999  typename Copier,
1000  typename Iterator,
1001  typename ScratchData,
1002  typename CopyData>
1003  void
1004  run(const Iterator & begin,
1005  const typename identity<Iterator>::type &end,
1006  Worker worker,
1007  Copier copier,
1008  const ScratchData & sample_scratch_data,
1009  const CopyData & sample_copy_data,
1010  const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1011  const unsigned int chunk_size = 8)
1012  {
1013  Assert(queue_length > 0,
1014  ExcMessage("The queue length must be at least one, and preferably "
1015  "larger than the number of processors on this system."));
1016  (void)queue_length; // removes -Wunused-parameter warning in optimized mode
1017  Assert(chunk_size > 0, ExcMessage("The chunk_size must be at least one."));
1018  (void)chunk_size; // removes -Wunused-parameter warning in optimized mode
1019 
1020  // if no work then skip. (only use operator!= for iterators since we may
1021  // not have an equality comparison operator)
1022  if (!(begin != end))
1023  return;
1024 
1025  // we want to use TBB if we have support and if it is not disabled at
1026  // runtime:
1027 # ifdef DEAL_II_WITH_THREADS
1028  if (MultithreadInfo::n_threads() == 1)
1029 # endif
1030  {
1031  // need to copy the sample since it is marked const
1032  ScratchData scratch_data = sample_scratch_data;
1033  CopyData copy_data = sample_copy_data; // NOLINT
1034 
1035  for (Iterator i = begin; i != end; ++i)
1036  {
1037  // need to check if the function is not the zero function. To
1038  // check zero-ness, create a C++ function out of it and check that
1039  if (static_cast<const std::function<
1040  void(const Iterator &, ScratchData &, CopyData &)> &>(worker))
1041  worker(i, scratch_data, copy_data);
1042  if (static_cast<const std::function<void(const CopyData &)> &>(
1043  copier))
1044  copier(copy_data);
1045  }
1046  }
1047 # ifdef DEAL_II_WITH_THREADS
1048  else // have TBB and use more than one thread
1049  {
1050  // Check that the copier exist
1051  if (static_cast<const std::function<void(const CopyData &)> &>(copier))
1052  {
1053  // create the three stages of the pipeline
1054  internal::Implementation2::
1055  IteratorRangeToItemStream<Iterator, ScratchData, CopyData>
1056  iterator_range_to_item_stream(begin,
1057  end,
1058  queue_length,
1059  chunk_size,
1060  sample_scratch_data,
1061  sample_copy_data);
1062 
1064  worker_filter(worker);
1066  copier_filter(copier);
1067 
1068  // now create a pipeline from these stages
1069  tbb::pipeline assembly_line;
1070  assembly_line.add_filter(iterator_range_to_item_stream);
1071  assembly_line.add_filter(worker_filter);
1072  assembly_line.add_filter(copier_filter);
1073 
1074  // and run it
1075  assembly_line.run(queue_length);
1076 
1077  assembly_line.clear();
1078  }
1079  else
1080  {
1081  // there is no copier function. in this case, we have an
1082  // embarrassingly parallel problem where we can
1083  // essentially apply parallel_for. because parallel_for
1084  // requires subdividing the range for which operator- is
1085  // necessary between iterators, it is often inefficient to
1086  // apply it directly to cell ranges and similar iterator
1087  // types for which operator- is expensive or, in fact,
1088  // nonexistent. rather, in that case, we simply copy the
1089  // iterators into a large array and use operator- on
1090  // iterators to this array of iterators.
1091  //
1092  // instead of duplicating code, this is essentially the
1093  // same situation we have in Implementation3 below, so we
1094  // just defer to that place
1095  std::vector<std::vector<Iterator>> all_iterators(1);
1096  for (Iterator p = begin; p != end; ++p)
1097  all_iterators[0].push_back(p);
1098 
1099  run(all_iterators,
1100  worker,
1101  copier,
1102  sample_scratch_data,
1103  sample_copy_data,
1104  queue_length,
1105  chunk_size);
1106  }
1107  }
1108 # endif
1109  }
1110 
1111 
1112 
1120  template <typename Worker,
1121  typename Copier,
1122  typename IteratorRangeType,
1123  typename ScratchData,
1124  typename CopyData,
1125  typename = typename std::enable_if<
1127  void
1128  run(IteratorRangeType iterator_range,
1129  Worker worker,
1130  Copier copier,
1131  const ScratchData &sample_scratch_data,
1132  const CopyData & sample_copy_data,
1133  const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1134  const unsigned int chunk_size = 8)
1135  {
1136  // Call the function above
1137  run(iterator_range.begin(),
1138  iterator_range.end(),
1139  worker,
1140  copier,
1142  sample_copy_data,
1143  queue_length,
1144  chunk_size);
1145  }
1146 
1147 
1148 
1152  template <typename Worker,
1153  typename Copier,
1154  typename Iterator,
1155  typename ScratchData,
1156  typename CopyData>
1157  void
1158  run(const IteratorRange<Iterator> &iterator_range,
1159  Worker worker,
1160  Copier copier,
1161  const ScratchData & sample_scratch_data,
1162  const CopyData & sample_copy_data,
1163  const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1164  const unsigned int chunk_size = 8)
1165  {
1166  // Call the function above
1167  run(iterator_range.begin(),
1168  iterator_range.end(),
1169  worker,
1170  copier,
1172  sample_copy_data,
1173  queue_length,
1174  chunk_size);
1175  }
1176 
1177 
1178  // Implementation 3:
1179  template <typename Worker,
1180  typename Copier,
1181  typename Iterator,
1182  typename ScratchData,
1183  typename CopyData>
1184  void
1185  run(const std::vector<std::vector<Iterator>> &colored_iterators,
1186  Worker worker,
1187  Copier copier,
1188  const ScratchData & sample_scratch_data,
1189  const CopyData & sample_copy_data,
1190  const unsigned int queue_length,
1191  const unsigned int chunk_size)
1192  {
1193  Assert(queue_length > 0,
1194  ExcMessage("The queue length must be at least one, and preferably "
1195  "larger than the number of processors on this system."));
1196  (void)queue_length; // removes -Wunused-parameter warning in optimized mode
1197  Assert(chunk_size > 0, ExcMessage("The chunk_size must be at least one."));
1198  (void)chunk_size; // removes -Wunused-parameter warning in optimized mode
1199 
1200  // we want to use TBB if we have support and if it is not disabled at
1201  // runtime:
1202 # ifdef DEAL_II_WITH_THREADS
1203  if (MultithreadInfo::n_threads() == 1)
1204 # endif
1205  {
1206  // need to copy the sample since it is marked const
1207  ScratchData scratch_data = sample_scratch_data;
1208  CopyData copy_data = sample_copy_data; // NOLINT
1209 
1210  for (unsigned int color = 0; color < colored_iterators.size(); ++color)
1211  for (typename std::vector<Iterator>::const_iterator p =
1212  colored_iterators[color].begin();
1213  p != colored_iterators[color].end();
1214  ++p)
1215  {
1216  // need to check if the function is not the zero function. To
1217  // check zero-ness, create a C++ function out of it and check that
1218  if (static_cast<const std::function<void(
1219  const Iterator &, ScratchData &, CopyData &)> &>(worker))
1220  worker(*p, scratch_data, copy_data);
1221  if (static_cast<const std::function<void(const CopyData &)> &>(
1222  copier))
1223  copier(copy_data);
1224  }
1225  }
1226 # ifdef DEAL_II_WITH_THREADS
1227  else // have TBB and use more than one thread
1228  {
1229  // loop over the various colors of what we're given
1230  for (unsigned int color = 0; color < colored_iterators.size(); ++color)
1231  if (colored_iterators[color].size() > 0)
1232  {
1233  using WorkerAndCopier = internal::Implementation3::
1234  WorkerAndCopier<Iterator, ScratchData, CopyData>;
1235 
1236  using RangeType = typename std::vector<Iterator>::const_iterator;
1237 
1238  WorkerAndCopier worker_and_copier(worker,
1239  copier,
1240  sample_scratch_data,
1241  sample_copy_data);
1242 
1244  colored_iterators[color].begin(),
1245  colored_iterators[color].end(),
1246  [&worker_and_copier](
1247  const tbb::blocked_range<
1248  typename std::vector<Iterator>::const_iterator> &range) {
1249  worker_and_copier(range);
1250  },
1251  chunk_size);
1252  }
1253  }
1254 # endif
1255  }
1256 
1257 
1258 
1300  template <typename MainClass,
1301  typename Iterator,
1302  typename ScratchData,
1303  typename CopyData>
1304  void
1305  run(const Iterator & begin,
1306  const typename identity<Iterator>::type &end,
1307  MainClass & main_object,
1308  void (MainClass::*worker)(const Iterator &, ScratchData &, CopyData &),
1309  void (MainClass::*copier)(const CopyData &),
1310  const ScratchData &sample_scratch_data,
1311  const CopyData & sample_copy_data,
1312  const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1313  const unsigned int chunk_size = 8)
1314  {
1315  // forward to the other function
1316  run(begin,
1317  end,
1318  [&main_object, worker](const Iterator &iterator,
1319  ScratchData & scratch_data,
1320  CopyData & copy_data) {
1321  (main_object.*worker)(iterator, scratch_data, copy_data);
1322  },
1323  [&main_object, copier](const CopyData &copy_data) {
1324  (main_object.*copier)(copy_data);
1325  },
1327  sample_copy_data,
1328  queue_length,
1329  chunk_size);
1330  }
1331 
1332 
1333  template <typename MainClass,
1334  typename Iterator,
1335  typename ScratchData,
1336  typename CopyData>
1337  void
1339  const IteratorOverIterators<typename identity<Iterator>::type> &end,
1340  MainClass &main_object,
1341  void (MainClass::*worker)(const Iterator &, ScratchData &, CopyData &),
1342  void (MainClass::*copier)(const CopyData &),
1343  const ScratchData &sample_scratch_data,
1344  const CopyData & sample_copy_data,
1345  const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1346  const unsigned int chunk_size = 8)
1347  {
1348  // forward to the other function
1349  run(begin,
1350  end,
1351  [&main_object, worker](const Iterator &iterator,
1352  ScratchData & scratch_data,
1353  CopyData & copy_data) {
1354  (main_object.*worker)(iterator, scratch_data, copy_data);
1355  },
1356  [&main_object, copier](const CopyData &copy_data) {
1357  (main_object.*copier)(copy_data);
1358  },
1360  sample_copy_data,
1361  queue_length,
1362  chunk_size);
1363  }
1364 
1365 
1366 
1374  template <typename MainClass,
1375  typename IteratorRangeType,
1376  typename ScratchData,
1377  typename CopyData,
1378  typename = typename std::enable_if<
1380  void
1381  run(IteratorRangeType iterator_range,
1382  MainClass & main_object,
1383  void (MainClass::*worker)(
1385  ScratchData &,
1386  CopyData &),
1387  void (MainClass::*copier)(const CopyData &),
1388  const ScratchData &sample_scratch_data,
1389  const CopyData & sample_copy_data,
1390  const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1391  const unsigned int chunk_size = 8)
1392  {
1393  // Call the function above
1394  run(std::begin(iterator_range),
1395  std::end(iterator_range),
1396  main_object,
1397  worker,
1398  copier,
1399  sample_scratch_data,
1400  sample_copy_data,
1401  queue_length,
1402  chunk_size);
1403  }
1404 
1405 
1406 
1410  template <typename MainClass,
1411  typename Iterator,
1412  typename ScratchData,
1413  typename CopyData>
1414  void
1416  MainClass & main_object,
1417  void (MainClass::*worker)(const Iterator &, ScratchData &, CopyData &),
1418  void (MainClass::*copier)(const CopyData &),
1419  const ScratchData &sample_scratch_data,
1420  const CopyData & sample_copy_data,
1421  const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1422  const unsigned int chunk_size = 8)
1423  {
1424  // Call the function above
1425  run(std::begin(iterator_range),
1426  std::end(iterator_range),
1427  main_object,
1428  worker,
1429  copier,
1430  sample_scratch_data,
1431  sample_copy_data,
1432  queue_length,
1433  chunk_size);
1434  }
1435 
1436 } // namespace WorkStream
1437 
1438 
1439 
1441 
1442 
1443 
1444 //---------------------------- work_stream.h ---------------------------
1445 // end of #ifndef dealii_work_stream_h
1446 #endif
1447 //---------------------------- work_stream.h ---------------------------
void * operator()(void *item) override
Definition: work_stream.h:627
typename Implementation3::ScratchAndCopyDataObjects< Iterator, ScratchData, CopyData > ScratchAndCopyDataObjects
Definition: work_stream.h:850
const std::function< void(const Iterator &, ScratchData &, CopyData &)> worker
Definition: work_stream.h:591
const std::function< void(const CopyData &)> copier
Definition: work_stream.h:871
std::list< ScratchAndCopyDataObjects > ScratchAndCopyDataList
Definition: work_stream.h:856
IteratorOverIterators begin()
ScratchAndCopyDataObjects(ScratchData *p, CopyData *q, const bool in_use)
Definition: work_stream.h:705
void parallel_for(Iterator x_begin, Iterator x_end, const Functor &functor, const unsigned int grainsize)
Definition: parallel.h:156
const std::function< void(const Iterator &, ScratchData &, CopyData &)> worker
Definition: work_stream.h:865
ScratchAndCopyDataObjects(const ScratchAndCopyDataObjects &o)
Definition: work_stream.h:722
void * operator()(void *item) override
Definition: work_stream.h:482
void operator()(const tbb::blocked_range< typename std::vector< Iterator >::const_iterator > &range)
Definition: work_stream.h:761
Threads::ThreadLocalStorage< ScratchDataList > * scratch_data
Definition: work_stream.h:275
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:308
static ::ExceptionBase & ExcMessage(std::string arg1)
#define Assert(cond, exc)
Definition: exceptions.h:1419
#define DEAL_II_NAMESPACE_CLOSE
Definition: config.h:359
VectorType::value_type * end(VectorType &V)
Worker(const std::function< void(const Iterator &, ScratchData &, CopyData &)> &worker, bool copier_exist=true)
Definition: work_stream.h:468
MatrixTableIterators::Iterator< TransposeTable< T >, Constness, MatrixTableIterators::Storage::column_major > Iterator
Definition: table.h:1913
#define DEAL_II_NAMESPACE_OPEN
Definition: config.h:358
VectorType::value_type * begin(VectorType &V)
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:743
const std::function< void(const CopyData &)> copier
Definition: work_stream.h:671
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:1185
static unsigned int n_threads()
Threads::ThreadLocalStorage< typename ItemType::ScratchDataList > thread_local_scratch
Definition: work_stream.h:434
void handle_std_exception(const std::exception &exc)
Copier(const std::function< void(const CopyData &)> &copier)
Definition: work_stream.h:617
Threads::ThreadLocalStorage< ScratchAndCopyDataList > data
Definition: work_stream.h:858
static ::ExceptionBase & ExcInternalError()
IteratorOverIterators end() const