1 //                                               -*- C++ -*-
2 /**
3  *  @brief This file supplies support for multithreading
4  *
5  *  Copyright 2005-2021 Airbus-EDF-IMACS-ONERA-Phimeca
6  *
7  *  This library is free software: you can redistribute it and/or modify
8  *  it under the terms of the GNU Lesser General Public License as published by
9  *  the Free Software Foundation, either version 3 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public License
18  *  along with this library.  If not, see <http://www.gnu.org/licenses/>.
19  *
20  */
21 #ifndef OPENTURNS_TBB_HXX
22 #define OPENTURNS_TBB_HXX
23 
24 #include <algorithm>
25 #include "openturns/OTprivate.hxx"
26 #include "openturns/OTconfig.hxx"
27 #include "openturns/Exception.hxx"
28 
29 #ifdef OPENTURNS_HAVE_TBB
30 #ifdef OPENTURNS_TBB_NO_IMPLICIT_LINKAGE
31 # ifndef __TBB_NO_IMPLICIT_LINKAGE
32 #  define __TBB_NO_IMPLICIT_LINKAGE 1
33 # endif
34 # ifndef __TBBMALLOC_NO_IMPLICIT_LINKAGE
35 #  define __TBBMALLOC_NO_IMPLICIT_LINKAGE 1
36 # endif
37 #endif
38 #include <tbb/task_arena.h>
39 #include <tbb/parallel_sort.h>
40 #include <tbb/parallel_reduce.h>
41 #else /* OPENTURNS_HAVE_TBB */
42 
43 // We redefine some TBB elements to simulate TBB availability through the code
44 // Those redefinitions are single threaded
45 namespace tbb
46 {
47 
48 template <typename T>
49 class blocked_range
50 {
51 public:
52   typedef T value_type;
53   typedef std::size_t size_type;
blocked_range(value_type from,value_type to,size_type gs=1)54   blocked_range(value_type from, value_type to, size_type gs = 1)
55     : from_(from), to_(to), grainSize_(gs)
56   {
57 #ifdef DEBUG_BOUNDCHECKING
58     if (from_ > to_)
59       throw OT::InvalidArgumentException(HERE) << "Range is malformed (from > to) with from=" << from_
60           << " and to=" << to;
61     if (grainSize_ < 1)
62       throw OT::InvalidArgumentException(HERE) << "Range is malformed (grainSize < 1) with grainSize=" << grainSize_;
63 #endif /* DEBUG_BOUNDCHECKING */
64   }
begin() const65   value_type begin() const
66   {
67     return from_;
68   }
end() const69   value_type end() const
70   {
71     return to_;
72   }
size() const73   size_type size() const
74   {
75     return size_t(to_ - from_);
76   }
grainsize() const77   size_type grainsize() const
78   {
79     return grainSize_;
80   }
81 private:
82   value_type from_;
83   value_type to_;
84   size_type grainSize_;
85 }; // end class blocked_range
86 
87 template <typename RANGE, typename BODY>
parallel_for(const RANGE & range,const BODY & body)88 void parallel_for(const RANGE & range, const BODY & body)
89 {
90   body( range );
91 }
92 
93 template <typename RANGE, typename BODY>
parallel_reduce(const RANGE & range,BODY & body)94 void parallel_reduce(const RANGE & range, BODY & body)
95 {
96   body( range );
97 }
98 
99 template <typename ITERATOR>
parallel_sort(ITERATOR first,ITERATOR last)100 void parallel_sort(ITERATOR first, ITERATOR last)
101 {
102   std::stable_sort(first, last);
103 }
104 
105 class task_arena
106 {
107 public:
task_arena(int)108   task_arena(int) {}
execute(const F & f)109   template<typename F> void execute(const F& f)
110   {
111     f();
112   }
113 };
114 
115 } // namespace tbb
116 #endif /* OPENTURNS_HAVE_TBB */
117 
118 BEGIN_NAMESPACE_OPENTURNS
119 
120 // disables blas threading inside TBB parallel regions
121 class TBBContext
122 {
123 public:
124   TBBContext();
125   ~TBBContext();
126 private:
127   int ompNumThreads_;
128   int openblasNumThreads_;
129 };
130 
131 class OT_API TBB
132 {
133 public:
134 #ifndef SWIG // swig fails to parse nested struct or template
135 
136 #ifdef OPENTURNS_HAVE_TBB
137   typedef tbb::split Split;
138 #else /* OPENTURNS_HAVE_TBB */
139   struct Split {};
140 #endif /* OPENTURNS_HAVE_TBB */
141 
142   template <typename T>
143   struct BlockedRange : public tbb::blocked_range<T>
144   {
145     typedef T value_type;
146     typedef typename tbb::blocked_range<T>::size_type size_type;
BlockedRangeTBB::BlockedRange147     BlockedRange(value_type from, value_type to, size_type gs = 1) : tbb::blocked_range<T>(from, to, gs) {}
BlockedRangeTBB::BlockedRange148     BlockedRange(const tbb::blocked_range<T> & range) : tbb::blocked_range<T>(range) {}
149   };
150 #endif // SWIG
151 
152   template <typename BODY>
153   static inline
ParallelFor(UnsignedInteger from,UnsignedInteger to,const BODY & body,std::size_t gs=1)154   void ParallelFor( UnsignedInteger from, UnsignedInteger to, const BODY & body, std::size_t gs = 1 )
155   {
156     TBBContext context;
157     P_task_arena_->execute([&]()
158     {
159       tbb::parallel_for(tbb::blocked_range<UnsignedInteger>(from, to, gs), body);
160     });
161   }
162 
163   template <typename BODY>
164   static inline
ParallelForIf(const Bool condition,UnsignedInteger from,UnsignedInteger to,const BODY & body,std::size_t gs=1)165   void ParallelForIf(const Bool condition, UnsignedInteger from, UnsignedInteger to, const BODY & body, std::size_t gs = 1)
166   {
167     if (condition)
168       ParallelFor(from, to, body, gs);
169     else
170       body(tbb::blocked_range<UnsignedInteger>(from, to, gs));
171   }
172 
173   template <typename BODY>
174   static inline
ParallelReduce(UnsignedInteger from,UnsignedInteger to,BODY & body,std::size_t gs=1)175   void ParallelReduce( UnsignedInteger from, UnsignedInteger to, BODY & body, std::size_t gs = 1)
176   {
177     TBBContext context;
178     P_task_arena_->execute([&]()
179     {
180       tbb::parallel_reduce(tbb::blocked_range<UnsignedInteger>(from, to, gs), body);
181     });
182   }
183 
184   template <typename BODY>
185   static inline
ParallelReduceIf(const Bool condition,UnsignedInteger from,UnsignedInteger to,BODY & body,std::size_t gs=1)186   void ParallelReduceIf(const Bool condition, UnsignedInteger from, UnsignedInteger to, BODY & body, std::size_t gs = 1)
187   {
188     if (condition)
189       ParallelReduce(from, to, body, gs);
190     else
191       body(tbb::blocked_range<UnsignedInteger>(from, to, gs));
192   }
193 
194   template <typename ITERATOR>
195   static inline
ParallelSort(ITERATOR first,ITERATOR last)196   void ParallelSort( ITERATOR first, ITERATOR last )
197   {
198     TBBContext context;
199     tbb::task_arena arena(ThreadsNumber_);
200     P_task_arena_->execute([&]()
201     {
202       tbb::parallel_sort(first, last);
203     });
204   }
205 
206   /* Whether TBB is available */
207   static Bool IsAvailable();
208 
209   /* Enable/disable */
210   static void Enable();
211   static void Disable();
212 
213   /* Accessor to the maximum number of threads */
214   static void SetThreadsNumber(const UnsignedInteger threadsNumber);
215   static UnsignedInteger GetThreadsNumber();
216 
217   /** @deprecated */
218   static void SetNumberOfThreads(const UnsignedInteger numberOfThreads);
219   static UnsignedInteger GetNumberOfThreads();
220 
221 private:
222   friend struct TBB_init;
223 
224   static UnsignedInteger ThreadsNumber_;
225   static tbb::task_arena * P_task_arena_;
226 
227 }; /* end class TBB */
228 
229 /** This struct initializes all static members of TBB */
230 struct OT_API TBB_init
231 {
232   TBB_init();
233   ~TBB_init();
234 }; /* end class TBB_init */
235 
236 END_NAMESPACE_OPENTURNS
237 
238 #endif /* OPENTURNS_TBB_HXX */
239