Reference documentation for deal.II version GIT relicensing-878-g25c11e0287 2024-06-21 14:50:02+00:00
\(\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// SPDX-License-Identifier: LGPL-2.1-or-later
4// Copyright (C) 2009 - 2023 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Part of the source code is dual licensed under Apache-2.0 WITH
9// LLVM-exception OR LGPL-2.1-or-later. Detailed license information
10// governing the source code and code contributions can be found in
11// LICENSE.md and CONTRIBUTING.md at the top level directory of deal.II.
12//
13// ------------------------------------------------------------------------
14
15#ifndef dealii_work_stream_h
16# define dealii_work_stream_h
17
18
19# include <deal.II/base/config.h>
20
28
29# ifdef DEAL_II_WITH_TBB
30# ifdef DEAL_II_TBB_WITH_ONEAPI
31# include <tbb/parallel_pipeline.h>
32# else
33# include <tbb/pipeline.h>
34# endif
35# endif
36
37# include <functional>
38# include <iterator>
39# include <memory>
40# include <utility>
41# include <vector>
42
44
45
46
159namespace WorkStream
160{
165 namespace internal
166 {
167# ifdef DEAL_II_WITH_TBB
182 namespace tbb_no_coloring
183 {
187 template <typename Iterator, typename ScratchData, typename CopyData>
189 {
190 public:
197 struct ItemType
198 {
205 {
206 std::unique_ptr<ScratchData> scratch_data;
208
213 : currently_in_use(false)
214 {}
215
216 ScratchDataObject(ScratchData *p, const bool in_use)
217 : scratch_data(p)
218 , currently_in_use(in_use)
219 {}
220
221 // Provide a copy constructor that actually doesn't copy the
222 // internal state. This makes handling ScratchAndCopyDataObjects
223 // easier to handle with STL containers.
227
228 ScratchDataObject(ScratchDataObject &&o) noexcept = default;
229 };
230
231
236 using ScratchDataList = std::list<ScratchDataObject>;
237
242 std::vector<Iterator> iterators;
243
249 std::vector<CopyData> copy_datas;
250
256 unsigned int n_iterators;
257
290
295 const ScratchData *sample_scratch_data;
296
302
303
309 : n_iterators(0)
310 , scratch_data(nullptr)
311 , sample_scratch_data(nullptr)
312 , currently_in_use(false)
313 {}
314 };
315
316
322 IteratorRangeToItemStream(const Iterator &begin,
323 const Iterator &end,
324 const unsigned int buffer_size,
325 const unsigned int chunk_size,
326 const ScratchData &sample_scratch_data,
327 const CopyData &sample_copy_data)
328 : remaining_iterator_range(begin, end)
329 , item_buffer(buffer_size)
332 {
333 // initialize the elements of the ring buffer
334 for (auto &item : item_buffer)
335 {
336 Assert(item.n_iterators == 0, ExcInternalError());
337
338 item.iterators.resize(chunk_size,
340 item.scratch_data = &thread_local_scratch;
341 item.sample_scratch_data = &sample_scratch_data;
342 item.copy_datas.resize(chunk_size, sample_copy_data);
343 item.currently_in_use = false;
344 }
345 }
346
347
351 ItemType *
353 {
354 // find first unused item. we know that there must be one
355 // because we have set the maximal number of tokens in flight
356 // and have set the ring buffer to have exactly this size. so
357 // if this function is called, we know that less than the
358 // maximal number of items in currently in flight
359 //
360 // note that we need not lock access to this array since
361 // the current stage is run sequentially and we can therefore
362 // enter the following block only once at any given time.
363 // thus, there can be no race condition between checking that
364 // a flag is false and setting it to true. (there may be
365 // another thread where we release items and set 'false'
366 // flags to 'true', but that too does not produce any
367 // problems)
368 ItemType *current_item = nullptr;
369 for (unsigned int i = 0; i < item_buffer.size(); ++i)
370 if (item_buffer[i].currently_in_use == false)
371 {
373 current_item = &item_buffer[i];
374 break;
375 }
376 Assert(current_item != nullptr,
377 ExcMessage("This can't be. There must be a free item!"));
378
379 // initialize the next item. it may
380 // consist of at most chunk_size
381 // elements
382 current_item->n_iterators = 0;
383 while ((remaining_iterator_range.first !=
384 remaining_iterator_range.second) &&
385 (current_item->n_iterators < chunk_size))
386 {
387 current_item->iterators[current_item->n_iterators] =
389
391 ++current_item->n_iterators;
392 }
393
394 if (current_item->n_iterators == 0)
395 // there were no items
396 // left. terminate the pipeline
397 return nullptr;
398 else
399 return current_item;
400 }
401
402 private:
407 std::pair<Iterator, Iterator> remaining_iterator_range;
408
412 std::vector<ItemType> item_buffer;
413
446
452 const ScratchData &sample_scratch_data;
453
460 const unsigned int chunk_size;
461 };
462
463
464
465 template <typename Worker,
466 typename Copier,
467 typename Iterator,
468 typename ScratchData,
469 typename CopyData>
470 void
471 run(const Iterator &begin,
473 Worker worker,
474 Copier copier,
475 const ScratchData &sample_scratch_data,
476 const CopyData &sample_copy_data,
477 const unsigned int queue_length,
478 const unsigned int chunk_size)
479 {
480 using ItemType = typename IteratorRangeToItemStream<Iterator,
481 ScratchData,
482 CopyData>::ItemType;
483
484 // Define the three stages of the pipeline:
485
486 //
487 // ----- Stage 1 -----
488 //
489 // The first stage is the one that provides us with chunks of data
490 // to work on (the stream of "items"). This stage will run sequentially.
492 iterator_range_to_item_stream(begin,
493 end,
494 queue_length,
495 chunk_size,
496 sample_scratch_data,
497 sample_copy_data);
498 auto item_generator = [&](tbb::flow_control &fc) -> ItemType * {
499 if (const auto item = iterator_range_to_item_stream.get_item())
500 return item;
501 else
502 {
503 fc.stop();
504 return nullptr;
505 }
506 };
507
508 //
509 // ----- Stage 2 -----
510 //
511 // The second stage is the one that does the actual work. This is the
512 // stage that runs in parallel
513 auto item_worker =
514 [worker =
515 std::function<void(const Iterator &, ScratchData &, CopyData &)>(
516 worker),
517 copier_exists =
518 static_cast<bool>(std::function<void(const CopyData &)>(copier))](
519 ItemType *current_item) {
520 // we need to find an unused scratch data object in the list that
521 // corresponds to the current thread and then mark it as used. if
522 // we can't find one, create one
523 //
524 // as discussed in the discussion of the documentation of the
525 // IteratorRangeToItemStream::scratch_data variable, there is no
526 // need to synchronize access to this variable using a mutex
527 // as long as we have no yield-point in between. this means that
528 // we can't take an iterator into the list now and expect it to
529 // still be valid after calling the worker, but we at least do
530 // not have to lock the following section
531 ScratchData *scratch_data = nullptr;
532 {
533 // see if there is an unused object. if so, grab it and mark
534 // it as used
535 for (auto &p : current_item->scratch_data->get())
536 if (p.currently_in_use == false)
537 {
538 scratch_data = p.scratch_data.get();
539 p.currently_in_use = true;
540
541 break;
542 }
543
544 // if no object was found, create one and mark it as used
545 if (scratch_data == nullptr)
546 {
547 scratch_data =
548 new ScratchData(*current_item->sample_scratch_data);
549 current_item->scratch_data->get().emplace_back(scratch_data,
550 true);
551 }
552 };
553
554 // then call the worker function on each element of the chunk we
555 // were given. since these worker functions are called on separate
556 // threads, nothing good can happen if they throw an exception and
557 // we are best off catching it and showing an error message
558 for (unsigned int i = 0; i < current_item->n_iterators; ++i)
559 {
560 try
561 {
562 if (worker)
563 worker(current_item->iterators[i],
564 *scratch_data,
565 current_item->copy_datas[i]);
566 }
567 catch (const std::exception &exc)
568 {
570 }
571 catch (...)
572 {
574 }
575 }
576
577 // finally mark the scratch object as unused again. as above, there
578 // is no need to lock anything here since the object we work on
579 // is thread-local
580 for (auto &p : current_item->scratch_data->get())
581 if (p.scratch_data.get() == scratch_data)
582 {
583 Assert(p.currently_in_use == true, ExcInternalError());
584 p.currently_in_use = false;
585
586 break;
587 }
588
589 // if there is no copier, mark current item as usable again
590 if (copier_exists == false)
591 current_item->currently_in_use = false;
592
593
594 // Then return the original pointer
595 // to the now modified object. The copier will work on it next.
596 return current_item;
597 };
598
599 //
600 // ----- Stage 3 -----
601 //
602 // The last stage is the one that copies data from the CopyData objects
603 // to the final destination. This stage runs sequentially again.
604 auto item_copier = [copier = std::function<void(const CopyData &)>(
605 copier)](ItemType *current_item) {
606 if (copier)
607 {
608 // Initiate copying data. For the same reasons as in the worker
609 // class above, catch exceptions rather than letting them
610 // propagate into unknown territories:
611 for (unsigned int i = 0; i < current_item->n_iterators; ++i)
612 {
613 try
614 {
615 copier(current_item->copy_datas[i]);
616 }
617 catch (const std::exception &exc)
618 {
620 }
621 catch (...)
622 {
624 }
625 }
626 }
627 // mark current item as usable again
628 current_item->currently_in_use = false;
629 };
630
631
632 // Now we just have to set up the pipeline and run it:
633 auto tbb_item_stream_filter = tbb::make_filter<void, ItemType *>(
634# ifdef DEAL_II_TBB_WITH_ONEAPI
635 tbb::filter_mode::serial_in_order,
636# else
637 tbb::filter::serial,
638# endif
639 item_generator);
640
641 auto tbb_worker_filter = tbb::make_filter<ItemType *, ItemType *>(
642# ifdef DEAL_II_TBB_WITH_ONEAPI
643 tbb::filter_mode::parallel,
644# else
645 tbb::filter::parallel,
646# endif
647 item_worker);
648
649 auto tbb_copier_filter = tbb::make_filter<ItemType *, void>(
650# ifdef DEAL_II_TBB_WITH_ONEAPI
651 tbb::filter_mode::serial_in_order,
652# else
653 tbb::filter::serial,
654# endif
655 item_copier);
656
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,
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
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.
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 WorkerAndCopier worker_and_copier(worker,
983 copier,
984 sample_scratch_data,
985 sample_copy_data);
986
988 colored_iterators[color].begin(),
989 colored_iterators[color].end(),
990 [&worker_and_copier](
991 const tbb::blocked_range<
992 typename std::vector<Iterator>::const_iterator> &range) {
993 worker_and_copier(range);
994 },
995 chunk_size);
996 }
997 }
998
999 } // namespace tbb_colored
1000# endif // DEAL_II_WITH_TBB
1001
1002
1003 } // namespace internal
1004
1005
1006
1054 template <typename Worker,
1055 typename Copier,
1056 typename Iterator,
1057 typename ScratchData,
1058 typename CopyData>
1059 void
1060 run(const std::vector<std::vector<Iterator>> &colored_iterators,
1061 Worker worker,
1062 Copier copier,
1063 const ScratchData &sample_scratch_data,
1064 const CopyData &sample_copy_data,
1065 const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1066 const unsigned int chunk_size = 8);
1067
1068
1118 template <typename Worker,
1119 typename Copier,
1120 typename Iterator,
1121 typename ScratchData,
1122 typename CopyData>
1123 void
1124 run(const Iterator &begin,
1126 Worker worker,
1127 Copier copier,
1128 const ScratchData &sample_scratch_data,
1129 const CopyData &sample_copy_data,
1130 const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1131 const unsigned int chunk_size = 8)
1132 {
1133 Assert(queue_length > 0,
1134 ExcMessage("The queue length must be at least one, and preferably "
1135 "larger than the number of processors on this system."));
1136 (void)queue_length; // removes -Wunused-parameter warning in optimized mode
1137 Assert(chunk_size > 0, ExcMessage("The chunk_size must be at least one."));
1138 (void)chunk_size; // removes -Wunused-parameter warning in optimized mode
1139
1140 // If no work then skip. (only use operator!= for iterators since we may
1141 // not have an equality comparison operator)
1142 if (!(begin != end))
1143 return;
1144
1146 {
1147# ifdef DEAL_II_WITH_TBB
1148 if (static_cast<const std::function<void(const CopyData &)> &>(copier))
1149 {
1150 // If we have a copier, run the algorithm:
1152 end,
1153 worker,
1154 copier,
1155 sample_scratch_data,
1156 sample_copy_data,
1157 queue_length,
1158 chunk_size);
1159 }
1160 else
1161 {
1162 // There is no copier function. in this case, we have an
1163 // embarrassingly parallel problem where we can
1164 // essentially apply parallel_for. because parallel_for
1165 // requires subdividing the range for which operator- is
1166 // necessary between iterators, it is often inefficient to
1167 // apply it directly to cell ranges and similar iterator
1168 // types for which operator- is expensive or, in fact,
1169 // nonexistent. rather, in that case, we simply copy the
1170 // iterators into a large array and use operator- on
1171 // iterators to this array of iterators.
1172 //
1173 // instead of duplicating code, this is essentially the
1174 // same situation we have in the colored implementation below, so we
1175 // just defer to that place
1176 std::vector<std::vector<Iterator>> all_iterators(1);
1177 for (Iterator p = begin; p != end; ++p)
1178 all_iterators[0].push_back(p);
1179
1180 run(all_iterators,
1181 worker,
1182 copier,
1183 sample_scratch_data,
1184 sample_copy_data,
1185 queue_length,
1186 chunk_size);
1187 }
1188
1189 // exit this function to not run the sequential version below:
1190 return;
1191# endif
1192 }
1193
1194 // no TBB installed or we are requested to run sequentially:
1196 begin, end, worker, copier, sample_scratch_data, sample_copy_data);
1197 }
1198
1199
1200
1208 template <typename Worker,
1209 typename Copier,
1210 typename IteratorRangeType,
1211 typename ScratchData,
1212 typename CopyData,
1213 typename = std::enable_if_t<has_begin_and_end<IteratorRangeType>>>
1214 void
1215 run(IteratorRangeType iterator_range,
1216 Worker worker,
1217 Copier copier,
1218 const ScratchData &sample_scratch_data,
1219 const CopyData &sample_copy_data,
1220 const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1221 const unsigned int chunk_size = 8)
1222 {
1223 // Call the function above
1224 run(iterator_range.begin(),
1225 iterator_range.end(),
1226 worker,
1227 copier,
1228 sample_scratch_data,
1229 sample_copy_data,
1230 queue_length,
1231 chunk_size);
1232 }
1233
1234
1235
1239 template <typename Worker,
1240 typename Copier,
1241 typename Iterator,
1242 typename ScratchData,
1243 typename CopyData>
1244 void
1245 run(const IteratorRange<Iterator> &iterator_range,
1246 Worker worker,
1247 Copier copier,
1248 const ScratchData &sample_scratch_data,
1249 const CopyData &sample_copy_data,
1250 const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1251 const unsigned int chunk_size = 8)
1252 {
1253 // Call the function above
1254 run(iterator_range.begin(),
1255 iterator_range.end(),
1256 worker,
1257 copier,
1258 sample_scratch_data,
1259 sample_copy_data,
1260 queue_length,
1261 chunk_size);
1262 }
1263
1264
1265
1266 template <typename Worker,
1267 typename Copier,
1268 typename Iterator,
1269 typename ScratchData,
1270 typename CopyData>
1271 void
1272 run(const std::vector<std::vector<Iterator>> &colored_iterators,
1273 Worker worker,
1274 Copier copier,
1275 const ScratchData &sample_scratch_data,
1276 const CopyData &sample_copy_data,
1277 const unsigned int queue_length,
1278 const unsigned int chunk_size)
1279 {
1280 Assert(queue_length > 0,
1281 ExcMessage("The queue length must be at least one, and preferably "
1282 "larger than the number of processors on this system."));
1283 (void)queue_length; // removes -Wunused-parameter warning in optimized mode
1284 Assert(chunk_size > 0, ExcMessage("The chunk_size must be at least one."));
1285 (void)chunk_size; // removes -Wunused-parameter warning in optimized mode
1286
1287
1289 {
1290# ifdef DEAL_II_WITH_TBB
1291 internal::tbb_colored::run(colored_iterators,
1292 worker,
1293 copier,
1294 sample_scratch_data,
1295 sample_copy_data,
1296 chunk_size);
1297
1298 // exit this function to not run the sequential version below:
1299 return;
1300# endif
1301 }
1302
1303 // run all colors sequentially:
1304 {
1305 internal::sequential::run(colored_iterators,
1306 worker,
1307 copier,
1308 sample_scratch_data,
1309 sample_copy_data);
1310 }
1311 }
1312
1313
1314
1356 template <typename MainClass,
1357 typename Iterator,
1358 typename ScratchData,
1359 typename CopyData>
1360 void
1361 run(const Iterator &begin,
1363 MainClass &main_object,
1364 void (MainClass::*worker)(const Iterator &, ScratchData &, CopyData &),
1365 void (MainClass::*copier)(const CopyData &),
1366 const ScratchData &sample_scratch_data,
1367 const CopyData &sample_copy_data,
1368 const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1369 const unsigned int chunk_size = 8)
1370 {
1371 // forward to the other function
1372 run(
1373 begin,
1374 end,
1375 [&main_object, worker](const Iterator &iterator,
1376 ScratchData &scratch_data,
1377 CopyData &copy_data) {
1378 (main_object.*worker)(iterator, scratch_data, copy_data);
1379 },
1380 [&main_object, copier](const CopyData &copy_data) {
1381 (main_object.*copier)(copy_data);
1382 },
1383 sample_scratch_data,
1384 sample_copy_data,
1385 queue_length,
1386 chunk_size);
1387 }
1388
1389
1390 template <typename MainClass,
1391 typename Iterator,
1392 typename ScratchData,
1393 typename CopyData>
1394 void
1397 MainClass &main_object,
1398 void (MainClass::*worker)(const Iterator &, ScratchData &, CopyData &),
1399 void (MainClass::*copier)(const CopyData &),
1400 const ScratchData &sample_scratch_data,
1401 const CopyData &sample_copy_data,
1402 const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1403 const unsigned int chunk_size = 8)
1404 {
1405 // forward to the other function
1406 run(
1407 begin,
1408 end,
1409 [&main_object, worker](const Iterator &iterator,
1410 ScratchData &scratch_data,
1411 CopyData &copy_data) {
1412 (main_object.*worker)(iterator, scratch_data, copy_data);
1413 },
1414 [&main_object, copier](const CopyData &copy_data) {
1415 (main_object.*copier)(copy_data);
1416 },
1417 sample_scratch_data,
1418 sample_copy_data,
1419 queue_length,
1420 chunk_size);
1421 }
1422
1423
1424
1432 template <typename MainClass,
1433 typename IteratorRangeType,
1434 typename ScratchData,
1435 typename CopyData,
1436 typename = std::enable_if_t<has_begin_and_end<IteratorRangeType>>>
1437 void
1439 IteratorRangeType iterator_range,
1440 MainClass &main_object,
1441 void (MainClass::*worker)(
1442 const typename std_cxx20::type_identity_t<IteratorRangeType>::iterator &,
1443 ScratchData &,
1444 CopyData &),
1445 void (MainClass::*copier)(const CopyData &),
1446 const ScratchData &sample_scratch_data,
1447 const CopyData &sample_copy_data,
1448 const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1449 const unsigned int chunk_size = 8)
1450 {
1451 // Call the function above
1452 run(std::begin(iterator_range),
1453 std::end(iterator_range),
1454 main_object,
1455 worker,
1456 copier,
1457 sample_scratch_data,
1458 sample_copy_data,
1459 queue_length,
1460 chunk_size);
1461 }
1462
1463
1464
1468 template <typename MainClass,
1469 typename Iterator,
1470 typename ScratchData,
1471 typename CopyData>
1472 void
1474 MainClass &main_object,
1475 void (MainClass::*worker)(const Iterator &, ScratchData &, CopyData &),
1476 void (MainClass::*copier)(const CopyData &),
1477 const ScratchData &sample_scratch_data,
1478 const CopyData &sample_copy_data,
1479 const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1480 const unsigned int chunk_size = 8)
1481 {
1482 // Call the function above
1483 run(std::begin(iterator_range),
1484 std::end(iterator_range),
1485 main_object,
1486 worker,
1487 copier,
1488 sample_scratch_data,
1489 sample_copy_data,
1490 queue_length,
1491 chunk_size);
1492 }
1493
1494} // namespace WorkStream
1495
1496
1497
1499
1500
1501
1502//---------------------------- work_stream.h ---------------------------
1503// end of #ifndef dealii_work_stream_h
1504#endif
1505//---------------------------- 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
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)
Threads::ThreadLocalStorage< ScratchAndCopyDataList > data
void operator()(const tbb::blocked_range< typename std::vector< Iterator >::const_iterator > &range)
const std::function< void(const Iterator &, ScratchData &, CopyData &)> worker
typename tbb_colored::ScratchAndCopyDataObjects< Iterator, ScratchData, CopyData > ScratchAndCopyDataObjects
const std::function< void(const CopyData &)> copier
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)
Threads::ThreadLocalStorage< typename ItemType::ScratchDataList > thread_local_scratch
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:502
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:503
#define Assert(cond, exc)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcMessage(std::string arg1)
void handle_std_exception(const std::exception &exc)
void run(const Iterator &begin, const std_cxx20::type_identity_t< Iterator > &end, Worker worker, Copier copier, const ScratchData &sample_scratch_data, const CopyData &sample_copy_data)
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)
void run(const Iterator &begin, const std_cxx20::type_identity_t< Iterator > &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)
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)
void parallel_for(Iterator x_begin, Iterator x_end, const Functor &functor, const unsigned int grainsize)
Definition parallel.h:83
typename type_identity< T >::type type_identity_t
Definition type_traits.h:95
STL namespace.
ScratchAndCopyDataObjects(const ScratchAndCopyDataObjects &)
ScratchAndCopyDataObjects(std::unique_ptr< ScratchData > &&p, std::unique_ptr< CopyData > &&q, const bool in_use)
Threads::ThreadLocalStorage< ScratchDataList > * scratch_data