Reference documentation for deal.II version GIT relicensing-399-g79d89019c5 2024-04-16 15:00: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 // Create 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 runs 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 tbb_item_stream_filter = tbb::make_filter<void, ItemType *>(
499# ifdef DEAL_II_TBB_WITH_ONEAPI
500 tbb::filter_mode::serial_in_order,
501# else
502 tbb::filter::serial,
503# endif
504 [&](tbb::flow_control &fc) -> ItemType * {
505 if (const auto item = iterator_range_to_item_stream.get_item())
506 return item;
507 else
508 {
509 fc.stop();
510 return nullptr;
511 }
512 });
513
514 //
515 // ----- Stage 2 -----
516 //
517 // The second stage is the one that does the actual work. This is the
518 // stage that runs in parallel
519 auto tbb_worker_filter = tbb::make_filter<ItemType *, ItemType *>(
520# ifdef DEAL_II_TBB_WITH_ONEAPI
521 tbb::filter_mode::parallel,
522# else
523 tbb::filter::parallel,
524# endif
525 [worker =
526 std::function<void(const Iterator &, ScratchData &, CopyData &)>(
527 worker),
528 copier_exists =
529 static_cast<bool>(std::function<void(const CopyData &)>(copier))](
530 ItemType *current_item) {
531 // we need to find an unused scratch data object in the list that
532 // corresponds to the current thread and then mark it as used. if
533 // we can't find one, create one
534 //
535 // as discussed in the discussion of the documentation of the
536 // IteratorRangeToItemStream::scratch_data variable, there is no
537 // need to synchronize access to this variable using a mutex
538 // as long as we have no yield-point in between. this means that
539 // we can't take an iterator into the list now and expect it to
540 // still be valid after calling the worker, but we at least do
541 // not have to lock the following section
542 ScratchData *scratch_data = nullptr;
543 {
544 // see if there is an unused object. if so, grab it and mark
545 // it as used
546 for (auto &p : current_item->scratch_data->get())
547 if (p.currently_in_use == false)
548 {
549 scratch_data = p.scratch_data.get();
550 p.currently_in_use = true;
551
552 break;
553 }
554
555 // if no object was found, create one and mark it as used
556 if (scratch_data == nullptr)
557 {
558 scratch_data =
559 new ScratchData(*current_item->sample_scratch_data);
560 current_item->scratch_data->get().emplace_back(scratch_data,
561 true);
562 }
563 }
564
565 // then call the worker function on each element of the chunk we
566 // were given. since these worker functions are called on separate
567 // threads, nothing good can happen if they throw an exception and
568 // we are best off catching it and showing an error message
569 for (unsigned int i = 0; i < current_item->n_iterators; ++i)
570 {
571 try
572 {
573 if (worker)
574 worker(current_item->iterators[i],
575 *scratch_data,
576 current_item->copy_datas[i]);
577 }
578 catch (const std::exception &exc)
579 {
581 }
582 catch (...)
583 {
585 }
586 }
587
588 // finally mark the scratch object as unused again. as above, there
589 // is no need to lock anything here since the object we work on
590 // is thread-local
591 for (auto &p : current_item->scratch_data->get())
592 if (p.scratch_data.get() == scratch_data)
593 {
594 Assert(p.currently_in_use == true, ExcInternalError());
595 p.currently_in_use = false;
596
597 break;
598 }
599
600 // if there is no copier, mark current item as usable again
601 if (copier_exists == false)
602 current_item->currently_in_use = false;
603
604
605 // Then return the original pointer
606 // to the now modified object. The copier will work on it next.
607 return current_item;
608 });
609
610
611 //
612 // ----- Stage 3 -----
613 //
614 // The last stage is the one that copies data from the CopyData objects
615 // to the final destination. This stage runs sequentially again.
616 auto tbb_copier_filter = tbb::make_filter<ItemType *, void>(
617# ifdef DEAL_II_TBB_WITH_ONEAPI
618 tbb::filter_mode::serial_in_order,
619# else
620 tbb::filter::serial,
621# endif
622 [copier = std::function<void(const CopyData &)>(copier)](
623 ItemType *current_item) {
624 if (copier)
625 {
626 // Initiate copying data. For the same reasons as in the worker
627 // class above, catch exceptions rather than letting them
628 // propagate into unknown territories:
629 for (unsigned int i = 0; i < current_item->n_iterators; ++i)
630 {
631 try
632 {
633 copier(current_item->copy_datas[i]);
634 }
635 catch (const std::exception &exc)
636 {
638 }
639 catch (...)
640 {
642 }
643 }
644 }
645 // mark current item as usable again
646 current_item->currently_in_use = false;
647 });
648
649
650 //
651 // ----- The pipeline -----
652 //
653 // Now create a pipeline from these stages and execute it:
654 tbb::parallel_pipeline(queue_length,
655 tbb_item_stream_filter & tbb_worker_filter &
656 tbb_copier_filter);
657 }
658
659 } // namespace tbb_no_coloring
660# endif // DEAL_II_WITH_TBB
661
662
669 namespace sequential
670 {
674 template <typename Worker,
675 typename Copier,
676 typename Iterator,
677 typename ScratchData,
678 typename CopyData>
679 void
680 run(const Iterator &begin,
682 Worker worker,
683 Copier copier,
684 const ScratchData &sample_scratch_data,
685 const CopyData &sample_copy_data)
686 {
687 // need to copy the sample since it is marked const
688 ScratchData scratch_data = sample_scratch_data;
689 CopyData copy_data = sample_copy_data; // NOLINT
690
691 // Optimization: Check if the functions are not the zero function. To
692 // check zero-ness, create a C++ function out of it:
693 const bool have_worker =
694 (static_cast<const std::function<
695 void(const Iterator &, ScratchData &, CopyData &)> &>(worker)) !=
696 nullptr;
697 const bool have_copier =
698 (static_cast<const std::function<void(const CopyData &)> &>(
699 copier)) != nullptr;
700
701 // Finally loop over all items and perform the necessary work:
702 for (Iterator i = begin; i != end; ++i)
703 {
704 if (have_worker)
705 worker(i, scratch_data, copy_data);
706 if (have_copier)
707 copier(copy_data);
708 }
709 }
710
711
712
716 template <typename Worker,
717 typename Copier,
718 typename Iterator,
719 typename ScratchData,
720 typename CopyData>
721 void
722 run(const std::vector<std::vector<Iterator>> &colored_iterators,
723 Worker worker,
724 Copier copier,
725 const ScratchData &sample_scratch_data,
726 const CopyData &sample_copy_data)
727 {
728 // need to copy the sample since it is marked const
729 ScratchData scratch_data = sample_scratch_data;
730 CopyData copy_data = sample_copy_data; // NOLINT
731
732 // Optimization: Check if the functions are not the zero function. To
733 // check zero-ness, create a C++ function out of it:
734 const bool have_worker =
735 (static_cast<const std::function<
736 void(const Iterator &, ScratchData &, CopyData &)> &>(worker)) !=
737 nullptr;
738 const bool have_copier =
739 (static_cast<const std::function<void(const CopyData &)> &>(
740 copier)) != nullptr;
741
742 // Finally loop over all items and perform the necessary work:
743 for (unsigned int color = 0; color < colored_iterators.size(); ++color)
744 if (colored_iterators[color].size() > 0)
745 for (auto &it : colored_iterators[color])
746 {
747 if (have_worker)
748 worker(it, scratch_data, copy_data);
749 if (have_copier)
750 copier(copy_data);
751 }
752 }
753
754 } // namespace sequential
755
756
757
758# ifdef DEAL_II_WITH_TBB
766 namespace tbb_colored
767 {
773 template <typename Iterator, typename ScratchData, typename CopyData>
775 {
776 std::unique_ptr<ScratchData> scratch_data;
777 std::unique_ptr<CopyData> copy_data;
779
786
787 ScratchAndCopyDataObjects(std::unique_ptr<ScratchData> &&p,
788 std::unique_ptr<CopyData> &&q,
789 const bool in_use)
790 : scratch_data(std::move(p))
791 , copy_data(std::move(q))
792 , currently_in_use(in_use)
793 {}
794
795 // Provide a copy constructor that actually doesn't copy the
796 // internal state. This makes handling ScratchAndCopyDataObjects
797 // easier to handle with STL containers.
801 };
802
803
804
810 template <typename Iterator, typename ScratchData, typename CopyData>
812 {
813 public:
818 const std::function<void(const Iterator &, ScratchData &, CopyData &)>
819 &worker,
820 const std::function<void(const CopyData &)> &copier,
821 const ScratchData &sample_scratch_data,
822 const CopyData &sample_copy_data)
823 : worker(worker)
824 , copier(copier)
827 {}
828
829
834 void
835 operator()(const tbb::blocked_range<
836 typename std::vector<Iterator>::const_iterator> &range)
837 {
838 // we need to find an unused scratch and corresponding copy
839 // data object in the list that corresponds to the current
840 // thread and then mark it as used. If we can't find one,
841 // create one as discussed in the discussion of the documentation
842 // of the IteratorRangeToItemStream::scratch_data variable,
843 // there is no need to synchronize access to this variable
844 // using a mutex as long as we have no yield-point in between.
845 // This means that we can't take an iterator into the list
846 // now and expect it to still be valid after calling the worker,
847 // but we at least do not have to lock the following section.
848 ScratchData *scratch_data = nullptr;
849 CopyData *copy_data = nullptr;
850 {
851 ScratchAndCopyDataList &scratch_and_copy_data_list = data.get();
852
853 // see if there is an unused object. if so, grab it and mark
854 // it as used
855 for (typename ScratchAndCopyDataList::iterator p =
856 scratch_and_copy_data_list.begin();
857 p != scratch_and_copy_data_list.end();
858 ++p)
859 if (p->currently_in_use == false)
860 {
861 scratch_data = p->scratch_data.get();
862 copy_data = p->copy_data.get();
863 p->currently_in_use = true;
864 break;
865 }
866
867 // if no element in the list was found, create one and mark it as
868 // used
869 if (scratch_data == nullptr)
870 {
871 Assert(copy_data == nullptr, ExcInternalError());
872
873 scratch_and_copy_data_list.emplace_back(
874 std::make_unique<ScratchData>(sample_scratch_data),
875 std::make_unique<CopyData>(sample_copy_data),
876 true);
877 scratch_data =
878 scratch_and_copy_data_list.back().scratch_data.get();
879 copy_data = scratch_and_copy_data_list.back().copy_data.get();
880 }
881 }
882
883 // then call the worker and copier functions on each
884 // element of the chunk we were given.
885 for (typename std::vector<Iterator>::const_iterator p = range.begin();
886 p != range.end();
887 ++p)
888 {
889 try
890 {
891 if (worker)
892 worker(*p, *scratch_data, *copy_data);
893 if (copier)
894 copier(*copy_data);
895 }
896 catch (const std::exception &exc)
897 {
899 }
900 catch (...)
901 {
903 }
904 }
905
906 // finally mark the scratch object as unused again. as above, there
907 // is no need to lock anything here since the object we work on
908 // is thread-local
909 {
910 ScratchAndCopyDataList &scratch_and_copy_data_list = data.get();
911
912 for (typename ScratchAndCopyDataList::iterator p =
913 scratch_and_copy_data_list.begin();
914 p != scratch_and_copy_data_list.end();
915 ++p)
916 if (p->scratch_data.get() == scratch_data)
917 {
918 Assert(p->currently_in_use == true, ExcInternalError());
919 p->currently_in_use = false;
920 }
921 }
922 }
923
924 private:
925 using ScratchAndCopyDataObjects = typename tbb_colored::
926 ScratchAndCopyDataObjects<Iterator, ScratchData, CopyData>;
927
932 using ScratchAndCopyDataList = std::list<ScratchAndCopyDataObjects>;
933
935
940 const std::function<void(const Iterator &, ScratchData &, CopyData &)>
942
947 const std::function<void(const CopyData &)> copier;
948
952 const ScratchData &sample_scratch_data;
953 const CopyData &sample_copy_data;
954 };
955
959 template <typename Worker,
960 typename Copier,
961 typename Iterator,
962 typename ScratchData,
963 typename CopyData>
964 void
965 run(const std::vector<std::vector<Iterator>> &colored_iterators,
966 Worker worker,
967 Copier copier,
968 const ScratchData &sample_scratch_data,
969 const CopyData &sample_copy_data,
970 const unsigned int chunk_size)
971 {
972 // loop over the various colors of what we're given
973 for (unsigned int color = 0; color < colored_iterators.size(); ++color)
974 if (colored_iterators[color].size() > 0)
975 {
976 using WorkerAndCopier = internal::tbb_colored::
977 WorkerAndCopier<Iterator, ScratchData, CopyData>;
978
979 WorkerAndCopier worker_and_copier(worker,
980 copier,
981 sample_scratch_data,
982 sample_copy_data);
983
985 colored_iterators[color].begin(),
986 colored_iterators[color].end(),
987 [&worker_and_copier](
988 const tbb::blocked_range<
989 typename std::vector<Iterator>::const_iterator> &range) {
990 worker_and_copier(range);
991 },
992 chunk_size);
993 }
994 }
995
996 } // namespace tbb_colored
997# endif // DEAL_II_WITH_TBB
998
999
1000 } // namespace internal
1001
1002
1003
1051 template <typename Worker,
1052 typename Copier,
1053 typename Iterator,
1054 typename ScratchData,
1055 typename CopyData>
1056 void
1057 run(const std::vector<std::vector<Iterator>> &colored_iterators,
1058 Worker worker,
1059 Copier copier,
1060 const ScratchData &sample_scratch_data,
1061 const CopyData &sample_copy_data,
1062 const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1063 const unsigned int chunk_size = 8);
1064
1065
1115 template <typename Worker,
1116 typename Copier,
1117 typename Iterator,
1118 typename ScratchData,
1119 typename CopyData>
1120 void
1121 run(const Iterator &begin,
1123 Worker worker,
1124 Copier copier,
1125 const ScratchData &sample_scratch_data,
1126 const CopyData &sample_copy_data,
1127 const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1128 const unsigned int chunk_size = 8)
1129 {
1130 Assert(queue_length > 0,
1131 ExcMessage("The queue length must be at least one, and preferably "
1132 "larger than the number of processors on this system."));
1133 (void)queue_length; // removes -Wunused-parameter warning in optimized mode
1134 Assert(chunk_size > 0, ExcMessage("The chunk_size must be at least one."));
1135 (void)chunk_size; // removes -Wunused-parameter warning in optimized mode
1136
1137 // If no work then skip. (only use operator!= for iterators since we may
1138 // not have an equality comparison operator)
1139 if (!(begin != end))
1140 return;
1141
1143 {
1144# ifdef DEAL_II_WITH_TBB
1145 if (static_cast<const std::function<void(const CopyData &)> &>(copier))
1146 {
1147 // If we have a copier, run the algorithm:
1149 end,
1150 worker,
1151 copier,
1152 sample_scratch_data,
1153 sample_copy_data,
1154 queue_length,
1155 chunk_size);
1156 }
1157 else
1158 {
1159 // There is no copier function. in this case, we have an
1160 // embarrassingly parallel problem where we can
1161 // essentially apply parallel_for. because parallel_for
1162 // requires subdividing the range for which operator- is
1163 // necessary between iterators, it is often inefficient to
1164 // apply it directly to cell ranges and similar iterator
1165 // types for which operator- is expensive or, in fact,
1166 // nonexistent. rather, in that case, we simply copy the
1167 // iterators into a large array and use operator- on
1168 // iterators to this array of iterators.
1169 //
1170 // instead of duplicating code, this is essentially the
1171 // same situation we have in the colored implementation below, so we
1172 // just defer to that place
1173 std::vector<std::vector<Iterator>> all_iterators(1);
1174 for (Iterator p = begin; p != end; ++p)
1175 all_iterators[0].push_back(p);
1176
1177 run(all_iterators,
1178 worker,
1179 copier,
1180 sample_scratch_data,
1181 sample_copy_data,
1182 queue_length,
1183 chunk_size);
1184 }
1185
1186 // exit this function to not run the sequential version below:
1187 return;
1188# endif
1189 }
1190
1191 // no TBB installed or we are requested to run sequentially:
1193 begin, end, worker, copier, sample_scratch_data, sample_copy_data);
1194 }
1195
1196
1197
1205 template <typename Worker,
1206 typename Copier,
1207 typename IteratorRangeType,
1208 typename ScratchData,
1209 typename CopyData,
1210 typename = std::enable_if_t<has_begin_and_end<IteratorRangeType>>>
1211 void
1212 run(IteratorRangeType iterator_range,
1213 Worker worker,
1214 Copier copier,
1215 const ScratchData &sample_scratch_data,
1216 const CopyData &sample_copy_data,
1217 const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1218 const unsigned int chunk_size = 8)
1219 {
1220 // Call the function above
1221 run(iterator_range.begin(),
1222 iterator_range.end(),
1223 worker,
1224 copier,
1225 sample_scratch_data,
1226 sample_copy_data,
1227 queue_length,
1228 chunk_size);
1229 }
1230
1231
1232
1236 template <typename Worker,
1237 typename Copier,
1238 typename Iterator,
1239 typename ScratchData,
1240 typename CopyData>
1241 void
1242 run(const IteratorRange<Iterator> &iterator_range,
1243 Worker worker,
1244 Copier copier,
1245 const ScratchData &sample_scratch_data,
1246 const CopyData &sample_copy_data,
1247 const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1248 const unsigned int chunk_size = 8)
1249 {
1250 // Call the function above
1251 run(iterator_range.begin(),
1252 iterator_range.end(),
1253 worker,
1254 copier,
1255 sample_scratch_data,
1256 sample_copy_data,
1257 queue_length,
1258 chunk_size);
1259 }
1260
1261
1262
1263 template <typename Worker,
1264 typename Copier,
1265 typename Iterator,
1266 typename ScratchData,
1267 typename CopyData>
1268 void
1269 run(const std::vector<std::vector<Iterator>> &colored_iterators,
1270 Worker worker,
1271 Copier copier,
1272 const ScratchData &sample_scratch_data,
1273 const CopyData &sample_copy_data,
1274 const unsigned int queue_length,
1275 const unsigned int chunk_size)
1276 {
1277 Assert(queue_length > 0,
1278 ExcMessage("The queue length must be at least one, and preferably "
1279 "larger than the number of processors on this system."));
1280 (void)queue_length; // removes -Wunused-parameter warning in optimized mode
1281 Assert(chunk_size > 0, ExcMessage("The chunk_size must be at least one."));
1282 (void)chunk_size; // removes -Wunused-parameter warning in optimized mode
1283
1284
1286 {
1287# ifdef DEAL_II_WITH_TBB
1288 internal::tbb_colored::run(colored_iterators,
1289 worker,
1290 copier,
1291 sample_scratch_data,
1292 sample_copy_data,
1293 chunk_size);
1294
1295 // exit this function to not run the sequential version below:
1296 return;
1297# endif
1298 }
1299
1300 // run all colors sequentially:
1301 {
1302 internal::sequential::run(colored_iterators,
1303 worker,
1304 copier,
1305 sample_scratch_data,
1306 sample_copy_data);
1307 }
1308 }
1309
1310
1311
1353 template <typename MainClass,
1354 typename Iterator,
1355 typename ScratchData,
1356 typename CopyData>
1357 void
1358 run(const Iterator &begin,
1360 MainClass &main_object,
1361 void (MainClass::*worker)(const Iterator &, ScratchData &, CopyData &),
1362 void (MainClass::*copier)(const CopyData &),
1363 const ScratchData &sample_scratch_data,
1364 const CopyData &sample_copy_data,
1365 const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1366 const unsigned int chunk_size = 8)
1367 {
1368 // forward to the other function
1369 run(
1370 begin,
1371 end,
1372 [&main_object, worker](const Iterator &iterator,
1373 ScratchData &scratch_data,
1374 CopyData &copy_data) {
1375 (main_object.*worker)(iterator, scratch_data, copy_data);
1376 },
1377 [&main_object, copier](const CopyData &copy_data) {
1378 (main_object.*copier)(copy_data);
1379 },
1380 sample_scratch_data,
1381 sample_copy_data,
1382 queue_length,
1383 chunk_size);
1384 }
1385
1386
1387 template <typename MainClass,
1388 typename Iterator,
1389 typename ScratchData,
1390 typename CopyData>
1391 void
1394 MainClass &main_object,
1395 void (MainClass::*worker)(const Iterator &, ScratchData &, CopyData &),
1396 void (MainClass::*copier)(const CopyData &),
1397 const ScratchData &sample_scratch_data,
1398 const CopyData &sample_copy_data,
1399 const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1400 const unsigned int chunk_size = 8)
1401 {
1402 // forward to the other function
1403 run(
1404 begin,
1405 end,
1406 [&main_object, worker](const Iterator &iterator,
1407 ScratchData &scratch_data,
1408 CopyData &copy_data) {
1409 (main_object.*worker)(iterator, scratch_data, copy_data);
1410 },
1411 [&main_object, copier](const CopyData &copy_data) {
1412 (main_object.*copier)(copy_data);
1413 },
1414 sample_scratch_data,
1415 sample_copy_data,
1416 queue_length,
1417 chunk_size);
1418 }
1419
1420
1421
1429 template <typename MainClass,
1430 typename IteratorRangeType,
1431 typename ScratchData,
1432 typename CopyData,
1433 typename = std::enable_if_t<has_begin_and_end<IteratorRangeType>>>
1434 void
1436 IteratorRangeType iterator_range,
1437 MainClass &main_object,
1438 void (MainClass::*worker)(
1439 const typename std_cxx20::type_identity_t<IteratorRangeType>::iterator &,
1440 ScratchData &,
1441 CopyData &),
1442 void (MainClass::*copier)(const CopyData &),
1443 const ScratchData &sample_scratch_data,
1444 const CopyData &sample_copy_data,
1445 const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1446 const unsigned int chunk_size = 8)
1447 {
1448 // Call the function above
1449 run(std::begin(iterator_range),
1450 std::end(iterator_range),
1451 main_object,
1452 worker,
1453 copier,
1454 sample_scratch_data,
1455 sample_copy_data,
1456 queue_length,
1457 chunk_size);
1458 }
1459
1460
1461
1465 template <typename MainClass,
1466 typename Iterator,
1467 typename ScratchData,
1468 typename CopyData>
1469 void
1471 MainClass &main_object,
1472 void (MainClass::*worker)(const Iterator &, ScratchData &, CopyData &),
1473 void (MainClass::*copier)(const CopyData &),
1474 const ScratchData &sample_scratch_data,
1475 const CopyData &sample_copy_data,
1476 const unsigned int queue_length = 2 * MultithreadInfo::n_threads(),
1477 const unsigned int chunk_size = 8)
1478 {
1479 // Call the function above
1480 run(std::begin(iterator_range),
1481 std::end(iterator_range),
1482 main_object,
1483 worker,
1484 copier,
1485 sample_scratch_data,
1486 sample_copy_data,
1487 queue_length,
1488 chunk_size);
1489 }
1490
1491} // namespace WorkStream
1492
1493
1494
1496
1497
1498
1499//---------------------------- work_stream.h ---------------------------
1500// end of #ifndef dealii_work_stream_h
1501#endif
1502//---------------------------- 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