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