Reference documentation for deal.II version 9.1.0-pre
work_stream.h
1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 2008 - 2018 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 
22 # include <deal.II/base/graph_coloring.h>
23 # include <deal.II/base/multithread_info.h>
24 # include <deal.II/base/parallel.h>
25 # include <deal.II/base/template_constraints.h>
26 # include <deal.II/base/thread_local_storage.h>
27 # include <deal.II/base/thread_management.h>
28 
29 # ifdef DEAL_II_WITH_THREADS
30 # include <deal.II/base/thread_management.h>
31 
32 # include <tbb/pipeline.h>
33 # endif
34 
35 # include <functional>
36 # include <memory>
37 # include <utility>
38 # include <vector>
39 
40 DEAL_II_NAMESPACE_OPEN
41 
42 
43 
147 namespace WorkStream
148 {
149 # ifdef DEAL_II_WITH_THREADS
150 
151  namespace internal
152  {
153  // TODO: The following classes all use std::shared_ptr, but the
154  // correct pointer class would actually be std::unique_ptr. make this
155  // replacement whenever we have a class that provides these semantics
156  // and that is available also as a fall-back whenever via boost or similar
157 
170  namespace Implementation2
171  {
175  template <typename Iterator, typename ScratchData, typename CopyData>
176  class IteratorRangeToItemStream : public tbb::filter
177  {
178  public:
185  struct ItemType
186  {
193  {
194  std::shared_ptr<ScratchData> scratch_data;
195  bool currently_in_use;
196 
201  : currently_in_use(false)
202  {}
203 
204  ScratchDataObject(ScratchData *p, const bool in_use)
205  : scratch_data(p)
206  , currently_in_use(in_use)
207  {}
208 
209  // TODO: when we push back an object to the list of scratch objects,
210  // in
211  // Worker::operator(), we first create an object and then copy
212  // it to the end of this list. this involves having two objects
213  // of the current type having pointers to it, each with their
214  // own currently_in_use flag. there is probably little harm in
215  // this because the original one goes out of scope right away
216  // again, but it's certainly awkward. one way to avoid this
217  // would be to use unique_ptr but we'd need to figure out a way
218  // to use it in non-C++11 mode
220  : scratch_data(o.scratch_data)
221  , currently_in_use(o.currently_in_use)
222  {}
223  };
224 
225 
230  using ScratchDataList = std::list<ScratchDataObject>;
231 
236  std::vector<Iterator> work_items;
237 
243  std::vector<CopyData> copy_datas;
244 
250  unsigned int n_items;
251 
284 
289  const ScratchData *sample_scratch_data;
290 
296 
297 
303  : n_items(0)
304  , scratch_data(nullptr)
305  , sample_scratch_data(nullptr)
306  , currently_in_use(false)
307  {}
308  };
309 
310 
316  IteratorRangeToItemStream(const Iterator & begin,
317  const Iterator & end,
318  const unsigned int buffer_size,
319  const unsigned int chunk_size,
320  const ScratchData &sample_scratch_data,
321  const CopyData & sample_copy_data)
322  : tbb::filter(/*is_serial=*/true)
323  , remaining_iterator_range(begin, end)
324  , item_buffer(buffer_size)
325  , sample_scratch_data(sample_scratch_data)
326  , chunk_size(chunk_size)
327  {
328  // initialize the elements of the ring buffer
329  for (unsigned int element = 0; element < item_buffer.size();
330  ++element)
331  {
332  Assert(item_buffer[element].n_items == 0, ExcInternalError());
333 
334  item_buffer[element].work_items.resize(
335  chunk_size, remaining_iterator_range.second);
336  item_buffer[element].scratch_data = &thread_local_scratch;
337  item_buffer[element].sample_scratch_data = &sample_scratch_data;
338  item_buffer[element].copy_datas.resize(chunk_size,
339  sample_copy_data);
340  item_buffer[element].currently_in_use = false;
341  }
342  }
343 
344 
348  virtual void *
349  operator()(void *) override
350  {
351  // find first unused item. we know that there must be one
352  // because we have set the maximal number of tokens in flight
353  // and have set the ring buffer to have exactly this size. so
354  // if this function is called, we know that less than the
355  // maximal number of items in currently in flight
356  //
357  // note that we need not lock access to this array since
358  // the current stage is run sequentially and we can therefore
359  // enter the following block only once at any given time.
360  // thus, there can be no race condition between checking that
361  // a flag is false and setting it to true. (there may be
362  // another thread where we release items and set 'false'
363  // flags to 'true', but that too does not produce any
364  // problems)
365  ItemType *current_item = nullptr;
366  for (unsigned int i = 0; i < item_buffer.size(); ++i)
367  if (item_buffer[i].currently_in_use == false)
368  {
369  item_buffer[i].currently_in_use = true;
370  current_item = &item_buffer[i];
371  break;
372  }
373  Assert(current_item != nullptr,
374  ExcMessage("This can't be. There must be a free item!"));
375 
376  // initialize the next item. it may
377  // consist of at most chunk_size
378  // elements
379  current_item->n_items = 0;
380  while ((remaining_iterator_range.first !=
381  remaining_iterator_range.second) &&
382  (current_item->n_items < chunk_size))
383  {
384  current_item->work_items[current_item->n_items] =
386 
387  ++remaining_iterator_range.first;
388  ++current_item->n_items;
389  }
390 
391  if (current_item->n_items == 0)
392  // there were no items
393  // left. terminate the pipeline
394  return nullptr;
395  else
396  return current_item;
397  }
398 
399  private:
404  std::pair<Iterator, Iterator> remaining_iterator_range;
405 
409  std::vector<ItemType> item_buffer;
410 
443 
449  const ScratchData &sample_scratch_data;
450 
457  const unsigned int chunk_size;
458  };
459 
460 
461 
467  template <typename Iterator, typename ScratchData, typename CopyData>
468  class Worker : public tbb::filter
469  {
470  public:
477  const std::function<void(const Iterator &, ScratchData &, CopyData &)>
478  & worker,
479  bool copier_exist = true)
480  : tbb::filter(/* is_serial= */ false)
481  , worker(worker)
482  , copier_exist(copier_exist)
483  {}
484 
485 
489  void *
490  operator()(void *item) override
491  {
492  // first unpack the current item
493  using ItemType =
494  typename IteratorRangeToItemStream<Iterator,
495  ScratchData,
496  CopyData>::ItemType;
497 
498  ItemType *current_item = static_cast<ItemType *>(item);
499 
500  // we need to find an unused scratch data object in the list that
501  // corresponds to the current thread and then mark it as used. if
502  // we can't find one, create one
503  //
504  // as discussed in the discussion of the documentation of the
505  // IteratorRangeToItemStream::scratch_data variable, there is no
506  // need to synchronize access to this variable using a mutex
507  // as long as we have no yield-point in between. this means that
508  // we can't take an iterator into the list now and expect it to
509  // still be valid after calling the worker, but we at least do
510  // not have to lock the following section
511  ScratchData *scratch_data = nullptr;
512  {
513  typename ItemType::ScratchDataList &scratch_data_list =
514  current_item->scratch_data->get();
515 
516  // see if there is an unused object. if so, grab it and mark
517  // it as used
518  for (typename ItemType::ScratchDataList::iterator p =
519  scratch_data_list.begin();
520  p != scratch_data_list.end();
521  ++p)
522  if (p->currently_in_use == false)
523  {
524  scratch_data = p->scratch_data.get();
525  p->currently_in_use = true;
526  break;
527  }
528 
529  // if no object was found, create one and mark it as used
530  if (scratch_data == nullptr)
531  {
532  scratch_data =
533  new ScratchData(*current_item->sample_scratch_data);
534 
535  typename ItemType::ScratchDataList::value_type
536  new_scratch_object(scratch_data, true);
537  scratch_data_list.push_back(new_scratch_object);
538  }
539  }
540 
541  // then call the worker function on each element of the chunk we were
542  // given. since these worker functions are called on separate threads,
543  // nothing good can happen if they throw an exception and we are best
544  // off catching it and showing an error message
545  for (unsigned int i = 0; i < current_item->n_items; ++i)
546  {
547  try
548  {
549  if (worker)
550  worker(current_item->work_items[i],
551  *scratch_data,
552  current_item->copy_datas[i]);
553  }
554  catch (const std::exception &exc)
555  {
556  Threads::internal::handle_std_exception(exc);
557  }
558  catch (...)
559  {
560  Threads::internal::handle_unknown_exception();
561  }
562  }
563 
564  // finally mark the scratch object as unused again. as above, there
565  // is no need to lock anything here since the object we work on
566  // is thread-local
567  {
568  typename ItemType::ScratchDataList &scratch_data_list =
569  current_item->scratch_data->get();
570 
571  for (typename ItemType::ScratchDataList::iterator p =
572  scratch_data_list.begin();
573  p != scratch_data_list.end();
574  ++p)
575  if (p->scratch_data.get() == scratch_data)
576  {
577  Assert(p->currently_in_use == true, ExcInternalError());
578  p->currently_in_use = false;
579  }
580  }
581 
582  // if there is no copier, mark current item as usable again
583  if (copier_exist == false)
584  current_item->currently_in_use = false;
585 
586 
587  // then return the original pointer
588  // to the now modified object
589  return item;
590  }
591 
592 
593  private:
598  const std::function<void(const Iterator &, ScratchData &, CopyData &)>
600 
606  };
607 
608 
609 
615  template <typename Iterator, typename ScratchData, typename CopyData>
616  class Copier : public tbb::filter
617  {
618  public:
625  Copier(const std::function<void(const CopyData &)> &copier)
626  : tbb::filter(/*is_serial=*/true)
627  , copier(copier)
628  {}
629 
630 
634  void *
635  operator()(void *item) override
636  {
637  // first unpack the current item
638  using ItemType =
639  typename IteratorRangeToItemStream<Iterator,
640  ScratchData,
641  CopyData>::ItemType;
642 
643  ItemType *current_item = static_cast<ItemType *>(item);
644 
645  // initiate copying data. for the same reasons as in the worker class
646  // above, catch exceptions rather than letting it propagate into
647  // unknown territories
648  for (unsigned int i = 0; i < current_item->n_items; ++i)
649  {
650  try
651  {
652  if (copier)
653  copier(current_item->copy_datas[i]);
654  }
655  catch (const std::exception &exc)
656  {
657  Threads::internal::handle_std_exception(exc);
658  }
659  catch (...)
660  {
661  Threads::internal::handle_unknown_exception();
662  }
663  }
664 
665  // mark current item as usable again
666  current_item->currently_in_use = false;
667 
668 
669  // return an invalid item since we are at the end of the
670  // pipeline
671  return nullptr;
672  }
673 
674 
675  private:
679  const std::function<void(const CopyData &)> copier;
680  };
681 
682  } // namespace Implementation2
683 
684 
692  namespace Implementation3
693  {
699  template <typename Iterator, typename ScratchData, typename CopyData>
701  {
702  std::shared_ptr<ScratchData> scratch_data;
703  std::shared_ptr<CopyData> copy_data;
704  bool currently_in_use;
705 
710  : currently_in_use(false)
711  {}
712 
713  ScratchAndCopyDataObjects(ScratchData *p,
714  CopyData * q,
715  const bool in_use)
716  : scratch_data(p)
717  , copy_data(q)
718  , currently_in_use(in_use)
719  {}
720 
721  // TODO: when we push back an object to the list of scratch objects, in
722  // Worker::operator(), we first create an object and then copy
723  // it to the end of this list. this involves having two objects
724  // of the current type having pointers to it, each with their own
725  // currently_in_use flag. there is probably little harm in this
726  // because the original one goes out of scope right away again, but
727  // it's certainly awkward. one way to avoid this would be to use
728  // unique_ptr but we'd need to figure out a way to use it in
729  // non-C++11 mode
731  : scratch_data(o.scratch_data)
732  , copy_data(o.copy_data)
733  , currently_in_use(o.currently_in_use)
734  {}
735  };
736 
737 
738 
744  template <typename Iterator, typename ScratchData, typename CopyData>
746  {
747  public:
752  const std::function<void(const Iterator &, ScratchData &, CopyData &)>
753  & worker,
754  const std::function<void(const CopyData &)> &copier,
755  const ScratchData & sample_scratch_data,
756  const CopyData & sample_copy_data)
757  : worker(worker)
758  , copier(copier)
759  , sample_scratch_data(sample_scratch_data)
760  , sample_copy_data(sample_copy_data)
761  {}
762 
763 
768  void
769  operator()(const tbb::blocked_range<
770  typename std::vector<Iterator>::const_iterator> &range)
771  {
772  // we need to find an unused scratch and corresponding copy
773  // data object in the list that corresponds to the current
774  // thread and then mark it as used. If we can't find one,
775  // create one as discussed in the discussion of the documentation
776  // of the IteratorRangeToItemStream::scratch_data variable,
777  // there is no need to synchronize access to this variable
778  // using a mutex as long as we have no yield-point in between.
779  // This means that we can't take an iterator into the list
780  // now and expect it to still be valid after calling the worker,
781  // but we at least do not have to lock the following section.
782  ScratchData *scratch_data = nullptr;
783  CopyData * copy_data = nullptr;
784  {
785  ScratchAndCopyDataList &scratch_and_copy_data_list = data.get();
786 
787  // see if there is an unused object. if so, grab it and mark
788  // it as used
789  for (typename ScratchAndCopyDataList::iterator p =
790  scratch_and_copy_data_list.begin();
791  p != scratch_and_copy_data_list.end();
792  ++p)
793  if (p->currently_in_use == false)
794  {
795  scratch_data = p->scratch_data.get();
796  copy_data = p->copy_data.get();
797  p->currently_in_use = true;
798  break;
799  }
800 
801  // if no element in the list was found, create one and mark it as
802  // used
803  if (scratch_data == nullptr)
804  {
805  Assert(copy_data == nullptr, ExcInternalError());
806  scratch_data = new ScratchData(sample_scratch_data);
807  copy_data = new CopyData(sample_copy_data);
808 
809  scratch_and_copy_data_list.emplace_back(scratch_data,
810  copy_data,
811  true);
812  }
813  }
814 
815  // then call the worker and copier functions on each
816  // element of the chunk we were given.
817  for (typename std::vector<Iterator>::const_iterator p = range.begin();
818  p != range.end();
819  ++p)
820  {
821  try
822  {
823  if (worker)
824  worker(*p, *scratch_data, *copy_data);
825  if (copier)
826  copier(*copy_data);
827  }
828  catch (const std::exception &exc)
829  {
830  Threads::internal::handle_std_exception(exc);
831  }
832  catch (...)
833  {
834  Threads::internal::handle_unknown_exception();
835  }
836  }
837 
838  // finally mark the scratch object as unused again. as above, there
839  // is no need to lock anything here since the object we work on
840  // is thread-local
841  {
842  ScratchAndCopyDataList &scratch_and_copy_data_list = data.get();
843 
844  for (typename ScratchAndCopyDataList::iterator p =
845  scratch_and_copy_data_list.begin();
846  p != scratch_and_copy_data_list.end();
847  ++p)
848  if (p->scratch_data.get() == scratch_data)
849  {
850  Assert(p->currently_in_use == true, ExcInternalError());
851  p->currently_in_use = false;
852  }
853  }
854  }
855 
856  private:
857  using ScratchAndCopyDataObjects = typename Implementation3::
859 
864  using ScratchAndCopyDataList = std::list<ScratchAndCopyDataObjects>;
865 
867 
872  const std::function<void(const Iterator &, ScratchData &, CopyData &)>
874 
879  const std::function<void(const CopyData &)> copier;
880 
884  const ScratchData &sample_scratch_data;
885  const CopyData & sample_copy_data;
886  };
887  } // namespace Implementation3
888 
889  } // namespace internal
890 
891 
892 # endif // DEAL_II_WITH_THREADS
893 
894 
929  template <typename Worker,
930  typename Copier,
931  typename Iterator,
932  typename ScratchData,
933  typename CopyData>
934  void
935  run(const std::vector<std::vector<Iterator>> &colored_iterators,
936  Worker worker,
937  Copier copier,
938  const ScratchData & sample_scratch_data,
939  const CopyData & sample_copy_data,
940  const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
941  const unsigned int chunk_size = 8);
942 
943 
978  template <typename Worker,
979  typename Copier,
980  typename Iterator,
981  typename ScratchData,
982  typename CopyData>
983  void
984  run(const Iterator & begin,
985  const typename identity<Iterator>::type &end,
986  Worker worker,
987  Copier copier,
988  const ScratchData & sample_scratch_data,
989  const CopyData & sample_copy_data,
990  const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
991  const unsigned int chunk_size = 8)
992  {
993  Assert(queue_length > 0,
994  ExcMessage("The queue length must be at least one, and preferably "
995  "larger than the number of processors on this system."));
996  (void)queue_length; // removes -Wunused-parameter warning in optimized mode
997  Assert(chunk_size > 0, ExcMessage("The chunk_size must be at least one."));
998  (void)chunk_size; // removes -Wunused-parameter warning in optimized mode
999 
1000  // if no work then skip. (only use operator!= for iterators since we may
1001  // not have an equality comparison operator)
1002  if (!(begin != end))
1003  return;
1004 
1005  // we want to use TBB if we have support and if it is not disabled at
1006  // runtime:
1007 # ifdef DEAL_II_WITH_THREADS
1008  if (MultithreadInfo::n_threads() == 1)
1009 # endif
1010  {
1011  // need to copy the sample since it is marked const
1012  ScratchData scratch_data = sample_scratch_data;
1013  CopyData copy_data = sample_copy_data; // NOLINT
1014 
1015  for (Iterator i = begin; i != end; ++i)
1016  {
1017  // need to check if the function is not the zero function. To
1018  // check zero-ness, create a C++ function out of it and check that
1019  if (static_cast<const std::function<
1020  void(const Iterator &, ScratchData &, CopyData &)> &>(worker))
1021  worker(i, scratch_data, copy_data);
1022  if (static_cast<const std::function<void(const CopyData &)> &>(
1023  copier))
1024  copier(copy_data);
1025  }
1026  }
1027 # ifdef DEAL_II_WITH_THREADS
1028  else // have TBB and use more than one thread
1029  {
1030  // Check that the copier exist
1031  if (static_cast<const std::function<void(const CopyData &)> &>(copier))
1032  {
1033  // create the three stages of the pipeline
1036  iterator_range_to_item_stream(begin,
1037  end,
1038  queue_length,
1039  chunk_size,
1040  sample_scratch_data,
1041  sample_copy_data);
1042 
1044  worker_filter(worker);
1046  copier_filter(copier);
1047 
1048  // now create a pipeline from these stages
1049  tbb::pipeline assembly_line;
1050  assembly_line.add_filter(iterator_range_to_item_stream);
1051  assembly_line.add_filter(worker_filter);
1052  assembly_line.add_filter(copier_filter);
1053 
1054  // and run it
1055  assembly_line.run(queue_length);
1056 
1057  assembly_line.clear();
1058  }
1059  else
1060  {
1061  // there is no copier function. in this case, we have an
1062  // embarrassingly parallel problem where we can
1063  // essentially apply parallel_for. because parallel_for
1064  // requires subdividing the range for which operator- is
1065  // necessary between iterators, it is often inefficient to
1066  // apply it directly to cell ranges and similar iterator
1067  // types for which operator- is expensive or, in fact,
1068  // nonexistent. rather, in that case, we simply copy the
1069  // iterators into a large array and use operator- on
1070  // iterators to this array of iterators.
1071  //
1072  // instead of duplicating code, this is essentially the
1073  // same situation we have in Implementation3 below, so we
1074  // just defer to that place
1075  std::vector<std::vector<Iterator>> all_iterators(1);
1076  for (Iterator p = begin; p != end; ++p)
1077  all_iterators[0].push_back(p);
1078 
1079  run(all_iterators,
1080  worker,
1081  copier,
1082  sample_scratch_data,
1083  sample_copy_data,
1084  queue_length,
1085  chunk_size);
1086  }
1087  }
1088 # endif
1089  }
1090 
1091 
1092  // Implementation 3:
1093  template <typename Worker,
1094  typename Copier,
1095  typename Iterator,
1096  typename ScratchData,
1097  typename CopyData>
1098  void
1099  run(const std::vector<std::vector<Iterator>> &colored_iterators,
1100  Worker worker,
1101  Copier copier,
1102  const ScratchData & sample_scratch_data,
1103  const CopyData & sample_copy_data,
1104  const unsigned int queue_length,
1105  const unsigned int chunk_size)
1106  {
1107  Assert(queue_length > 0,
1108  ExcMessage("The queue length must be at least one, and preferably "
1109  "larger than the number of processors on this system."));
1110  (void)queue_length; // removes -Wunused-parameter warning in optimized mode
1111  Assert(chunk_size > 0, ExcMessage("The chunk_size must be at least one."));
1112  (void)chunk_size; // removes -Wunused-parameter warning in optimized mode
1113 
1114  // we want to use TBB if we have support and if it is not disabled at
1115  // runtime:
1116 # ifdef DEAL_II_WITH_THREADS
1117  if (MultithreadInfo::n_threads() == 1)
1118 # endif
1119  {
1120  // need to copy the sample since it is marked const
1121  ScratchData scratch_data = sample_scratch_data;
1122  CopyData copy_data = sample_copy_data; // NOLINT
1123 
1124  for (unsigned int color = 0; color < colored_iterators.size(); ++color)
1125  for (typename std::vector<Iterator>::const_iterator p =
1126  colored_iterators[color].begin();
1127  p != colored_iterators[color].end();
1128  ++p)
1129  {
1130  // need to check if the function is not the zero function. To
1131  // check zero-ness, create a C++ function out of it and check that
1132  if (static_cast<const std::function<void(
1133  const Iterator &, ScratchData &, CopyData &)> &>(worker))
1134  worker(*p, scratch_data, copy_data);
1135  if (static_cast<const std::function<void(const CopyData &)> &>(
1136  copier))
1137  copier(copy_data);
1138  }
1139  }
1140 # ifdef DEAL_II_WITH_THREADS
1141  else // have TBB and use more than one thread
1142  {
1143  // loop over the various colors of what we're given
1144  for (unsigned int color = 0; color < colored_iterators.size(); ++color)
1145  if (colored_iterators[color].size() > 0)
1146  {
1147  using WorkerAndCopier = internal::Implementation3::
1149 
1150  using RangeType = typename std::vector<Iterator>::const_iterator;
1151 
1152  WorkerAndCopier worker_and_copier(worker,
1153  copier,
1154  sample_scratch_data,
1155  sample_copy_data);
1156 
1157  tbb::parallel_for(
1158  tbb::blocked_range<RangeType>(colored_iterators[color].begin(),
1159  colored_iterators[color].end(),
1160  /*grain_size=*/chunk_size),
1161  std::bind(&WorkerAndCopier::operator(),
1162  std::ref(worker_and_copier),
1163  std::placeholders::_1),
1164  tbb::auto_partitioner());
1165  }
1166  }
1167 # endif
1168  }
1169 
1170 
1171 
1201  template <typename MainClass,
1202  typename Iterator,
1203  typename ScratchData,
1204  typename CopyData>
1205  void
1206  run(const Iterator & begin,
1207  const typename identity<Iterator>::type &end,
1208  MainClass & main_object,
1209  void (MainClass::*worker)(const Iterator &, ScratchData &, CopyData &),
1210  void (MainClass::*copier)(const CopyData &),
1211  const ScratchData &sample_scratch_data,
1212  const CopyData & sample_copy_data,
1213  const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1214  const unsigned int chunk_size = 8)
1215  {
1216  // forward to the other function
1217  run(begin,
1218  end,
1219  std::bind(worker,
1220  std::ref(main_object),
1221  std::placeholders::_1,
1222  std::placeholders::_2,
1223  std::placeholders::_3),
1224  std::bind(copier, std::ref(main_object), std::placeholders::_1),
1225  sample_scratch_data,
1226  sample_copy_data,
1227  queue_length,
1228  chunk_size);
1229  }
1230 
1231 } // namespace WorkStream
1232 
1233 
1234 
1235 DEAL_II_NAMESPACE_CLOSE
1236 
1237 
1238 
1239 //---------------------------- work_stream.h ---------------------------
1240 // end of #ifndef dealii_work_stream_h
1241 #endif
1242 //---------------------------- work_stream.h ---------------------------
void * operator()(void *item) override
Definition: work_stream.h:635
const std::function< void(const Iterator &, ScratchData &, CopyData &)> worker
Definition: work_stream.h:599
const std::function< void(const CopyData &)> copier
Definition: work_stream.h:879
std::list< ScratchAndCopyDataObjects > ScratchAndCopyDataList
Definition: work_stream.h:864
const std::function< void(const Iterator &, ScratchData &, CopyData &)> worker
Definition: work_stream.h:873
void * operator()(void *item) override
Definition: work_stream.h:490
void operator()(const tbb::blocked_range< typename std::vector< Iterator >::const_iterator > &range)
Definition: work_stream.h:769
Threads::ThreadLocalStorage< ScratchDataList > * scratch_data
Definition: work_stream.h:283
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:316
static::ExceptionBase & ExcMessage(std::string arg1)
#define Assert(cond, exc)
Definition: exceptions.h:1227
Worker(const std::function< void(const Iterator &, ScratchData &, CopyData &)> &worker, bool copier_exist=true)
Definition: work_stream.h:476
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:751
const std::function< void(const CopyData &)> copier
Definition: work_stream.h:679
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:1099
static unsigned int n_threads()
Threads::ThreadLocalStorage< typename ItemType::ScratchDataList > thread_local_scratch
Definition: work_stream.h:442
Copier(const std::function< void(const CopyData &)> &copier)
Definition: work_stream.h:625
static::ExceptionBase & ExcInternalError()