deal.II version GIT relicensing-2577-g108be0c5b5 2025-02-07 13:10:00+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
parallel.h
Go to the documentation of this file.
1// ------------------------------------------------------------------------
2//
3// SPDX-License-Identifier: LGPL-2.1-or-later
4// Copyright (C) 2009 - 2024 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_parallel_h
16#define dealii_parallel_h
17
18
19#include <deal.II/base/config.h>
20
22#include <deal.II/base/mutex.h>
25
26#include <cstddef>
27#include <functional>
28#include <memory>
29#include <tuple>
30
31#ifdef DEAL_II_WITH_TASKFLOW
33
34# include <taskflow/algorithm/for_each.hpp>
35# include <taskflow/taskflow.hpp>
36#endif
37
38#ifdef DEAL_II_WITH_TBB
39# include <tbb/blocked_range.h>
40# include <tbb/parallel_for.h>
41# include <tbb/parallel_reduce.h>
42# include <tbb/partitioner.h>
43#else
44# include <boost/range/iterator_range.hpp>
45#endif
46
47#ifdef DEAL_II_HAVE_CXX20
48# include <concepts>
49#endif
50
51
52// TODO[WB]: allow calling functions to pass along a tbb::affinity_partitioner
53// object to ensure that subsequent calls use the same cache lines
54
56
57namespace parallel
58{
59 namespace internal
60 {
65 template <typename Number>
67 {
68 static const bool value = true;
69 };
70
71#ifdef __INTEL_COMPILER
72 // Disable long double SIMD instructions on ICC. This is to work around a
73 // bug that generates wrong code at least up to intel 15 (see
74 // tests/lac/vector-vector, tests/lac/intel-15-bug, and the discussion at
75 // https://github.com/dealii/dealii/issues/598).
76 template <>
77 struct EnableOpenMPSimdFor<long double>
78 {
79 static const bool value = false;
80 };
81#endif
82
83#ifdef DEAL_II_WITH_TASKFLOW
87 template <typename Iterator, typename Functor>
88 void
89 taskflow_parallel_for(Iterator x_begin,
90 Iterator x_end,
91 const Functor &functor,
92 const unsigned int grainsize)
93 {
94 tf::Executor &executor = MultithreadInfo::get_taskflow_executor();
95 tf::Taskflow taskflow;
96
97 // TODO: We have several choices for the Partitioner and we should spend
98 // some time benchmarking them:
99 // 1. StaticPartitioner(grainsize): all work items have grainsize number
100 // of items
101 // 2. GuidedPartitioner(grainsize):
102 // "The size of a partition is proportional to the number of unassigned
103 // iterations divided by the number of workers, and the size will
104 // gradually decrease to the given chunk size."
105 // 3. GuidedPartitioner(0): The default.
106 taskflow.for_each(
107 x_begin,
108 x_end,
109 [&](const auto &item) { functor(item); },
110 tf::StaticPartitioner(grainsize));
111 executor.run(taskflow).wait();
112 }
113#endif
114
115#ifdef DEAL_II_WITH_TBB
119 template <typename Iterator, typename Functor>
120 void
121 parallel_for(Iterator x_begin,
122 Iterator x_end,
123 const Functor &functor,
124 const unsigned int grainsize)
125 {
126 tbb::parallel_for(tbb::blocked_range<Iterator>(x_begin, x_end, grainsize),
127 functor,
128 tbb::auto_partitioner());
129 }
130
131
132
136 template <typename Iterator, typename Functor>
137 void
138 parallel_for(Iterator x_begin,
139 Iterator x_end,
140 const Functor &functor,
141 const unsigned int grainsize,
142 const std::shared_ptr<tbb::affinity_partitioner> &partitioner)
143 {
144 tbb::parallel_for(tbb::blocked_range<Iterator>(x_begin, x_end, grainsize),
145 functor,
146 *partitioner);
147 }
148
149#else
150
154 template <typename Iterator, typename Functor>
155 void
156 parallel_for(Iterator x_begin,
157 Iterator x_end,
158 const Functor &functor,
159 const unsigned int)
160 {
161 functor(boost::iterator_range<Iterator>(x_begin, x_end));
162 }
163
164#endif
165 } // namespace internal
166
196 template <typename InputIterator, typename OutputIterator, typename Function>
198 (std::invocable<Function, decltype(*std::declval<InputIterator>())> &&
199 std::assignable_from<
200 decltype(*std::declval<OutputIterator>()),
201 std::invoke_result_t<Function,
202 decltype(*std::declval<InputIterator>())>>))
203 DEAL_II_DEPRECATED_EARLY void transform(const InputIterator &begin_in,
204 const InputIterator &end_in,
205 OutputIterator out,
206 const Function &function,
207 const unsigned int grainsize)
208 {
209#ifdef DEAL_II_WITH_TASKFLOW
210 using Iterators = std::tuple<InputIterator, OutputIterator>;
211 using SyncIterators = SynchronousIterators<Iterators>;
212 Iterators x_begin(begin_in, out);
213 Iterators x_end(end_in, OutputIterator());
214
216 SyncIterators(x_begin),
217 SyncIterators(x_end),
218 [function](const auto &it) {
219 *std::get<1>(it) = function(*std::get<0>(it));
220 },
221 grainsize);
222
223#elif defined(DEAL_II_WITH_TBB)
224 using Iterators = std::tuple<InputIterator, OutputIterator>;
225 using SyncIterators = SynchronousIterators<Iterators>;
226 Iterators x_begin(begin_in, out);
227 Iterators x_end(end_in, OutputIterator());
229 SyncIterators(x_begin),
230 SyncIterators(x_end),
231 [function](const auto &range) {
232 for (const auto &p : range)
233 *std::get<1>(p) = function(*std::get<0>(p));
234 },
235 grainsize);
236#else
237 // make sure we don't get compiler
238 // warnings about unused arguments
239 (void)grainsize;
240
241 for (InputIterator in = begin_in; in != end_in;)
242 *out++ = function(*in++);
243#endif
244 }
245
246
247
279 template <typename InputIterator1,
280 typename InputIterator2,
281 typename OutputIterator,
282 typename Function>
284 (std::invocable<Function,
285 decltype(*std::declval<InputIterator1>()),
286 decltype(*std::declval<InputIterator2>())> &&
287 std::assignable_from<
288 decltype(*std::declval<OutputIterator>()),
289 std::invoke_result_t<Function,
290 decltype(*std::declval<InputIterator1>()),
291 decltype(*std::declval<InputIterator2>())>>))
292 DEAL_II_DEPRECATED_EARLY void transform(const InputIterator1 &begin_in1,
293 const InputIterator1 &end_in1,
294 InputIterator2 in2,
295 OutputIterator out,
296 const Function &function,
297 const unsigned int grainsize)
298 {
299#ifdef DEAL_II_WITH_TASKFLOW
300 using Iterators =
301 std::tuple<InputIterator1, InputIterator2, OutputIterator>;
302 using SyncIterators = SynchronousIterators<Iterators>;
303 Iterators x_begin(begin_in1, in2, out);
304 Iterators x_end(end_in1, InputIterator2(), OutputIterator());
305
307 SyncIterators(x_begin),
308 SyncIterators(x_end),
309 [function](const auto &it) {
310 *std::get<2>(it) = function(*std::get<0>(it), *std::get<1>(it));
311 },
312 grainsize);
313
314#elif defined(DEAL_II_WITH_TBB)
315 using Iterators =
316 std::tuple<InputIterator1, InputIterator2, OutputIterator>;
317 using SyncIterators = SynchronousIterators<Iterators>;
318 Iterators x_begin(begin_in1, in2, out);
319 Iterators x_end(end_in1, InputIterator2(), OutputIterator());
321 SyncIterators(x_begin),
322 SyncIterators(x_end),
323 [function](const auto &range) {
324 for (const auto &p : range)
325 *std::get<2>(p) = function(*std::get<0>(p), *std::get<1>(p));
326 },
327 grainsize);
328
329#else
330 // make sure we don't get compiler
331 // warnings about unused arguments
332 (void)grainsize;
333
334 for (InputIterator1 in1 = begin_in1; in1 != end_in1;)
335 *out++ = function(*in1++, *in2++);
336#endif
337 }
338
339
340
374 template <typename InputIterator1,
375 typename InputIterator2,
376 typename InputIterator3,
377 typename OutputIterator,
378 typename Function>
380 (std::invocable<Function,
381 decltype(*std::declval<InputIterator1>()),
382 decltype(*std::declval<InputIterator2>()),
383 decltype(*std::declval<InputIterator3>())> &&
384 std::assignable_from<
385 decltype(*std::declval<OutputIterator>()),
386 std::invoke_result_t<Function,
387 decltype(*std::declval<InputIterator1>()),
388 decltype(*std::declval<InputIterator2>()),
389 decltype(*std::declval<InputIterator3>())>>))
390 DEAL_II_DEPRECATED_EARLY void transform(const InputIterator1 &begin_in1,
391 const InputIterator1 &end_in1,
392 InputIterator2 in2,
393 InputIterator3 in3,
394 OutputIterator out,
395 const Function &function,
396 const unsigned int grainsize)
397 {
398#ifdef DEAL_II_WITH_TASKFLOW
399 using Iterators = std::
400 tuple<InputIterator1, InputIterator2, InputIterator3, OutputIterator>;
401 using SyncIterators = SynchronousIterators<Iterators>;
402 Iterators x_begin(begin_in1, in2, in3, out);
403 Iterators x_end(end_in1,
404 InputIterator2(),
405 InputIterator3(),
406 OutputIterator());
407
409 SyncIterators(x_begin),
410 SyncIterators(x_end),
411 [function](const auto &it) {
412 *std::get<3>(it) =
413 function(*std::get<0>(it), *std::get<1>(it), *std::get<2>(it));
414 },
415 grainsize);
416
417#elif defined(DEAL_II_WITH_TBB)
418 using Iterators = std::
419 tuple<InputIterator1, InputIterator2, InputIterator3, OutputIterator>;
420 using SyncIterators = SynchronousIterators<Iterators>;
421 Iterators x_begin(begin_in1, in2, in3, out);
422 Iterators x_end(end_in1,
423 InputIterator2(),
424 InputIterator3(),
425 OutputIterator());
427 SyncIterators(x_begin),
428 SyncIterators(x_end),
429 [function](const auto &range) {
430 for (const auto &p : range)
431 *std::get<3>(p) =
432 function(*std::get<0>(p), *std::get<1>(p), *std::get<2>(p));
433 },
434 grainsize);
435#else
436 // make sure we don't get compiler
437 // warnings about unused arguments
438 (void)grainsize;
439
440 for (OutputIterator1 in1 = begin_in1; in1 != end_in1;)
441 *out++ = function(*in1++, *in2++, *in3++);
442#endif
443 }
444
445
446 namespace internal
447 {
448#ifdef DEAL_II_WITH_TBB
455 template <typename Iterator, typename Function>
456 DEAL_II_CXX20_REQUIRES((std::invocable<Function, Iterator, Iterator>))
457 void apply_to_subranges(const tbb::blocked_range<Iterator> &range,
458 const Function &f)
459 {
460 f(range.begin(), range.end());
461 }
462#endif
463 } // namespace internal
464
465
537 template <typename Iterator, typename Function>
538 DEAL_II_CXX20_REQUIRES((std::invocable<Function, Iterator, Iterator>))
539 void apply_to_subranges(const Iterator &begin,
540 const std_cxx20::type_identity_t<Iterator> &end,
541 const Function &f,
542 const unsigned int grainsize)
543 {
544#ifndef DEAL_II_WITH_TBB
545 // make sure we don't get compiler
546 // warnings about unused arguments
547 (void)grainsize;
548
549 f(begin, end);
550#else
552 begin,
553 end,
554 [&f](const tbb::blocked_range<Iterator> &range) {
555 internal::apply_to_subranges<Iterator, Function>(range, f);
556 },
557 grainsize);
558#endif
559 }
560
561
562
591 {
596 virtual ~ParallelForInteger() = default;
597
606 void
607 apply_parallel(const std::size_t begin,
608 const std::size_t end,
609 const std::size_t minimum_parallel_grain_size) const;
610
617 virtual void
618 apply_to_subrange(const std::size_t, const std::size_t) const = 0;
619 };
620
621
622
689 template <typename ResultType, typename Iterator, typename Function>
691 (std::invocable<Function, Iterator, Iterator> &&
692 std::convertible_to<std::invoke_result_t<Function, Iterator, Iterator>,
693 ResultType>))
694 ResultType
696 const Iterator &begin,
697 const std_cxx20::type_identity_t<Iterator> &end,
698 const unsigned int grainsize)
699 {
700#ifndef DEAL_II_WITH_TBB
701 // make sure we don't get compiler
702 // warnings about unused arguments
703 (void)grainsize;
704
705 return f(begin, end);
706#else
707 return tbb::parallel_reduce(
708 tbb::blocked_range<Iterator>(begin, end, grainsize),
709 ResultType(0),
710 [f](const auto &range, const ResultType &starting_value) {
711 ResultType value = starting_value;
712 value += f(range.begin(), range.end());
713 return value;
714 },
715 std::plus<ResultType>(),
716 tbb::auto_partitioner());
717#endif
718 }
719
720
721 // --------------------- for loop affinity partitioner -----------------------
722
732 namespace internal
733 {
735 {
736 public:
741
742#ifdef DEAL_II_WITH_TBB
748
755 std::shared_ptr<tbb::affinity_partitioner>
757
763 void
765 const std::shared_ptr<tbb::affinity_partitioner> &p);
766
767 private:
772 std::shared_ptr<tbb::affinity_partitioner> my_partitioner;
773
778 bool in_use;
779
784#endif
785 };
786 } // namespace internal
787} // namespace parallel
788
789
790namespace internal
791{
792 namespace VectorImplementation
793 {
808 extern unsigned int minimum_parallel_grain_size;
809 } // namespace VectorImplementation
810
811
812 namespace SparseMatrixImplementation
813 {
819 extern unsigned int minimum_parallel_grain_size;
820 } // namespace SparseMatrixImplementation
821
822} // end of namespace internal
823
824
825/* --------------------------- inline functions ------------------------- */
826
827namespace parallel
828{
829 inline void
831 const std::size_t begin,
832 const std::size_t end,
833 const std::size_t minimum_parallel_grain_size) const
834 {
835#ifndef DEAL_II_WITH_TBB
836 // make sure we don't get compiler
837 // warnings about unused arguments
838 (void)minimum_parallel_grain_size;
839
840 apply_to_subrange(begin, end);
841#else
843 begin,
844 end,
845 [this](const tbb::blocked_range<std::size_t> &range) {
846 apply_to_subrange(range.begin(), range.end());
847 },
848 minimum_parallel_grain_size);
849#endif
850 }
851
852} // end of namespace parallel
853
855
856#endif
static tf::Executor & get_taskflow_executor()
std::shared_ptr< tbb::affinity_partitioner > acquire_one_partitioner()
Definition parallel.cc:74
void release_one_partitioner(const std::shared_ptr< tbb::affinity_partitioner > &p)
Definition parallel.cc:87
std::shared_ptr< tbb::affinity_partitioner > my_partitioner
Definition parallel.h:772
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:498
#define DEAL_II_CXX20_REQUIRES(condition)
Definition config.h:175
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:499
unsigned int minimum_parallel_grain_size
Definition parallel.cc:33
void parallel_for(Iterator x_begin, Iterator x_end, const Functor &functor, const unsigned int grainsize)
Definition parallel.h:121
void taskflow_parallel_for(Iterator x_begin, Iterator x_end, const Functor &functor, const unsigned int grainsize)
Definition parallel.h:89
void apply_to_subranges(const tbb::blocked_range< Iterator > &range, const Function &f)
Definition parallel.h:457
void apply_to_subranges(const Iterator &begin, const std_cxx20::type_identity_t< Iterator > &end, const Function &f, const unsigned int grainsize)
Definition parallel.h:539
void transform(const InputIterator &begin_in, const InputIterator &end_in, OutputIterator out, const Function &function, const unsigned int grainsize)
Definition parallel.h:203
ResultType accumulate_from_subranges(const Function &f, const Iterator &begin, const std_cxx20::type_identity_t< Iterator > &end, const unsigned int grainsize)
Definition parallel.h:695
virtual ~ParallelForInteger()=default
virtual void apply_to_subrange(const std::size_t, const std::size_t) const =0
void apply_parallel(const std::size_t begin, const std::size_t end, const std::size_t minimum_parallel_grain_size) const
Definition parallel.h:830