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