Reference documentation for deal.II version 8.5.1
work_stream.h
1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 2008 - 2017 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 at
12 // the top level of the deal.II distribution.
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 #include <deal.II/base/graph_coloring.h>
22 #include <deal.II/base/multithread_info.h>
23 #include <deal.II/base/thread_management.h>
24 #include <deal.II/base/template_constraints.h>
25 #include <deal.II/base/std_cxx11/function.h>
26 #include <deal.II/base/std_cxx11/bind.h>
27 #include <deal.II/base/thread_local_storage.h>
28 #include <deal.II/base/parallel.h>
29 
30 #ifdef DEAL_II_WITH_THREADS
31 # include <deal.II/base/thread_management.h>
32 # include <tbb/pipeline.h>
33 #endif
34 
35 #include <vector>
36 #include <utility>
37 #include <memory>
38 
39 
40 DEAL_II_NAMESPACE_OPEN
41 
42 
43 
147 namespace WorkStream
148 {
149 
150 #ifdef DEAL_II_WITH_THREADS
151 
152  namespace internal
153  {
154 
155 //TODO: The following classes all use std_cxx11::shared_ptr, but the
156 // correct pointer class would actually be std::unique_ptr. make this
157 // replacement whenever we have a class that provides these semantics
158 // and that is available also as a fall-back whenever via boost or similar
159 
172  namespace Implementation2
173  {
177  template <typename Iterator,
178  typename ScratchData,
179  typename CopyData>
180  class IteratorRangeToItemStream : public tbb::filter
181  {
182  public:
189  struct ItemType
190  {
197  {
198  std_cxx11::shared_ptr<ScratchData> scratch_data;
199  bool currently_in_use;
200 
205  :
206  currently_in_use (false)
207  {}
208 
209  ScratchDataObject (ScratchData *p,
210  const bool in_use)
211  :
212  scratch_data (p),
213  currently_in_use (in_use)
214  {}
215 
216 //TODO: when we push back an object to the list of scratch objects, in
217 // Worker::operator(), we first create an object and then copy
218 // it to the end of this list. this involves having two objects
219 // of the current type having pointers to it, each with their own
220 // currently_in_use flag. there is probably little harm in this because
221 // the original one goes out of scope right away again, but it's
222 // certainly awkward. one way to avoid this would be to use unique_ptr
223 // but we'd need to figure out a way to use it in non-C++11 mode
225  :
226  scratch_data (o.scratch_data),
227  currently_in_use (o.currently_in_use)
228  {}
229  };
230 
231 
236  typedef std::list<ScratchDataObject> ScratchDataList;
237 
242  std::vector<Iterator> work_items;
243 
249  std::vector<CopyData> copy_datas;
250 
256  unsigned int n_items;
257 
290 
295  const ScratchData *sample_scratch_data;
296 
302 
303 
309  :
310  n_items (0),
311  scratch_data (0),
313  currently_in_use (false)
314  {}
315  };
316 
317 
323  IteratorRangeToItemStream (const Iterator &begin,
324  const Iterator &end,
325  const unsigned int buffer_size,
326  const unsigned int chunk_size,
327  const ScratchData &sample_scratch_data,
328  const CopyData &sample_copy_data)
329  :
330  tbb::filter (/*is_serial=*/true),
331  remaining_iterator_range (begin, end),
332  item_buffer (buffer_size),
335  {
336  // initialize the elements of the ring buffer
337  for (unsigned int element=0; element<item_buffer.size(); ++element)
338  {
339  Assert (item_buffer[element].n_items == 0,
340  ExcInternalError());
341 
342  item_buffer[element].work_items.resize (chunk_size,
343  remaining_iterator_range.second);
344  item_buffer[element].scratch_data = &thread_local_scratch;
345  item_buffer[element].sample_scratch_data = &sample_scratch_data;
346  item_buffer[element].copy_datas.resize (chunk_size,
347  sample_copy_data);
348  item_buffer[element].currently_in_use = false;
349  }
350  }
351 
352 
356  virtual void *operator () (void *)
357  {
358  // find first unused item. we know that there must be one
359  // because we have set the maximal number of tokens in flight
360  // and have set the ring buffer to have exactly this size. so
361  // if this function is called, we know that less than the
362  // maximal number of items in currently in flight
363  //
364  // note that we need not lock access to this array since
365  // the current stage is run sequentially and we can therefore
366  // enter the following block only once at any given time.
367  // thus, there can be no race condition between checking that
368  // a flag is false and setting it to true. (there may be
369  // another thread where we release items and set 'false'
370  // flags to 'true', but that too does not produce any
371  // problems)
372  ItemType *current_item = 0;
373  for (unsigned int i=0; i<item_buffer.size(); ++i)
374  if (item_buffer[i].currently_in_use == false)
375  {
376  item_buffer[i].currently_in_use = true;
377  current_item = &item_buffer[i];
378  break;
379  }
380  Assert (current_item != 0, ExcMessage ("This can't be. There must be a free item!"));
381 
382  // initialize the next item. it may
383  // consist of at most chunk_size
384  // elements
385  current_item->n_items = 0;
386  while ((remaining_iterator_range.first !=
388  &&
389  (current_item->n_items < chunk_size))
390  {
391  current_item->work_items[current_item->n_items]
392  = remaining_iterator_range.first;
393 
394  ++remaining_iterator_range.first;
395  ++current_item->n_items;
396  }
397 
398  if (current_item->n_items == 0)
399  // there were no items
400  // left. terminate the pipeline
401  return 0;
402  else
403  return current_item;
404  }
405 
406  private:
411  std::pair<Iterator,Iterator> remaining_iterator_range;
412 
416  std::vector<ItemType> item_buffer;
417 
449 
455  const ScratchData &sample_scratch_data;
456 
463  const unsigned int chunk_size;
464  };
465 
466 
467 
473  template <typename Iterator,
474  typename ScratchData,
475  typename CopyData>
476  class Worker : public tbb::filter
477  {
478  public:
484  Worker (const std_cxx11::function<void (const Iterator &,
485  ScratchData &,
486  CopyData &)> &worker,
487  bool copier_exist=true)
488  :
489  tbb::filter (/* is_serial= */ false),
490  worker (worker),
492  {}
493 
494 
498  void *operator () (void *item)
499  {
500  // first unpack the current item
501  typedef
503  ItemType;
504 
505  ItemType *current_item = static_cast<ItemType *> (item);
506 
507  // we need to find an unused scratch data object in the list that
508  // corresponds to the current thread and then mark it as used. if
509  // we can't find one, create one
510  //
511  // as discussed in the discussion of the documentation of the
512  // IteratorRangeToItemStream::scratch_data variable, there is no
513  // need to synchronize access to this variable using a mutex
514  // as long as we have no yield-point in between. this means that
515  // we can't take an iterator into the list now and expect it to
516  // still be valid after calling the worker, but we at least do
517  // not have to lock the following section
518  ScratchData *scratch_data = 0;
519  {
520  typename ItemType::ScratchDataList &
521  scratch_data_list = current_item->scratch_data->get();
522 
523  // see if there is an unused object. if so, grab it and mark
524  // it as used
525  for (typename ItemType::ScratchDataList::iterator
526  p = scratch_data_list.begin();
527  p != scratch_data_list.end(); ++p)
528  if (p->currently_in_use == false)
529  {
530  scratch_data = p->scratch_data.get();
531  p->currently_in_use = true;
532  break;
533  }
534 
535  // if no object was found, create one and mark it as used
536  if (scratch_data == 0)
537  {
538  scratch_data = new ScratchData(*current_item->sample_scratch_data);
539 
540  typename ItemType::ScratchDataList::value_type
541  new_scratch_object (scratch_data, true);
542  scratch_data_list.push_back (new_scratch_object);
543  }
544  }
545 
546  // then call the worker function on each element of the chunk we were
547  // given. since these worker functions are called on separate threads,
548  // nothing good can happen if they throw an exception and we are best
549  // off catching it and showing an error message
550  for (unsigned int i=0; i<current_item->n_items; ++i)
551  {
552  try
553  {
554  if (worker)
555  worker (current_item->work_items[i],
556  *scratch_data,
557  current_item->copy_datas[i]);
558  }
559  catch (const std::exception &exc)
560  {
561  Threads::internal::handle_std_exception (exc);
562  }
563  catch (...)
564  {
565  Threads::internal::handle_unknown_exception ();
566  }
567  }
568 
569  // finally mark the scratch object as unused again. as above, there
570  // is no need to lock anything here since the object we work on
571  // is thread-local
572  {
573  typename ItemType::ScratchDataList &
574  scratch_data_list = current_item->scratch_data->get();
575 
576  for (typename ItemType::ScratchDataList::iterator p =
577  scratch_data_list.begin(); p != scratch_data_list.end();
578  ++p)
579  if (p->scratch_data.get() == scratch_data)
580  {
581  Assert(p->currently_in_use == true, ExcInternalError());
582  p->currently_in_use = false;
583  }
584  }
585 
586  // if there is no copier, mark current item as usable again
587  if (copier_exist==false)
588  current_item->currently_in_use = false;
589 
590 
591  // then return the original pointer
592  // to the now modified object
593  return item;
594  }
595 
596 
597  private:
602  const std_cxx11::function<void (const Iterator &,
603  ScratchData &,
604  CopyData &)> worker;
605 
611  };
612 
613 
614 
620  template <typename Iterator,
621  typename ScratchData,
622  typename CopyData>
623  class Copier : public tbb::filter
624  {
625  public:
632  Copier (const std_cxx11::function<void (const CopyData &)> &copier)
633  :
634  tbb::filter (/*is_serial=*/true),
635  copier (copier)
636  {}
637 
638 
642  void *operator () (void *item)
643  {
644  // first unpack the current item
645  typedef
647  ItemType;
648 
649  ItemType *current_item = static_cast<ItemType *> (item);
650 
651  // initiate copying data. for the same reasons as in the worker class
652  // above, catch exceptions rather than letting it propagate into
653  // unknown territories
654  for (unsigned int i=0; i<current_item->n_items; ++i)
655  {
656  try
657  {
658  if (copier)
659  copier (current_item->copy_datas[i]);
660  }
661  catch (const std::exception &exc)
662  {
663  Threads::internal::handle_std_exception (exc);
664  }
665  catch (...)
666  {
667  Threads::internal::handle_unknown_exception ();
668  }
669  }
670 
671  // mark current item as usable again
672  current_item->currently_in_use = false;
673 
674 
675  // return an invalid item since we are at the end of the
676  // pipeline
677  return 0;
678  }
679 
680 
681  private:
685  const std_cxx11::function<void (const CopyData &)> copier;
686  };
687 
688  }
689 
690 
698  namespace Implementation3
699  {
705  template <typename Iterator,
706  typename ScratchData,
707  typename CopyData>
709  {
710  std_cxx11::shared_ptr<ScratchData> scratch_data;
711  std_cxx11::shared_ptr<CopyData> copy_data;
712  bool currently_in_use;
713 
718  :
719  currently_in_use (false)
720  {}
721 
722  ScratchAndCopyDataObjects (ScratchData *p,
723  CopyData *q,
724  const bool in_use)
725  :
726  scratch_data (p),
727  copy_data (q),
728  currently_in_use (in_use)
729  {}
730 
731 //TODO: when we push back an object to the list of scratch objects, in
732 // Worker::operator(), we first create an object and then copy
733 // it to the end of this list. this involves having two objects
734 // of the current type having pointers to it, each with their own
735 // currently_in_use flag. there is probably little harm in this because
736 // the original one goes out of scope right away again, but it's
737 // certainly awkward. one way to avoid this would be to use unique_ptr
738 // but we'd need to figure out a way to use it in non-C++11 mode
740  :
741  scratch_data (o.scratch_data),
742  copy_data (o.copy_data),
743  currently_in_use (o.currently_in_use)
744  {}
745  };
746 
747 
748 
749 
750 
756  template <typename Iterator,
757  typename ScratchData,
758  typename CopyData>
760  {
761  public:
765  WorkerAndCopier (const std_cxx11::function<void (const Iterator &,
766  ScratchData &,
767  CopyData &)> &worker,
768  const std_cxx11::function<void (const CopyData &)> &copier,
769  const ScratchData &sample_scratch_data,
770  const CopyData &sample_copy_data)
771  :
772  worker (worker),
773  copier (copier),
775  sample_copy_data (sample_copy_data)
776  {}
777 
778 
783  void operator() (const tbb::blocked_range<typename std::vector<Iterator>::const_iterator> &range)
784  {
785  // we need to find an unused scratch and corresponding copy
786  // data object in the list that corresponds to the current
787  // thread and then mark it as used. If we can't find one,
788  // create one as discussed in the discussion of the documentation
789  // of the IteratorRangeToItemStream::scratch_data variable,
790  // there is no need to synchronize access to this variable
791  // using a mutex as long as we have no yield-point in between.
792  // This means that we can't take an iterator into the list
793  // now and expect it to still be valid after calling the worker,
794  // but we at least do not have to lock the following section.
795  ScratchData *scratch_data = 0;
796  CopyData *copy_data = 0;
797  {
798  ScratchAndCopyDataList &scratch_and_copy_data_list = data.get();
799 
800  // see if there is an unused object. if so, grab it and mark
801  // it as used
802  for (typename ScratchAndCopyDataList::iterator
803  p = scratch_and_copy_data_list.begin();
804  p != scratch_and_copy_data_list.end(); ++p)
805  if (p->currently_in_use == false)
806  {
807  scratch_data = p->scratch_data.get();
808  copy_data = p->copy_data.get();
809  p->currently_in_use = true;
810  break;
811  }
812 
813  // if no element in the list was found, create one and mark it as used
814  if (scratch_data == 0)
815  {
816  Assert (copy_data==0, ExcInternalError());
817  scratch_data = new ScratchData(sample_scratch_data);
818  copy_data = new CopyData(sample_copy_data);
819 
820  typename ScratchAndCopyDataList::value_type
821  new_scratch_object (scratch_data, copy_data, true);
822  scratch_and_copy_data_list.push_back (new_scratch_object);
823  }
824  }
825 
826  // then call the worker and copier functions on each
827  // element of the chunk we were given.
828  for (typename std::vector<Iterator>::const_iterator p=range.begin();
829  p != range.end(); ++p)
830  {
831  try
832  {
833  if (worker)
834  worker (*p,
835  *scratch_data,
836  *copy_data);
837  if (copier)
838  copier (*copy_data);
839  }
840  catch (const std::exception &exc)
841  {
842  Threads::internal::handle_std_exception (exc);
843  }
844  catch (...)
845  {
846  Threads::internal::handle_unknown_exception ();
847  }
848  }
849 
850  // finally mark the scratch object as unused again. as above, there
851  // is no need to lock anything here since the object we work on
852  // is thread-local
853  {
854  ScratchAndCopyDataList &scratch_and_copy_data_list = data.get();
855 
856  for (typename ScratchAndCopyDataList::iterator p =
857  scratch_and_copy_data_list.begin(); p != scratch_and_copy_data_list.end();
858  ++p)
859  if (p->scratch_data.get() == scratch_data)
860  {
861  Assert(p->currently_in_use == true, ExcInternalError());
862  p->currently_in_use = false;
863  }
864  }
865 
866  }
867 
868  private:
869  typedef
872 
877  typedef std::list<ScratchAndCopyDataObjects> ScratchAndCopyDataList;
878 
880 
885  const std_cxx11::function<void (const Iterator &,
886  ScratchData &,
887  CopyData &)> worker;
888 
893  const std_cxx11::function<void (const CopyData &)> copier;
894 
898  const ScratchData &sample_scratch_data;
899  const CopyData &sample_copy_data;
900  };
901  }
902 
903  }
904 
905 
906 #endif // DEAL_II_WITH_THREADS
907 
908 
943  template <typename Worker,
944  typename Copier,
945  typename Iterator,
946  typename ScratchData,
947  typename CopyData>
948  void
949  run (const std::vector<std::vector<Iterator> > &colored_iterators,
950  Worker worker,
951  Copier copier,
952  const ScratchData &sample_scratch_data,
953  const CopyData &sample_copy_data,
954  const unsigned int queue_length = 2*MultithreadInfo::n_threads(),
955  const unsigned int chunk_size = 8);
956 
957 
992  template <typename Worker,
993  typename Copier,
994  typename Iterator,
995  typename ScratchData,
996  typename CopyData>
997  void
998  run (const Iterator &begin,
999  const typename identity<Iterator>::type &end,
1000  Worker worker,
1001  Copier copier,
1002  const ScratchData &sample_scratch_data,
1003  const CopyData &sample_copy_data,
1004  const unsigned int queue_length = 2*MultithreadInfo::n_threads(),
1005  const unsigned int chunk_size = 8)
1006  {
1007  Assert (queue_length > 0,
1008  ExcMessage ("The queue length must be at least one, and preferably "
1009  "larger than the number of processors on this system."));
1010  (void)queue_length; // removes -Wunused-parameter warning in optimized mode
1011  Assert (chunk_size > 0,
1012  ExcMessage ("The chunk_size must be at least one."));
1013  (void)chunk_size; // removes -Wunused-parameter warning in optimized mode
1014 
1015  // if no work then skip. (only use operator!= for iterators since we may
1016  // not have an equality comparison operator)
1017  if (!(begin != end))
1018  return;
1019 
1020  // we want to use TBB if we have support and if it is not disabled at
1021  // runtime:
1022 #ifdef DEAL_II_WITH_THREADS
1023  if (MultithreadInfo::n_threads()==1)
1024 #endif
1025  {
1026  // need to copy the sample since it is marked const
1027  ScratchData scratch_data = sample_scratch_data;
1028  CopyData copy_data = sample_copy_data;
1029 
1030  for (Iterator i=begin; i!=end; ++i)
1031  {
1032  // need to check if the function is not the zero function. To
1033  // check zero-ness, create a C++ function out of it and check that
1034  if (static_cast<const std_cxx11::function<void (const Iterator &,
1035  ScratchData &,
1036  CopyData &)>& >(worker))
1037  worker (i, scratch_data, copy_data);
1038  if (static_cast<const std_cxx11::function<void (const CopyData &)>& >
1039  (copier))
1040  copier (copy_data);
1041  }
1042  }
1043 #ifdef DEAL_II_WITH_THREADS
1044  else // have TBB and use more than one thread
1045  {
1046  // Check that the copier exist
1047  if (static_cast<const std_cxx11::function<void (const CopyData &)>& >(copier))
1048  {
1049  // create the three stages of the pipeline
1051  iterator_range_to_item_stream (begin, end,
1052  queue_length,
1053  chunk_size,
1054  sample_scratch_data,
1055  sample_copy_data);
1056 
1059 
1060  // now create a pipeline from these stages
1061  tbb::pipeline assembly_line;
1062  assembly_line.add_filter (iterator_range_to_item_stream);
1063  assembly_line.add_filter (worker_filter);
1064  assembly_line.add_filter (copier_filter);
1065 
1066  // and run it
1067  assembly_line.run (queue_length);
1068 
1069  assembly_line.clear ();
1070  }
1071  else
1072  {
1073  // there is no copier function. in this case, we have an
1074  // embarrassingly parallel problem where we can
1075  // essentially apply parallel_for. because parallel_for
1076  // requires subdividing the range for which operator- is
1077  // necessary between iterators, it is often inefficient to
1078  // apply it directory to cell ranges and similar iterator
1079  // types for which operator- is expensive or, in fact,
1080  // nonexistent. rather, in that case, we simply copy the
1081  // iterators into a large array and use operator- on
1082  // iterators to this array of iterators.
1083  //
1084  // instead of duplicating code, this is essentially the
1085  // same situation we have in Implementation3 below, so we
1086  // just defer to that place
1087  std::vector<std::vector<Iterator> > all_iterators (1);
1088  for (Iterator p=begin; p!=end; ++p)
1089  all_iterators[0].push_back (p);
1090 
1091  run (all_iterators,
1092  worker, copier,
1093  sample_scratch_data,
1094  sample_copy_data,
1095  queue_length,
1096  chunk_size);
1097  }
1098  }
1099 #endif
1100  }
1101 
1102 
1103  // Implementation 3:
1104  template <typename Worker,
1105  typename Copier,
1106  typename Iterator,
1107  typename ScratchData,
1108  typename CopyData>
1109  void
1110  run (const std::vector<std::vector<Iterator> > &colored_iterators,
1111  Worker worker,
1112  Copier copier,
1113  const ScratchData &sample_scratch_data,
1114  const CopyData &sample_copy_data,
1115  const unsigned int queue_length,
1116  const unsigned int chunk_size)
1117  {
1118  Assert (queue_length > 0,
1119  ExcMessage ("The queue length must be at least one, and preferably "
1120  "larger than the number of processors on this system."));
1121  (void)queue_length; // removes -Wunused-parameter warning in optimized mode
1122  Assert (chunk_size > 0,
1123  ExcMessage ("The chunk_size must be at least one."));
1124  (void)chunk_size; // removes -Wunused-parameter warning in optimized mode
1125 
1126  // we want to use TBB if we have support and if it is not disabled at
1127  // runtime:
1128 #ifdef DEAL_II_WITH_THREADS
1129  if (MultithreadInfo::n_threads()==1)
1130 #endif
1131  {
1132  // need to copy the sample since it is marked const
1133  ScratchData scratch_data = sample_scratch_data;
1134  CopyData copy_data = sample_copy_data;
1135 
1136  for (unsigned int color=0; color<colored_iterators.size(); ++color)
1137  for (typename std::vector<Iterator>::const_iterator p = colored_iterators[color].begin();
1138  p != colored_iterators[color].end(); ++p)
1139  {
1140  // need to check if the function is not the zero function. To
1141  // check zero-ness, create a C++ function out of it and check that
1142  if (static_cast<const std_cxx11::function<void (const Iterator &,
1143  ScratchData &,
1144  CopyData &)>& >(worker))
1145  worker (*p, scratch_data, copy_data);
1146  if (static_cast<const std_cxx11::function<void (const CopyData &)>& >(copier))
1147  copier (copy_data);
1148  }
1149  }
1150 #ifdef DEAL_II_WITH_THREADS
1151  else // have TBB and use more than one thread
1152  {
1153  // loop over the various colors of what we're given
1154  for (unsigned int color=0; color<colored_iterators.size(); ++color)
1155  if (colored_iterators[color].size() > 0)
1156  {
1157  typedef
1159  WorkerAndCopier;
1160 
1161  typedef
1162  typename std::vector<Iterator>::const_iterator
1163  RangeType;
1164 
1165  WorkerAndCopier worker_and_copier (worker,
1166  copier,
1167  sample_scratch_data,
1168  sample_copy_data);
1169 
1170  tbb::parallel_for (tbb::blocked_range<RangeType>
1171  (colored_iterators[color].begin(),
1172  colored_iterators[color].end(),
1173  /*grain_size=*/chunk_size),
1174  std_cxx11::bind (&WorkerAndCopier::operator(),
1175  std_cxx11::ref(worker_and_copier),
1176  std_cxx11::_1),
1177  tbb::auto_partitioner());
1178  }
1179  }
1180 #endif
1181  }
1182 
1183 
1184 
1185 
1186 
1216  template <typename MainClass,
1217  typename Iterator,
1218  typename ScratchData,
1219  typename CopyData>
1220  void
1221  run (const Iterator &begin,
1222  const typename identity<Iterator>::type &end,
1223  MainClass &main_object,
1224  void (MainClass::*worker) (const Iterator &,
1225  ScratchData &,
1226  CopyData &),
1227  void (MainClass::*copier) (const CopyData &),
1228  const ScratchData &sample_scratch_data,
1229  const CopyData &sample_copy_data,
1230  const unsigned int queue_length = 2*MultithreadInfo::n_threads(),
1231  const unsigned int chunk_size = 8)
1232  {
1233  // forward to the other function
1234  run (begin, end,
1235  std_cxx11::bind (worker,
1236  std_cxx11::ref (main_object),
1237  std_cxx11::_1, std_cxx11::_2, std_cxx11::_3),
1238  std_cxx11::bind (copier,
1239  std_cxx11::ref (main_object),
1240  std_cxx11::_1),
1241  sample_scratch_data,
1242  sample_copy_data,
1243  queue_length,
1244  chunk_size);
1245  }
1246 
1247 }
1248 
1249 
1250 
1251 
1252 DEAL_II_NAMESPACE_CLOSE
1253 
1254 
1255 
1256 
1257 //---------------------------- work_stream.h ---------------------------
1258 // end of #ifndef dealii__work_stream_h
1259 #endif
1260 //---------------------------- work_stream.h ---------------------------
const std_cxx11::function< void(const Iterator &, ScratchData &, CopyData &)> worker
Definition: work_stream.h:887
void operator()(const tbb::blocked_range< typename std::vector< Iterator >::const_iterator > &range)
Definition: work_stream.h:783
Threads::ThreadLocalStorage< ScratchDataList > * scratch_data
Definition: work_stream.h:289
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:323
static ::ExceptionBase & ExcMessage(std::string arg1)
#define Assert(cond, exc)
Definition: exceptions.h:313
Worker(const std_cxx11::function< void(const Iterator &, ScratchData &, CopyData &)> &worker, bool copier_exist=true)
Definition: work_stream.h:484
const std_cxx11::function< void(const CopyData &)> copier
Definition: work_stream.h:685
const std_cxx11::function< void(const Iterator &, ScratchData &, CopyData &)> worker
Definition: work_stream.h:604
std::list< ScratchAndCopyDataObjects > ScratchAndCopyDataList
Definition: work_stream.h:877
const std_cxx11::function< void(const CopyData &)> copier
Definition: work_stream.h:893
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:1110
static unsigned int n_threads()
Threads::ThreadLocalStorage< typename ItemType::ScratchDataList > thread_local_scratch
Definition: work_stream.h:448
Copier(const std_cxx11::function< void(const CopyData &)> &copier)
Definition: work_stream.h:632
WorkerAndCopier(const std_cxx11::function< void(const Iterator &, ScratchData &, CopyData &)> &worker, const std_cxx11::function< void(const CopyData &)> &copier, const ScratchData &sample_scratch_data, const CopyData &sample_copy_data)
Definition: work_stream.h:765
static ::ExceptionBase & ExcInternalError()