Reference documentation for deal.II version 9.4.1
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
work_stream.h
Go to the documentation of this file.
1// ---------------------------------------------------------------------
2//
3// Copyright (C) 2008 - 2022 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
29
30# ifdef DEAL_II_WITH_TBB
32# ifdef DEAL_II_TBB_WITH_ONEAPI
33# include <tbb/parallel_pipeline.h>
34# else
35# include <tbb/pipeline.h>
36# endif
38# endif
39
40# include <functional>
41# include <iterator>
42# include <memory>
43# include <utility>
44# include <vector>
45
47
48
49
162namespace WorkStream
163{
168 namespace internal
169 {
170# ifdef DEAL_II_WITH_TBB
185 namespace tbb_no_coloring
186 {
190 template <typename Iterator, typename ScratchData, typename CopyData>
192 {
193 public:
200 struct ItemType
201 {
208 {
209 std::unique_ptr<ScratchData> scratch_data;
211
216 : currently_in_use(false)
217 {}
218
219 ScratchDataObject(ScratchData *p, const bool in_use)
220 : scratch_data(p)
221 , currently_in_use(in_use)
222 {}
223
224 // Provide a copy constructor that actually doesn't copy the
225 // internal state. This makes handling ScratchAndCopyDataObjects
226 // easier to handle with STL containers.
228 : currently_in_use(false)
229 {}
230
231 ScratchDataObject(ScratchDataObject &&o) noexcept = default;
232 };
233
234
239 using ScratchDataList = std::list<ScratchDataObject>;
240
245 std::vector<Iterator> iterators;
246
252 std::vector<CopyData> copy_datas;
253
259 unsigned int n_iterators;
260
293
298 const ScratchData *sample_scratch_data;
299
305
306
312 : n_iterators(0)
313 , scratch_data(nullptr)
314 , sample_scratch_data(nullptr)
315 , currently_in_use(false)
316 {}
317 };
318
319
325 IteratorRangeToItemStream(const Iterator & begin,
326 const Iterator & end,
327 const unsigned int buffer_size,
328 const unsigned int chunk_size,
329 const ScratchData &sample_scratch_data,
330 const CopyData & sample_copy_data)
331 : remaining_iterator_range(begin, end)
332 , item_buffer(buffer_size)
335 {
336 // initialize the elements of the ring buffer
337 for (auto &item : item_buffer)
338 {
339 Assert(item.n_iterators == 0, ExcInternalError());
340
341 item.iterators.resize(chunk_size,
343 item.scratch_data = &thread_local_scratch;
344 item.sample_scratch_data = &sample_scratch_data;
345 item.copy_datas.resize(chunk_size, sample_copy_data);
346 item.currently_in_use = false;
347 }
348 }
349
350
354 ItemType *
356 {
357 // find first unused item. we know that there must be one
358 // because we have set the maximal number of tokens in flight
359 // and have set the ring buffer to have exactly this size. so
360 // if this function is called, we know that less than the
361 // maximal number of items in currently in flight
362 //
363 // note that we need not lock access to this array since
364 // the current stage is run sequentially and we can therefore
365 // enter the following block only once at any given time.
366 // thus, there can be no race condition between checking that
367 // a flag is false and setting it to true. (there may be
368 // another thread where we release items and set 'false'
369 // flags to 'true', but that too does not produce any
370 // problems)
371 ItemType *current_item = nullptr;
372 for (unsigned int i = 0; i < item_buffer.size(); ++i)
373 if (item_buffer[i].currently_in_use == false)
374 {
375 item_buffer[i].currently_in_use = true;
376 current_item = &item_buffer[i];
377 break;
378 }
379 Assert(current_item != nullptr,
380 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_iterators = 0;
386 while ((remaining_iterator_range.first !=
387 remaining_iterator_range.second) &&
388 (current_item->n_iterators < chunk_size))
389 {
390 current_item->iterators[current_item->n_iterators] =
392
394 ++current_item->n_iterators;
395 }
396
397 if (current_item->n_iterators == 0)
398 // there were no items
399 // left. terminate the pipeline
400 return nullptr;
401 else
402 return current_item;
403 }
404
405 private:
410 std::pair<Iterator, Iterator> remaining_iterator_range;
411
415 std::vector<ItemType> item_buffer;
416
449
455 const ScratchData &sample_scratch_data;
456
463 const unsigned int chunk_size;
464 };
465
466
467
468 template <typename Worker,
469 typename Copier,
470 typename Iterator,
471 typename ScratchData,
472 typename CopyData>
473 void
474 run(const Iterator & begin,
475 const typename identity<Iterator>::type &end,
476 Worker worker,
477 Copier copier,
478 const ScratchData & sample_scratch_data,
479 const CopyData & sample_copy_data,
480 const unsigned int queue_length,
481 const unsigned int chunk_size)
482 {
483 using ItemType = typename IteratorRangeToItemStream<Iterator,
484 ScratchData,
485 CopyData>::ItemType;
486
487 // Create the three stages of the pipeline:
488
489 //
490 // ----- Stage 1 -----
491 //
492 // The first stage is the one that provides us with chunks of data
493 // to work on (the stream of "items"). This stage runs sequentially.
495 iterator_range_to_item_stream(begin,
496 end,
497 queue_length,
498 chunk_size,
499 sample_scratch_data,
500 sample_copy_data);
501 auto tbb_item_stream_filter = tbb::make_filter<void, ItemType *>(
503 tbb::filter_mode::serial_in_order,
504# else
505 tbb::filter::serial,
506# endif
507 [&](tbb::flow_control &fc) -> ItemType * {
508 if (const auto item = iterator_range_to_item_stream.get_item())
509 return item;
510 else
511 {
512 fc.stop();
513 return nullptr;
514 }
515 });
516
517 //
518 // ----- Stage 2 -----
519 //
520 // The second stage is the one that does the actual work. This is the
521 // stage that runs in parallel
522 auto tbb_worker_filter = tbb::make_filter<ItemType *, ItemType *>(
524 tbb::filter_mode::parallel,
525# else
526 tbb::filter::parallel,
527# endif
528 [worker =
529 std::function<void(const Iterator &, ScratchData &, CopyData &)>(
530 worker),
531 copier_exists =
532 static_cast<bool>(std::function<void(const CopyData &)>(copier))](
533 ItemType *current_item) {
534 // we need to find an unused scratch data object in the list that
535 // corresponds to the current thread and then mark it as used. if
536 // we can't find one, create one
537 //
538 // as discussed in the discussion of the documentation of the
539 // IteratorRangeToItemStream::scratch_data variable, there is no
540 // need to synchronize access to this variable using a mutex
541 // as long as we have no yield-point in between. this means that
542 // we can't take an iterator into the list now and expect it to
543 // still be valid after calling the worker, but we at least do
544 // not have to lock the following section
545 ScratchData *scratch_data = nullptr;
546 {
547 // see if there is an unused object. if so, grab it and mark
548 // it as used
549 for (auto &p : current_item->scratch_data->get())
550 if (p.currently_in_use == false)
551 {
552 scratch_data = p.scratch_data.get();
553 p.currently_in_use = true;
554
555 break;
556 }
557
558 // if no object was found, create one and mark it as used
559 if (scratch_data == nullptr)
560 {
561 scratch_data =
562 new ScratchData(*current_item->sample_scratch_data);
563 current_item->scratch_data->get().emplace_back(scratch_data,
564 true);
565 }
566 }
567
568 // then call the worker function on each element of the chunk we
569 // were given. since these worker functions are called on separate
570 // threads, nothing good can happen if they throw an exception and
571 // we are best off catching it and showing an error message
572 for (unsigned int i = 0; i < current_item->n_iterators; ++i)
573 {
574 try
575 {
576 if (worker)
577 worker(current_item->iterators[i],
578 *scratch_data,
579 current_item->copy_datas[i]);
580 }
581 catch (const std::exception &exc)
582 {
584 }
585 catch (...)
586 {
588 }
589 }
590
591 // finally mark the scratch object as unused again. as above, there
592 // is no need to lock anything here since the object we work on
593 // is thread-local
594 for (auto &p : current_item->scratch_data->get())
595 if (p.scratch_data.get() == scratch_data)
596 {
597 Assert(p.currently_in_use == true, ExcInternalError());
598 p.currently_in_use = false;
599
600 break;
601 }
602
603 // if there is no copier, mark current item as usable again
604 if (copier_exists == false)
605 current_item->currently_in_use = false;
606
607
608 // Then return the original pointer
609 // to the now modified object. The copier will work on it next.
610 return current_item;
611 });
612
613
614 //
615 // ----- Stage 3 -----
616 //
617 // The last stage is the one that copies data from the CopyData objects
618 // to the final destination. This stage runs sequentially again.
619 auto tbb_copier_filter = tbb::make_filter<ItemType *, void>(
621 tbb::filter_mode::serial_in_order,
622# else
623 tbb::filter::serial,
624# endif
625 [copier = std::function<void(const CopyData &)>(copier)](
626 ItemType *current_item) {
627 if (copier)
628 {
629 // Initiate copying data. For the same reasons as in the worker
630 // class above, catch exceptions rather than letting them
631 // propagate into unknown territories:
632 for (unsigned int i = 0; i < current_item->n_iterators; ++i)
633 {
634 try
635 {
636 copier(current_item->copy_datas[i]);
637 }
638 catch (const std::exception &exc)
639 {
641 }
642 catch (...)
643 {
645 }
646 }
647 }
648 // mark current item as usable again
649 current_item->currently_in_use = false;
650 });
651
652
653 //
654 // ----- The pipeline -----
655 //
656 // Now create a pipeline from these stages and execute it:
657 tbb::parallel_pipeline(queue_length,
658 tbb_item_stream_filter & tbb_worker_filter &
659 tbb_copier_filter);
660 }
661
662 } // namespace tbb_no_coloring
663# endif // DEAL_II_WITH_TBB
664
665
672 namespace sequential
673 {
677 template <typename Worker,
678 typename Copier,
679 typename Iterator,
680 typename ScratchData,
681 typename CopyData>
682 void
683 run(const Iterator & begin,
684 const typename identity<Iterator>::type &end,
685 Worker worker,
686 Copier copier,
687 const ScratchData & sample_scratch_data,
688 const CopyData & sample_copy_data)
689 {
690 // need to copy the sample since it is marked const
691 ScratchData scratch_data = sample_scratch_data;
692 CopyData copy_data = sample_copy_data; // NOLINT
693
694 // Optimization: Check if the functions are not the zero function. To
695 // check zero-ness, create a C++ function out of it:
696 const bool have_worker =
697 (static_cast<const std::function<
698 void(const Iterator &, ScratchData &, CopyData &)> &>(worker)) !=
699 nullptr;
700 const bool have_copier =
701 (static_cast<const std::function<void(const CopyData &)> &>(
702 copier)) != nullptr;
703
704 // Finally loop over all items and perform the necessary work:
705 for (Iterator i = begin; i != end; ++i)
706 {
707 if (have_worker)
708 worker(i, scratch_data, copy_data);
709 if (have_copier)
710 copier(copy_data);
711 }
712 }
713
714
715
719 template <typename Worker,
720 typename Copier,
721 typename Iterator,
722 typename ScratchData,
723 typename CopyData>
724 void
725 run(const std::vector<std::vector<Iterator>> &colored_iterators,
726 Worker worker,
727 Copier copier,
728 const ScratchData & sample_scratch_data,
729 const CopyData & sample_copy_data)
730 {
731 // need to copy the sample since it is marked const
732 ScratchData scratch_data = sample_scratch_data;
733 CopyData copy_data = sample_copy_data; // NOLINT
734
735 // Optimization: Check if the functions are not the zero function. To
736 // check zero-ness, create a C++ function out of it:
737 const bool have_worker =
738 (static_cast<const std::function<
739 void(const Iterator &, ScratchData &, CopyData &)> &>(worker)) !=
740 nullptr;
741 const bool have_copier =
742 (static_cast<const std::function<void(const CopyData &)> &>(
743 copier)) != nullptr;
744
745 // Finally loop over all items and perform the necessary work:
746 for (unsigned int color = 0; color < colored_iterators.size(); ++color)
747 if (colored_iterators[color].size() > 0)
748 for (auto &it : colored_iterators[color])
749 {
750 if (have_worker)
751 worker(it, scratch_data, copy_data);
752 if (have_copier)
753 copier(copy_data);
754 }
755 }
756
757 } // namespace sequential
758
759
760
761# ifdef DEAL_II_WITH_TBB
769 namespace tbb_colored
770 {
776 template <typename Iterator, typename ScratchData, typename CopyData>
778 {
779 std::unique_ptr<ScratchData> scratch_data;
780 std::unique_ptr<CopyData> copy_data;
782
787 : currently_in_use(false)
788 {}
789
790 ScratchAndCopyDataObjects(std::unique_ptr<ScratchData> &&p,
791 std::unique_ptr<CopyData> && q,
792 const bool in_use)
793 : scratch_data(std::move(p))
794 , copy_data(std::move(q))
795 , currently_in_use(in_use)
796 {}
797
798 // Provide a copy constructor that actually doesn't copy the
799 // internal state. This makes handling ScratchAndCopyDataObjects
800 // easier to handle with STL containers.
802 : currently_in_use(false)
803 {}
804 };
805
806
807
813 template <typename Iterator, typename ScratchData, typename CopyData>
815 {
816 public:
821 const std::function<void(const Iterator &, ScratchData &, CopyData &)>
822 & worker,
823 const std::function<void(const CopyData &)> &copier,
824 const ScratchData & sample_scratch_data,
825 const CopyData & sample_copy_data)
826 : worker(worker)
827 , copier(copier)
830 {}
831
832
837 void
838 operator()(const tbb::blocked_range<
839 typename std::vector<Iterator>::const_iterator> &range)
840 {
841 // we need to find an unused scratch and corresponding copy
842 // data object in the list that corresponds to the current
843 // thread and then mark it as used. If we can't find one,
844 // create one as discussed in the discussion of the documentation
845 // of the IteratorRangeToItemStream::scratch_data variable,
846 // there is no need to synchronize access to this variable
847 // using a mutex as long as we have no yield-point in between.
848 // This means that we can't take an iterator into the list
849 // now and expect it to still be valid after calling the worker,
850 // but we at least do not have to lock the following section.
851 ScratchData *scratch_data = nullptr;
852 CopyData * copy_data = nullptr;
853 {
854 ScratchAndCopyDataList &scratch_and_copy_data_list = data.get();
855
856 // see if there is an unused object. if so, grab it and mark
857 // it as used
858 for (typename ScratchAndCopyDataList::iterator p =
859 scratch_and_copy_data_list.begin();
860 p != scratch_and_copy_data_list.end();
861 ++p)
862 if (p->currently_in_use == false)
863 {
864 scratch_data = p->scratch_data.get();
865 copy_data = p->copy_data.get();
866 p->currently_in_use = true;
867 break;
868 }
869
870 // if no element in the list was found, create one and mark it as
871 // used
872 if (scratch_data == nullptr)
873 {
874 Assert(copy_data == nullptr, ExcInternalError());
875
876 scratch_and_copy_data_list.emplace_back(
877 std::make_unique<ScratchData>(sample_scratch_data),
878 std::make_unique<CopyData>(sample_copy_data),
879 true);
880 scratch_data =
881 scratch_and_copy_data_list.back().scratch_data.get();
882 copy_data = scratch_and_copy_data_list.back().copy_data.get();
883 }
884 }
885
886 // then call the worker and copier functions on each
887 // element of the chunk we were given.
888 for (typename std::vector<Iterator>::const_iterator p = range.begin();
889 p != range.end();
890 ++p)
891 {
892 try
893 {
894 if (worker)
895 worker(*p, *scratch_data, *copy_data);
896 if (copier)
897 copier(*copy_data);
898 }
899 catch (const std::exception &exc)
900 {
902 }
903 catch (...)
904 {
906 }
907 }
908
909 // finally mark the scratch object as unused again. as above, there
910 // is no need to lock anything here since the object we work on
911 // is thread-local
912 {
913 ScratchAndCopyDataList &scratch_and_copy_data_list = data.get();
914
915 for (typename ScratchAndCopyDataList::iterator p =
916 scratch_and_copy_data_list.begin();
917 p != scratch_and_copy_data_list.end();
918 ++p)
919 if (p->scratch_data.get() == scratch_data)
920 {
921 Assert(p->currently_in_use == true, ExcInternalError());
922 p->currently_in_use = false;
923 }
924 }
925 }
926
927 private:
928 using ScratchAndCopyDataObjects = typename tbb_colored::
929 ScratchAndCopyDataObjects<Iterator, ScratchData, CopyData>;
930
935 using ScratchAndCopyDataList = std::list<ScratchAndCopyDataObjects>;
936
938
943 const std::function<void(const Iterator &, ScratchData &, CopyData &)>
945
950 const std::function<void(const CopyData &)> copier;
951
955 const ScratchData &sample_scratch_data;
956 const CopyData & sample_copy_data;
957 };
958
962 template <typename Worker,
963 typename Copier,
964 typename Iterator,
965 typename ScratchData,
966 typename CopyData>
967 void
968 run(const std::vector<std::vector<Iterator>> &colored_iterators,
969 Worker worker,
970 Copier copier,
971 const ScratchData & sample_scratch_data,
972 const CopyData & sample_copy_data,
973 const unsigned int chunk_size)
974 {
975 // loop over the various colors of what we're given
976 for (unsigned int color = 0; color < colored_iterators.size(); ++color)
977 if (colored_iterators[color].size() > 0)
978 {
979 using WorkerAndCopier = internal::tbb_colored::
980 WorkerAndCopier<Iterator, ScratchData, CopyData>;
981
982 using RangeType = typename std::vector<Iterator>::const_iterator;
983
984 WorkerAndCopier worker_and_copier(worker,
985 copier,
986 sample_scratch_data,
987 sample_copy_data);
988
990 colored_iterators[color].begin(),
991 colored_iterators[color].end(),
992 [&worker_and_copier](
993 const tbb::blocked_range<
994 typename std::vector<Iterator>::const_iterator> &range) {
995 worker_and_copier(range);
996 },
997 chunk_size);
998 }
999 }
1000
1001 } // namespace tbb_colored
1002# endif // DEAL_II_WITH_TBB
1003
1004
1005 } // namespace internal
1006
1007
1008
1056 template <typename Worker,
1057 typename Copier,
1058 typename Iterator,
1059 typename ScratchData,
1060 typename CopyData>
1061 void
1062 run(const std::vector<std::vector<Iterator>> &colored_iterators,
1063 Worker worker,
1064 Copier copier,
1065 const ScratchData & sample_scratch_data,
1066 const CopyData & sample_copy_data,
1067 const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1068 const unsigned int chunk_size = 8);
1069
1070
1120 template <typename Worker,
1121 typename Copier,
1122 typename Iterator,
1123 typename ScratchData,
1124 typename CopyData>
1125 void
1126 run(const Iterator & begin,
1127 const typename identity<Iterator>::type &end,
1128 Worker worker,
1129 Copier copier,
1130 const ScratchData & sample_scratch_data,
1131 const CopyData & sample_copy_data,
1132 const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1133 const unsigned int chunk_size = 8)
1134 {
1135 Assert(queue_length > 0,
1136 ExcMessage("The queue length must be at least one, and preferably "
1137 "larger than the number of processors on this system."));
1138 (void)queue_length; // removes -Wunused-parameter warning in optimized mode
1139 Assert(chunk_size > 0, ExcMessage("The chunk_size must be at least one."));
1140 (void)chunk_size; // removes -Wunused-parameter warning in optimized mode
1141
1142 // If no work then skip. (only use operator!= for iterators since we may
1143 // not have an equality comparison operator)
1144 if (!(begin != end))
1145 return;
1146
1148 {
1149# ifdef DEAL_II_WITH_TBB
1150 if (static_cast<const std::function<void(const CopyData &)> &>(copier))
1151 {
1152 // If we have a copier, run the algorithm:
1154 end,
1155 worker,
1156 copier,
1157 sample_scratch_data,
1158 sample_copy_data,
1159 queue_length,
1160 chunk_size);
1161 }
1162 else
1163 {
1164 // There is no copier function. in this case, we have an
1165 // embarrassingly parallel problem where we can
1166 // essentially apply parallel_for. because parallel_for
1167 // requires subdividing the range for which operator- is
1168 // necessary between iterators, it is often inefficient to
1169 // apply it directly to cell ranges and similar iterator
1170 // types for which operator- is expensive or, in fact,
1171 // nonexistent. rather, in that case, we simply copy the
1172 // iterators into a large array and use operator- on
1173 // iterators to this array of iterators.
1174 //
1175 // instead of duplicating code, this is essentially the
1176 // same situation we have in the colored implementation below, so we
1177 // just defer to that place
1178 std::vector<std::vector<Iterator>> all_iterators(1);
1179 for (Iterator p = begin; p != end; ++p)
1180 all_iterators[0].push_back(p);
1181
1182 run(all_iterators,
1183 worker,
1184 copier,
1185 sample_scratch_data,
1186 sample_copy_data,
1187 queue_length,
1188 chunk_size);
1189 }
1190
1191 // exit this function to not run the sequential version below:
1192 return;
1193# endif
1194 }
1195
1196 // no TBB installed or we are requested to run sequentially:
1198 begin, end, worker, copier, sample_scratch_data, sample_copy_data);
1199 }
1200
1201
1202
1210 template <typename Worker,
1211 typename Copier,
1212 typename IteratorRangeType,
1213 typename ScratchData,
1214 typename CopyData,
1215 typename = typename std::enable_if<
1216 has_begin_and_end<IteratorRangeType>>::type>
1217 void
1218 run(IteratorRangeType iterator_range,
1219 Worker worker,
1220 Copier copier,
1221 const ScratchData &sample_scratch_data,
1222 const CopyData & sample_copy_data,
1223 const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1224 const unsigned int chunk_size = 8)
1225 {
1226 // Call the function above
1227 run(iterator_range.begin(),
1228 iterator_range.end(),
1229 worker,
1230 copier,
1231 sample_scratch_data,
1232 sample_copy_data,
1233 queue_length,
1234 chunk_size);
1235 }
1236
1237
1238
1242 template <typename Worker,
1243 typename Copier,
1244 typename Iterator,
1245 typename ScratchData,
1246 typename CopyData>
1247 void
1248 run(const IteratorRange<Iterator> &iterator_range,
1249 Worker worker,
1250 Copier copier,
1251 const ScratchData & sample_scratch_data,
1252 const CopyData & sample_copy_data,
1253 const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1254 const unsigned int chunk_size = 8)
1255 {
1256 // Call the function above
1257 run(iterator_range.begin(),
1258 iterator_range.end(),
1259 worker,
1260 copier,
1261 sample_scratch_data,
1262 sample_copy_data,
1263 queue_length,
1264 chunk_size);
1265 }
1266
1267
1268
1269 template <typename Worker,
1270 typename Copier,
1271 typename Iterator,
1272 typename ScratchData,
1273 typename CopyData>
1274 void
1275 run(const std::vector<std::vector<Iterator>> &colored_iterators,
1276 Worker worker,
1277 Copier copier,
1278 const ScratchData & sample_scratch_data,
1279 const CopyData & sample_copy_data,
1280 const unsigned int queue_length,
1281 const unsigned int chunk_size)
1282 {
1283 Assert(queue_length > 0,
1284 ExcMessage("The queue length must be at least one, and preferably "
1285 "larger than the number of processors on this system."));
1286 (void)queue_length; // removes -Wunused-parameter warning in optimized mode
1287 Assert(chunk_size > 0, ExcMessage("The chunk_size must be at least one."));
1288 (void)chunk_size; // removes -Wunused-parameter warning in optimized mode
1289
1290
1292 {
1293# ifdef DEAL_II_WITH_TBB
1294 internal::tbb_colored::run(colored_iterators,
1295 worker,
1296 copier,
1297 sample_scratch_data,
1298 sample_copy_data,
1299 chunk_size);
1300
1301 // exit this function to not run the sequential version below:
1302 return;
1303# endif
1304 }
1305
1306 // run all colors sequentially:
1307 {
1308 internal::sequential::run(colored_iterators,
1309 worker,
1310 copier,
1311 sample_scratch_data,
1312 sample_copy_data);
1313 }
1314 }
1315
1316
1317
1359 template <typename MainClass,
1360 typename Iterator,
1361 typename ScratchData,
1362 typename CopyData>
1363 void
1364 run(const Iterator & begin,
1365 const typename identity<Iterator>::type &end,
1366 MainClass & main_object,
1367 void (MainClass::*worker)(const Iterator &, ScratchData &, CopyData &),
1368 void (MainClass::*copier)(const CopyData &),
1369 const ScratchData &sample_scratch_data,
1370 const CopyData & sample_copy_data,
1371 const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1372 const unsigned int chunk_size = 8)
1373 {
1374 // forward to the other function
1375 run(
1376 begin,
1377 end,
1378 [&main_object, worker](const Iterator &iterator,
1379 ScratchData & scratch_data,
1380 CopyData & copy_data) {
1381 (main_object.*worker)(iterator, scratch_data, copy_data);
1382 },
1383 [&main_object, copier](const CopyData &copy_data) {
1384 (main_object.*copier)(copy_data);
1385 },
1386 sample_scratch_data,
1387 sample_copy_data,
1388 queue_length,
1389 chunk_size);
1390 }
1391
1392
1393 template <typename MainClass,
1394 typename Iterator,
1395 typename ScratchData,
1396 typename CopyData>
1397 void
1400 MainClass &main_object,
1401 void (MainClass::*worker)(const Iterator &, ScratchData &, CopyData &),
1402 void (MainClass::*copier)(const CopyData &),
1403 const ScratchData &sample_scratch_data,
1404 const CopyData & sample_copy_data,
1405 const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1406 const unsigned int chunk_size = 8)
1407 {
1408 // forward to the other function
1409 run(
1410 begin,
1411 end,
1412 [&main_object, worker](const Iterator &iterator,
1413 ScratchData & scratch_data,
1414 CopyData & copy_data) {
1415 (main_object.*worker)(iterator, scratch_data, copy_data);
1416 },
1417 [&main_object, copier](const CopyData &copy_data) {
1418 (main_object.*copier)(copy_data);
1419 },
1420 sample_scratch_data,
1421 sample_copy_data,
1422 queue_length,
1423 chunk_size);
1424 }
1425
1426
1427
1435 template <typename MainClass,
1436 typename IteratorRangeType,
1437 typename ScratchData,
1438 typename CopyData,
1439 typename = typename std::enable_if<
1440 has_begin_and_end<IteratorRangeType>>::type>
1441 void
1442 run(IteratorRangeType iterator_range,
1443 MainClass & main_object,
1444 void (MainClass::*worker)(
1446 ScratchData &,
1447 CopyData &),
1448 void (MainClass::*copier)(const CopyData &),
1449 const ScratchData &sample_scratch_data,
1450 const CopyData & sample_copy_data,
1451 const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1452 const unsigned int chunk_size = 8)
1453 {
1454 // Call the function above
1455 run(std::begin(iterator_range),
1456 std::end(iterator_range),
1457 main_object,
1458 worker,
1459 copier,
1460 sample_scratch_data,
1461 sample_copy_data,
1462 queue_length,
1463 chunk_size);
1464 }
1465
1466
1467
1471 template <typename MainClass,
1472 typename Iterator,
1473 typename ScratchData,
1474 typename CopyData>
1475 void
1477 MainClass & main_object,
1478 void (MainClass::*worker)(const Iterator &, ScratchData &, CopyData &),
1479 void (MainClass::*copier)(const CopyData &),
1480 const ScratchData &sample_scratch_data,
1481 const CopyData & sample_copy_data,
1482 const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1483 const unsigned int chunk_size = 8)
1484 {
1485 // Call the function above
1486 run(std::begin(iterator_range),
1487 std::end(iterator_range),
1488 main_object,
1489 worker,
1490 copier,
1491 sample_scratch_data,
1492 sample_copy_data,
1493 queue_length,
1494 chunk_size);
1495 }
1496
1497} // namespace WorkStream
1498
1499
1500
1502
1503
1504
1505//---------------------------- work_stream.h ---------------------------
1506// end of #ifndef dealii_work_stream_h
1507#endif
1508//---------------------------- work_stream.h ---------------------------
IteratorOverIterators end() const
IteratorOverIterators begin()
static unsigned int n_threads()
A class that provides a separate storage location on each thread that accesses the object.
std::list< ScratchAndCopyDataObjects > ScratchAndCopyDataList
Definition: work_stream.h:935
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:820
Threads::ThreadLocalStorage< ScratchAndCopyDataList > data
Definition: work_stream.h:937
void operator()(const tbb::blocked_range< typename std::vector< Iterator >::const_iterator > &range)
Definition: work_stream.h:838
const std::function< void(const Iterator &, ScratchData &, CopyData &)> worker
Definition: work_stream.h:944
typename tbb_colored::ScratchAndCopyDataObjects< Iterator, ScratchData, CopyData > ScratchAndCopyDataObjects
Definition: work_stream.h:929
const std::function< void(const CopyData &)> copier
Definition: work_stream.h:950
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:325
Threads::ThreadLocalStorage< typename ItemType::ScratchDataList > thread_local_scratch
Definition: work_stream.h:448
#define DEAL_II_NAMESPACE_OPEN
Definition: config.h:442
#define DEAL_II_DISABLE_EXTRA_DIAGNOSTICS
Definition: config.h:456
#define DEAL_II_TBB_WITH_ONEAPI
Definition: config.h:78
#define DEAL_II_NAMESPACE_CLOSE
Definition: config.h:443
#define DEAL_II_ENABLE_EXTRA_DIAGNOSTICS
Definition: config.h:495
#define Assert(cond, exc)
Definition: exceptions.h:1473
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcMessage(std::string arg1)
void handle_std_exception(const std::exception &exc)
void run(const Iterator &begin, const typename identity< Iterator >::type &end, Worker worker, Copier copier, const ScratchData &sample_scratch_data, const CopyData &sample_copy_data)
Definition: work_stream.h:683
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 chunk_size)
Definition: work_stream.h:968
void run(const Iterator &begin, const typename identity< Iterator >::type &end, Worker worker, Copier copier, const ScratchData &sample_scratch_data, const CopyData &sample_copy_data, const unsigned int queue_length, const unsigned int chunk_size)
Definition: work_stream.h:474
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:1275
void parallel_for(Iterator x_begin, Iterator x_end, const Functor &functor, const unsigned int grainsize)
Definition: parallel.h:80
STL namespace.
ScratchAndCopyDataObjects(const ScratchAndCopyDataObjects &)
Definition: work_stream.h:801
ScratchAndCopyDataObjects(std::unique_ptr< ScratchData > &&p, std::unique_ptr< CopyData > &&q, const bool in_use)
Definition: work_stream.h:790
Threads::ThreadLocalStorage< ScratchDataList > * scratch_data
Definition: work_stream.h:292