Reference documentation for deal.II version 8.4.1
work_stream.h
1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 2008 - 2015 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 
136 namespace WorkStream
137 {
138 
139 #ifdef DEAL_II_WITH_THREADS
140 
141  namespace internal
142  {
143 
144 //TODO: The following classes all use std_cxx11::shared_ptr, but the
145 // correct pointer class would actually be std::unique_ptr. make this
146 // replacement whenever we have a class that provides these semantics
147 // and that is available also as a fall-back whenever via boost or similar
148 
161  namespace Implementation2
162  {
166  template <typename Iterator,
167  typename ScratchData,
168  typename CopyData>
169  class IteratorRangeToItemStream : public tbb::filter
170  {
171  public:
178  struct ItemType
179  {
186  {
187  std_cxx11::shared_ptr<ScratchData> scratch_data;
188  bool currently_in_use;
189 
194  :
195  currently_in_use (false)
196  {}
197 
198  ScratchDataObject (ScratchData *p,
199  const bool in_use)
200  :
201  scratch_data (p),
202  currently_in_use (in_use)
203  {}
204 
205 //TODO: when we push back an object to the list of scratch objects, in
206 // Worker::operator(), we first create an object and then copy
207 // it to the end of this list. this involves having two objects
208 // of the current type having pointers to it, each with their own
209 // currently_in_use flag. there is probably little harm in this because
210 // the original one goes out of scope right away again, but it's
211 // certainly awkward. one way to avoid this would be to use unique_ptr
212 // but we'd need to figure out a way to use it in non-C++11 mode
214  :
215  scratch_data (o.scratch_data),
216  currently_in_use (o.currently_in_use)
217  {}
218  };
219 
220 
225  typedef std::list<ScratchDataObject> ScratchDataList;
226 
231  std::vector<Iterator> work_items;
232 
238  std::vector<CopyData> copy_datas;
239 
245  unsigned int n_items;
246 
279 
284  const ScratchData *sample_scratch_data;
285 
291 
292 
298  :
299  n_items (0),
300  scratch_data (0),
301  sample_scratch_data (0),
302  currently_in_use (false)
303  {}
304  };
305 
306 
312  IteratorRangeToItemStream (const Iterator &begin,
313  const Iterator &end,
314  const unsigned int buffer_size,
315  const unsigned int chunk_size,
316  const ScratchData &sample_scratch_data,
317  const CopyData &sample_copy_data)
318  :
319  tbb::filter (/*is_serial=*/true),
320  remaining_iterator_range (begin, end),
321  item_buffer (buffer_size),
322  sample_scratch_data (sample_scratch_data),
323  chunk_size (chunk_size)
324  {
325  // initialize the elements of the ring buffer
326  for (unsigned int element=0; element<item_buffer.size(); ++element)
327  {
328  Assert (item_buffer[element].n_items == 0,
329  ExcInternalError());
330 
331  item_buffer[element].work_items.resize (chunk_size,
332  remaining_iterator_range.second);
333  item_buffer[element].scratch_data = &thread_local_scratch;
334  item_buffer[element].sample_scratch_data = &sample_scratch_data;
335  item_buffer[element].copy_datas.resize (chunk_size,
336  sample_copy_data);
337  item_buffer[element].currently_in_use = false;
338  }
339  }
340 
341 
345  virtual void *operator () (void *)
346  {
347  // find first unused item. we know that there must be one
348  // because we have set the maximal number of tokens in flight
349  // and have set the ring buffer to have exactly this size. so
350  // if this function is called, we know that less than the
351  // maximal number of items in currently in flight
352  //
353  // note that we need not lock access to this array since
354  // the current stage is run sequentially and we can therefore
355  // enter the following block only once at any given time.
356  // thus, there can be no race condition between checking that
357  // a flag is false and setting it to true. (there may be
358  // another thread where we release items and set 'false'
359  // flags to 'true', but that too does not produce any
360  // problems)
361  ItemType *current_item = 0;
362  for (unsigned int i=0; i<item_buffer.size(); ++i)
363  if (item_buffer[i].currently_in_use == false)
364  {
365  item_buffer[i].currently_in_use = true;
366  current_item = &item_buffer[i];
367  break;
368  }
369  Assert (current_item != 0, ExcMessage ("This can't be. There must be a free item!"));
370 
371  // initialize the next item. it may
372  // consist of at most chunk_size
373  // elements
374  current_item->n_items = 0;
375  while ((remaining_iterator_range.first !=
377  &&
378  (current_item->n_items < chunk_size))
379  {
380  current_item->work_items[current_item->n_items]
381  = remaining_iterator_range.first;
382 
383  ++remaining_iterator_range.first;
384  ++current_item->n_items;
385  }
386 
387  if (current_item->n_items == 0)
388  // there were no items
389  // left. terminate the pipeline
390  return 0;
391  else
392  return current_item;
393  }
394 
395  private:
400  std::pair<Iterator,Iterator> remaining_iterator_range;
401 
405  std::vector<ItemType> item_buffer;
406 
438 
444  const ScratchData &sample_scratch_data;
445 
452  const unsigned int chunk_size;
453 
458  void init_buffer_elements (const unsigned int element,
459  const CopyData &sample_copy_data)
460  {
461  Assert (item_buffer[element].n_items == 0,
462  ExcInternalError());
463 
464  item_buffer[element].work_items
465  .resize (chunk_size, remaining_iterator_range.second);
466  item_buffer[element].scratch_data
468  item_buffer[element].sample_scratch_data
470  item_buffer[element].copy_datas
471  .resize (chunk_size, sample_copy_data);
472  }
473  };
474 
475 
476 
482  template <typename Iterator,
483  typename ScratchData,
484  typename CopyData>
485  class Worker : public tbb::filter
486  {
487  public:
493  Worker (const std_cxx11::function<void (const Iterator &,
494  ScratchData &,
495  CopyData &)> &worker,
496  bool copier_exist=true)
497  :
498  tbb::filter (/* is_serial= */ false),
499  worker (worker),
501  {}
502 
503 
507  void *operator () (void *item)
508  {
509  // first unpack the current item
510  typedef
512  ItemType;
513 
514  ItemType *current_item = static_cast<ItemType *> (item);
515 
516  // we need to find an unused scratch data object in the list that
517  // corresponds to the current thread and then mark it as used. if
518  // we can't find one, create one
519  //
520  // as discussed in the discussion of the documentation of the
521  // IteratorRangeToItemStream::scratch_data variable, there is no
522  // need to synchronize access to this variable using a mutex
523  // as long as we have no yield-point in between. this means that
524  // we can't take an iterator into the list now and expect it to
525  // still be valid after calling the worker, but we at least do
526  // not have to lock the following section
527  ScratchData *scratch_data = 0;
528  {
529  typename ItemType::ScratchDataList &
530  scratch_data_list = current_item->scratch_data->get();
531 
532  // see if there is an unused object. if so, grab it and mark
533  // it as used
534  for (typename ItemType::ScratchDataList::iterator
535  p = scratch_data_list.begin();
536  p != scratch_data_list.end(); ++p)
537  if (p->currently_in_use == false)
538  {
539  scratch_data = p->scratch_data.get();
540  p->currently_in_use = true;
541  break;
542  }
543 
544  // if no object was found, create one and mark it as used
545  if (scratch_data == 0)
546  {
547  scratch_data = new ScratchData(*current_item->sample_scratch_data);
548 
549  typename ItemType::ScratchDataList::value_type
550  new_scratch_object (scratch_data, true);
551  scratch_data_list.push_back (new_scratch_object);
552  }
553  }
554 
555  // then call the worker function on each element of the chunk we were
556  // given. since these worker functions are called on separate threads,
557  // nothing good can happen if they throw an exception and we are best
558  // off catching it and showing an error message
559  for (unsigned int i=0; i<current_item->n_items; ++i)
560  {
561  try
562  {
563  if (worker)
564  worker (current_item->work_items[i],
565  *scratch_data,
566  current_item->copy_datas[i]);
567  }
568  catch (const std::exception &exc)
569  {
570  Threads::internal::handle_std_exception (exc);
571  }
572  catch (...)
573  {
574  Threads::internal::handle_unknown_exception ();
575  }
576  }
577 
578  // finally mark the scratch object as unused again. as above, there
579  // is no need to lock anything here since the object we work on
580  // is thread-local
581  {
582  typename ItemType::ScratchDataList &
583  scratch_data_list = current_item->scratch_data->get();
584 
585  for (typename ItemType::ScratchDataList::iterator p =
586  scratch_data_list.begin(); p != scratch_data_list.end();
587  ++p)
588  if (p->scratch_data.get() == scratch_data)
589  {
590  Assert(p->currently_in_use == true, ExcInternalError());
591  p->currently_in_use = false;
592  }
593  }
594 
595  // if there is no copier, mark current item as usable again
596  if (copier_exist==false)
597  current_item->currently_in_use = false;
598 
599 
600  // then return the original pointer
601  // to the now modified object
602  return item;
603  }
604 
605 
606  private:
611  const std_cxx11::function<void (const Iterator &,
612  ScratchData &,
613  CopyData &)> worker;
614 
620  };
621 
622 
623 
629  template <typename Iterator,
630  typename ScratchData,
631  typename CopyData>
632  class Copier : public tbb::filter
633  {
634  public:
641  Copier (const std_cxx11::function<void (const CopyData &)> &copier)
642  :
643  tbb::filter (/*is_serial=*/true),
644  copier (copier)
645  {}
646 
647 
651  void *operator () (void *item)
652  {
653  // first unpack the current item
654  typedef
656  ItemType;
657 
658  ItemType *current_item = static_cast<ItemType *> (item);
659 
660  // initiate copying data. for the same reasons as in the worker class
661  // above, catch exceptions rather than letting it propagate into
662  // unknown territories
663  for (unsigned int i=0; i<current_item->n_items; ++i)
664  {
665  try
666  {
667  if (copier)
668  copier (current_item->copy_datas[i]);
669  }
670  catch (const std::exception &exc)
671  {
672  Threads::internal::handle_std_exception (exc);
673  }
674  catch (...)
675  {
676  Threads::internal::handle_unknown_exception ();
677  }
678  }
679 
680  // mark current item as usable again
681  current_item->currently_in_use = false;
682 
683 
684  // return an invalid item since we are at the end of the
685  // pipeline
686  return 0;
687  }
688 
689 
690  private:
694  const std_cxx11::function<void (const CopyData &)> copier;
695  };
696 
697  }
698 
699 
707  namespace Implementation3
708  {
714  template <typename Iterator,
715  typename ScratchData,
716  typename CopyData>
718  {
719  std_cxx11::shared_ptr<ScratchData> scratch_data;
720  std_cxx11::shared_ptr<CopyData> copy_data;
721  bool currently_in_use;
722 
727  :
728  currently_in_use (false)
729  {}
730 
731  ScratchAndCopyDataObjects (ScratchData *p,
732  CopyData *q,
733  const bool in_use)
734  :
735  scratch_data (p),
736  copy_data (q),
737  currently_in_use (in_use)
738  {}
739 
740 //TODO: when we push back an object to the list of scratch objects, in
741 // Worker::operator(), we first create an object and then copy
742 // it to the end of this list. this involves having two objects
743 // of the current type having pointers to it, each with their own
744 // currently_in_use flag. there is probably little harm in this because
745 // the original one goes out of scope right away again, but it's
746 // certainly awkward. one way to avoid this would be to use unique_ptr
747 // but we'd need to figure out a way to use it in non-C++11 mode
749  :
750  scratch_data (o.scratch_data),
751  copy_data (o.copy_data),
752  currently_in_use (o.currently_in_use)
753  {}
754  };
755 
756 
757 
758 
759 
765  template <typename Iterator,
766  typename ScratchData,
767  typename CopyData>
769  {
770  public:
774  WorkerAndCopier (const std_cxx11::function<void (const Iterator &,
775  ScratchData &,
776  CopyData &)> &worker,
777  const std_cxx11::function<void (const CopyData &)> &copier,
778  const ScratchData &sample_scratch_data,
779  const CopyData &sample_copy_data)
780  :
781  worker (worker),
782  copier (copier),
783  sample_scratch_data (sample_scratch_data),
784  sample_copy_data (sample_copy_data)
785  {}
786 
787 
792  void operator() (const tbb::blocked_range<typename std::vector<Iterator>::const_iterator> &range)
793  {
794  // we need to find an unused scratch and corresponding copy
795  // data object in the list that corresponds to the current
796  // thread and then mark it as used. If we can't find one,
797  // create one as discussed in the discussion of the documentation
798  // of the IteratorRangeToItemStream::scratch_data variable,
799  // there is no need to synchronize access to this variable
800  // using a mutex as long as we have no yield-point in between.
801  // This means that we can't take an iterator into the list
802  // now and expect it to still be valid after calling the worker,
803  // but we at least do not have to lock the following section.
804  ScratchData *scratch_data = 0;
805  CopyData *copy_data = 0;
806  {
807  ScratchAndCopyDataList &scratch_and_copy_data_list = data.get();
808 
809  // see if there is an unused object. if so, grab it and mark
810  // it as used
811  for (typename ScratchAndCopyDataList::iterator
812  p = scratch_and_copy_data_list.begin();
813  p != scratch_and_copy_data_list.end(); ++p)
814  if (p->currently_in_use == false)
815  {
816  scratch_data = p->scratch_data.get();
817  copy_data = p->copy_data.get();
818  p->currently_in_use = true;
819  break;
820  }
821 
822  // if no element in the list was found, create one and mark it as used
823  if (scratch_data == 0)
824  {
825  Assert (copy_data==0, ExcInternalError());
826  scratch_data = new ScratchData(sample_scratch_data);
827  copy_data = new CopyData(sample_copy_data);
828 
829  typename ScratchAndCopyDataList::value_type
830  new_scratch_object (scratch_data, copy_data, true);
831  scratch_and_copy_data_list.push_back (new_scratch_object);
832  }
833  }
834 
835  // then call the worker and copier functions on each
836  // element of the chunk we were given.
837  for (typename std::vector<Iterator>::const_iterator p=range.begin();
838  p != range.end(); ++p)
839  {
840  try
841  {
842  if (worker)
843  worker (*p,
844  *scratch_data,
845  *copy_data);
846  if (copier)
847  copier (*copy_data);
848  }
849  catch (const std::exception &exc)
850  {
851  Threads::internal::handle_std_exception (exc);
852  }
853  catch (...)
854  {
855  Threads::internal::handle_unknown_exception ();
856  }
857  }
858 
859  // finally mark the scratch object as unused again. as above, there
860  // is no need to lock anything here since the object we work on
861  // is thread-local
862  {
863  ScratchAndCopyDataList &scratch_and_copy_data_list = data.get();
864 
865  for (typename ScratchAndCopyDataList::iterator p =
866  scratch_and_copy_data_list.begin(); p != scratch_and_copy_data_list.end();
867  ++p)
868  if (p->scratch_data.get() == scratch_data)
869  {
870  Assert(p->currently_in_use == true, ExcInternalError());
871  p->currently_in_use = false;
872  }
873  }
874 
875  }
876 
877  private:
878  typedef
881 
886  typedef std::list<ScratchAndCopyDataObjects> ScratchAndCopyDataList;
887 
889 
894  const std_cxx11::function<void (const Iterator &,
895  ScratchData &,
896  CopyData &)> worker;
897 
902  const std_cxx11::function<void (const CopyData &)> copier;
903 
907  const ScratchData &sample_scratch_data;
908  const CopyData &sample_copy_data;
909  };
910  }
911 
912  }
913 
914 
915 #endif // DEAL_II_WITH_THREADS
916 
917 
952  template <typename Worker,
953  typename Copier,
954  typename Iterator,
955  typename ScratchData,
956  typename CopyData>
957  void
958  run (const std::vector<std::vector<Iterator> > &colored_iterators,
959  Worker worker,
960  Copier copier,
961  const ScratchData &sample_scratch_data,
962  const CopyData &sample_copy_data,
963  const unsigned int queue_length = 2*MultithreadInfo::n_threads(),
964  const unsigned int chunk_size = 8);
965 
966 
1001  template <typename Worker,
1002  typename Copier,
1003  typename Iterator,
1004  typename ScratchData,
1005  typename CopyData>
1006  void
1007  run (const Iterator &begin,
1008  const typename identity<Iterator>::type &end,
1009  Worker worker,
1010  Copier copier,
1011  const ScratchData &sample_scratch_data,
1012  const CopyData &sample_copy_data,
1013  const unsigned int queue_length = 2*MultithreadInfo::n_threads(),
1014  const unsigned int chunk_size = 8)
1015  {
1016  Assert (queue_length > 0,
1017  ExcMessage ("The queue length must be at least one, and preferably "
1018  "larger than the number of processors on this system."));
1019  (void)queue_length; // removes -Wunused-parameter warning in optimized mode
1020  Assert (chunk_size > 0,
1021  ExcMessage ("The chunk_size must be at least one."));
1022  (void)chunk_size; // removes -Wunused-parameter warning in optimized mode
1023 
1024  // if no work then skip. (only use operator!= for iterators since we may
1025  // not have an equality comparison operator)
1026  if (!(begin != end))
1027  return;
1028 
1029  // we want to use TBB if we have support and if it is not disabled at
1030  // runtime:
1031 #ifdef DEAL_II_WITH_THREADS
1032  if (MultithreadInfo::n_threads()==1)
1033 #endif
1034  {
1035  // need to copy the sample since it is marked const
1036  ScratchData scratch_data = sample_scratch_data;
1037  CopyData copy_data = sample_copy_data;
1038 
1039  for (Iterator i=begin; i!=end; ++i)
1040  {
1041  // need to check if the function is not the zero function. To
1042  // check zero-ness, create a C++ function out of it and check that
1043  if (static_cast<const std_cxx11::function<void (const Iterator &,
1044  ScratchData &,
1045  CopyData &)>& >(worker))
1046  worker (i, scratch_data, copy_data);
1047  if (static_cast<const std_cxx11::function<void (const CopyData &)>& >
1048  (copier))
1049  copier (copy_data);
1050  }
1051  }
1052 #ifdef DEAL_II_WITH_THREADS
1053  else // have TBB and use more than one thread
1054  {
1055  // Check that the copier exist
1056  if (static_cast<const std_cxx11::function<void (const CopyData &)>& >(copier))
1057  {
1058  // create the three stages of the pipeline
1060  iterator_range_to_item_stream (begin, end,
1061  queue_length,
1062  chunk_size,
1063  sample_scratch_data,
1064  sample_copy_data);
1065 
1068 
1069  // now create a pipeline from these stages
1070  tbb::pipeline assembly_line;
1071  assembly_line.add_filter (iterator_range_to_item_stream);
1072  assembly_line.add_filter (worker_filter);
1073  assembly_line.add_filter (copier_filter);
1074 
1075  // and run it
1076  assembly_line.run (queue_length);
1077 
1078  assembly_line.clear ();
1079  }
1080  else
1081  {
1082  // there is no copier function. in this case, we have an
1083  // embarrassingly parallel problem where we can
1084  // essentially apply parallel_for. because parallel_for
1085  // requires subdividing the range for which operator- is
1086  // necessary between iterators, it is often inefficient to
1087  // apply it directory to cell ranges and similar iterator
1088  // types for which operator- is expensive or, in fact,
1089  // nonexistent. rather, in that case, we simply copy the
1090  // iterators into a large array and use operator- on
1091  // iterators to this array of iterators.
1092  //
1093  // instead of duplicating code, this is essentially the
1094  // same situation we have in Implementation3 below, so we
1095  // just defer to that place
1096  std::vector<std::vector<Iterator> > all_iterators (1);
1097  for (Iterator p=begin; p!=end; ++p)
1098  all_iterators[0].push_back (p);
1099 
1100  run (all_iterators,
1101  worker, copier,
1102  sample_scratch_data,
1103  sample_copy_data,
1104  queue_length,
1105  chunk_size);
1106  }
1107  }
1108 #endif
1109  }
1110 
1111 
1112  // Implementation 3:
1113  template <typename Worker,
1114  typename Copier,
1115  typename Iterator,
1116  typename ScratchData,
1117  typename CopyData>
1118  void
1119  run (const std::vector<std::vector<Iterator> > &colored_iterators,
1120  Worker worker,
1121  Copier copier,
1122  const ScratchData &sample_scratch_data,
1123  const CopyData &sample_copy_data,
1124  const unsigned int queue_length,
1125  const unsigned int chunk_size)
1126  {
1127  Assert (queue_length > 0,
1128  ExcMessage ("The queue length must be at least one, and preferably "
1129  "larger than the number of processors on this system."));
1130  (void)queue_length; // removes -Wunused-parameter warning in optimized mode
1131  Assert (chunk_size > 0,
1132  ExcMessage ("The chunk_size must be at least one."));
1133  (void)chunk_size; // removes -Wunused-parameter warning in optimized mode
1134 
1135  // we want to use TBB if we have support and if it is not disabled at
1136  // runtime:
1137 #ifdef DEAL_II_WITH_THREADS
1138  if (MultithreadInfo::n_threads()==1)
1139 #endif
1140  {
1141  // need to copy the sample since it is marked const
1142  ScratchData scratch_data = sample_scratch_data;
1143  CopyData copy_data = sample_copy_data;
1144 
1145  for (unsigned int color=0; color<colored_iterators.size(); ++color)
1146  for (typename std::vector<Iterator>::const_iterator p = colored_iterators[color].begin();
1147  p != colored_iterators[color].end(); ++p)
1148  {
1149  // need to check if the function is not the zero function. To
1150  // check zero-ness, create a C++ function out of it and check that
1151  if (static_cast<const std_cxx11::function<void (const Iterator &,
1152  ScratchData &,
1153  CopyData &)>& >(worker))
1154  worker (*p, scratch_data, copy_data);
1155  if (static_cast<const std_cxx11::function<void (const CopyData &)>& >(copier))
1156  copier (copy_data);
1157  }
1158  }
1159 #ifdef DEAL_II_WITH_THREADS
1160  else // have TBB and use more than one thread
1161  {
1162  // loop over the various colors of what we're given
1163  for (unsigned int color=0; color<colored_iterators.size(); ++color)
1164  if (colored_iterators[color].size() > 0)
1165  {
1166  typedef
1168  WorkerAndCopier;
1169 
1170  typedef
1171  typename std::vector<Iterator>::const_iterator
1172  RangeType;
1173 
1174  WorkerAndCopier worker_and_copier (worker,
1175  copier,
1176  sample_scratch_data,
1177  sample_copy_data);
1178 
1179  tbb::parallel_for (tbb::blocked_range<RangeType>
1180  (colored_iterators[color].begin(),
1181  colored_iterators[color].end(),
1182  /*grain_size=*/chunk_size),
1183  std_cxx11::bind (&WorkerAndCopier::operator(),
1184  std_cxx11::ref(worker_and_copier),
1185  std_cxx11::_1),
1186  tbb::auto_partitioner());
1187  }
1188  }
1189 #endif
1190  }
1191 
1192 
1193 
1194 
1195 
1225  template <typename MainClass,
1226  typename Iterator,
1227  typename ScratchData,
1228  typename CopyData>
1229  void
1230  run (const Iterator &begin,
1231  const typename identity<Iterator>::type &end,
1232  MainClass &main_object,
1233  void (MainClass::*worker) (const Iterator &,
1234  ScratchData &,
1235  CopyData &),
1236  void (MainClass::*copier) (const CopyData &),
1237  const ScratchData &sample_scratch_data,
1238  const CopyData &sample_copy_data,
1239  const unsigned int queue_length = 2*MultithreadInfo::n_threads(),
1240  const unsigned int chunk_size = 8)
1241  {
1242  // forward to the other function
1243  run (begin, end,
1244  std_cxx11::bind (worker,
1245  std_cxx11::ref (main_object),
1246  std_cxx11::_1, std_cxx11::_2, std_cxx11::_3),
1247  std_cxx11::bind (copier,
1248  std_cxx11::ref (main_object),
1249  std_cxx11::_1),
1250  sample_scratch_data,
1251  sample_copy_data,
1252  queue_length,
1253  chunk_size);
1254  }
1255 
1256 }
1257 
1258 
1259 
1260 
1261 DEAL_II_NAMESPACE_CLOSE
1262 
1263 
1264 
1265 
1266 //---------------------------- work_stream.h ---------------------------
1267 // end of #ifndef dealii__work_stream_h
1268 #endif
1269 //---------------------------- work_stream.h ---------------------------
::ExceptionBase & ExcMessage(std::string arg1)
void init_buffer_elements(const unsigned int element, const CopyData &sample_copy_data)
Definition: work_stream.h:458
const std_cxx11::function< void(const Iterator &, ScratchData &, CopyData &)> worker
Definition: work_stream.h:896
void operator()(const tbb::blocked_range< typename std::vector< Iterator >::const_iterator > &range)
Definition: work_stream.h:792
Threads::ThreadLocalStorage< ScratchDataList > * scratch_data
Definition: work_stream.h:278
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:312
#define Assert(cond, exc)
Definition: exceptions.h:294
Worker(const std_cxx11::function< void(const Iterator &, ScratchData &, CopyData &)> &worker, bool copier_exist=true)
Definition: work_stream.h:493
const std_cxx11::function< void(const CopyData &)> copier
Definition: work_stream.h:694
const std_cxx11::function< void(const Iterator &, ScratchData &, CopyData &)> worker
Definition: work_stream.h:613
std::list< ScratchAndCopyDataObjects > ScratchAndCopyDataList
Definition: work_stream.h:886
const std_cxx11::function< void(const CopyData &)> copier
Definition: work_stream.h:902
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:1119
static unsigned int n_threads()
Threads::ThreadLocalStorage< typename ItemType::ScratchDataList > thread_local_scratch
Definition: work_stream.h:437
Copier(const std_cxx11::function< void(const CopyData &)> &copier)
Definition: work_stream.h:641
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:774