c7b59798 |
1 | // Copyright (c) 2013-2014 OPEN CASCADE SAS |
2 | // |
3 | // This file is part of Open CASCADE Technology software library. |
4 | // |
5 | // This library is free software; you can redistribute it and/or modify it under |
6 | // the terms of the GNU Lesser General Public License version 2.1 as published |
7 | // by the Free Software Foundation, with special exception defined in the file |
8 | // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT |
9 | // distribution for complete text of the license and disclaimer of any warranty. |
10 | // |
11 | // Alternatively, this file may be used under the terms of Open CASCADE |
12 | // commercial license or contractual agreement. |
13 | |
14 | #ifndef OSD_Parallel_HeaderFile |
15 | #define OSD_Parallel_HeaderFile |
16 | |
fc867b96 |
17 | #include <OSD_ThreadPool.hxx> |
00af0ebb |
18 | #include <Standard_Type.hxx> |
19 | #include <memory> |
c7b59798 |
20 | |
00af0ebb |
21 | //! @brief Simple tool for code parallelization. |
22 | //! |
23 | //! OSD_Parallel class provides simple interface for parallel processing of |
24 | //! tasks that can be formulated in terms of "for" or "foreach" loops. |
25 | //! |
26 | //! To use this tool it is necessary to: |
27 | //! - organize the data to be processed in a collection accessible by |
28 | //! iteration (usually array or vector); |
29 | //! - implement a functor class providing operator () accepting iterator |
30 | //! (or index in array) that does the job; |
31 | //! - call either For() or ForEach() providing begin and end iterators and |
32 | //! a functor object. |
c7b59798 |
33 | //! |
00af0ebb |
34 | //! Iterators should satisfy requirements of STL forward iterator. |
35 | //! Functor |
c7b59798 |
36 | //! |
37 | //! @code |
38 | //! class Functor |
39 | //! { |
40 | //! public: |
41 | //! void operator() ([proccesing instance]) const |
42 | //! { |
43 | //! //... |
44 | //! } |
45 | //! }; |
46 | //! @endcode |
47 | //! |
00af0ebb |
48 | //! The operator () should be implemented in a thread-safe way so that |
49 | //! the same functor object can process different data items in parallel threads. |
50 | //! |
51 | //! Iteration by index (For) is expected to be more efficient than using iterators |
52 | //! (ForEach). |
53 | //! |
54 | //! Implementation uses TBB if OCCT is built with support of TBB; otherwise it |
55 | //! uses ad-hoc parallelization tool. In general, if TBB is available, it is |
56 | //! more efficient to use it directly instead of using OSD_Parallel. |
57 | |
c7b59798 |
58 | class OSD_Parallel |
59 | { |
00af0ebb |
60 | private: |
61 | |
62 | //! Interface class defining API for polymorphic wrappers over iterators. |
63 | //! Intended to add polymorphic behaviour to For and ForEach functionality |
64 | //! for arbitrary objects and eliminate dependency on template parameters. |
65 | class IteratorInterface |
c7b59798 |
66 | { |
00af0ebb |
67 | public: |
68 | virtual ~IteratorInterface() {} |
c7b59798 |
69 | |
00af0ebb |
70 | //! Returns true if iterators wrapped by this and theOther are equal |
71 | virtual bool IsEqual (const IteratorInterface& theOther) const = 0; |
c7b59798 |
72 | |
00af0ebb |
73 | //! Increments wrapped iterator |
74 | virtual void Increment () = 0; |
75 | |
76 | //! Returns new instance of the wrapper containing copy |
77 | //! of the wrapped iterator. |
78 | virtual IteratorInterface* Clone() const = 0; |
79 | }; |
c7b59798 |
80 | |
00af0ebb |
81 | //! Implementation of polymorphic iterator wrapper suitable for basic |
82 | //! types as well as for std iterators. |
83 | //! Wraps instance of actual iterator type Type. |
84 | template<class Type> |
85 | class IteratorWrapper : public IteratorInterface |
86 | { |
87 | public: |
88 | IteratorWrapper() {} |
89 | IteratorWrapper(const Type& theValue) : myValue(theValue) {} |
90 | |
91 | virtual bool IsEqual (const IteratorInterface& theOther) const Standard_OVERRIDE |
c7b59798 |
92 | { |
00af0ebb |
93 | return myValue == dynamic_cast<const IteratorWrapper<Type>&>(theOther).myValue; |
c7b59798 |
94 | } |
95 | |
00af0ebb |
96 | virtual void Increment () Standard_OVERRIDE |
c7b59798 |
97 | { |
00af0ebb |
98 | ++myValue; |
c7b59798 |
99 | } |
100 | |
00af0ebb |
101 | virtual IteratorInterface* Clone() const Standard_OVERRIDE |
c7b59798 |
102 | { |
00af0ebb |
103 | return new IteratorWrapper<Type>(myValue); |
c7b59798 |
104 | } |
105 | |
00af0ebb |
106 | const Type& Value() const { return myValue; } |
c7b59798 |
107 | |
00af0ebb |
108 | private: |
109 | Type myValue; |
c7b59798 |
110 | }; |
111 | |
00af0ebb |
112 | protected: |
113 | // Note: UniversalIterator and FunctorInterface are made protected to be |
114 | // accessible from specialization using threads (non-TBB). |
115 | |
116 | //! Fixed-type iterator, implementing STL forward iterator interface, used for |
117 | //! iteration over objects subject to parallel processing. |
118 | //! It stores pointer to instance of polymorphic iterator inheriting from |
119 | //! IteratorInterface, which contains actual type-specific iterator. |
8af9bbd5 |
120 | class UniversalIterator |
00af0ebb |
121 | // Note that TBB requires that value_type of iterator be copyable, |
122 | // thus we use its own type for that |
c7b59798 |
123 | { |
00af0ebb |
124 | public: |
8af9bbd5 |
125 | |
126 | // Since C++20 inheritance from std::iterator is deprecated, so define predefined types manually: |
127 | using iterator_category = std::forward_iterator_tag; |
b3284f3b |
128 | using value_type = IteratorInterface*; |
8af9bbd5 |
129 | using difference_type = ptrdiff_t; |
b3284f3b |
130 | using pointer = value_type; |
131 | using reference = value_type; |
8af9bbd5 |
132 | |
00af0ebb |
133 | UniversalIterator() {} |
c7b59798 |
134 | |
00af0ebb |
135 | UniversalIterator(IteratorInterface* theOther) |
136 | : myPtr(theOther) |
c7b59798 |
137 | { |
138 | } |
139 | |
00af0ebb |
140 | UniversalIterator(const UniversalIterator& theOther) |
141 | : myPtr (theOther.myPtr->Clone()) |
c7b59798 |
142 | { |
00af0ebb |
143 | } |
c7b59798 |
144 | |
00af0ebb |
145 | UniversalIterator& operator= (const UniversalIterator& theOther) |
146 | { |
147 | myPtr.reset (theOther.myPtr->Clone()); |
148 | return *this; |
149 | } |
c7b59798 |
150 | |
00af0ebb |
151 | bool operator!= (const UniversalIterator& theOther) const |
152 | { |
153 | return ! myPtr->IsEqual (*theOther.myPtr); |
154 | } |
c7b59798 |
155 | |
00af0ebb |
156 | bool operator== (const UniversalIterator& theOther) const |
157 | { |
158 | return myPtr->IsEqual (*theOther.myPtr); |
c7b59798 |
159 | } |
160 | |
00af0ebb |
161 | UniversalIterator& operator++() |
c7b59798 |
162 | { |
00af0ebb |
163 | myPtr->Increment(); |
164 | return *this; |
165 | } |
166 | |
167 | UniversalIterator operator++(int) |
168 | { |
169 | UniversalIterator aValue(*this); |
170 | myPtr->Increment(); |
171 | return aValue; |
172 | } |
c7b59798 |
173 | |
b3284f3b |
174 | reference operator* () const { return myPtr.get(); } |
175 | reference operator* () { return myPtr.get(); } |
c7b59798 |
176 | |
00af0ebb |
177 | private: |
00af0ebb |
178 | std::unique_ptr<IteratorInterface> myPtr; |
00af0ebb |
179 | }; |
c7b59798 |
180 | |
00af0ebb |
181 | //! Interface class representing functor object. |
5e6e5914 |
182 | //! Intended to add polymorphic behaviour to For and ForEach functionality |
00af0ebb |
183 | //! enabling execution of arbitrary function in parallel mode. |
184 | class FunctorInterface |
185 | { |
186 | public: |
187 | virtual ~FunctorInterface() {} |
c7b59798 |
188 | |
b3284f3b |
189 | virtual void operator () (IteratorInterface* theIterator) const = 0; |
190 | |
191 | // type cast to actual iterator |
192 | template <typename Iterator> |
193 | static const Iterator& DownCast(IteratorInterface* theIterator) |
194 | { |
195 | return dynamic_cast<OSD_Parallel::IteratorWrapper<Iterator>*>(theIterator)->Value(); |
196 | } |
00af0ebb |
197 | }; |
c7b59798 |
198 | |
00af0ebb |
199 | private: |
c7b59798 |
200 | |
00af0ebb |
201 | //! Wrapper for functors manipulating on std iterators. |
202 | template<class Iterator, class Functor> |
203 | class FunctorWrapperIter : public FunctorInterface |
204 | { |
205 | public: |
206 | FunctorWrapperIter (const Functor& theFunctor) |
207 | : myFunctor(theFunctor) |
208 | { |
209 | } |
210 | |
b3284f3b |
211 | virtual void operator() (IteratorInterface* theIterator) const Standard_OVERRIDE |
00af0ebb |
212 | { |
b3284f3b |
213 | const Iterator& anIt = DownCast<Iterator>(theIterator); |
00af0ebb |
214 | myFunctor(*anIt); |
215 | } |
216 | |
217 | private: |
218 | FunctorWrapperIter (const FunctorWrapperIter&); |
219 | void operator = (const FunctorWrapperIter&); |
220 | const Functor& myFunctor; |
c7b59798 |
221 | }; |
222 | |
00af0ebb |
223 | //! Wrapper for functors manipulating on integer index. |
224 | template<class Functor> |
225 | class FunctorWrapperInt : public FunctorInterface |
226 | { |
227 | public: |
228 | FunctorWrapperInt (const Functor& theFunctor) |
229 | : myFunctor(theFunctor) |
230 | { |
231 | } |
c7b59798 |
232 | |
b3284f3b |
233 | virtual void operator() (IteratorInterface* theIterator) const Standard_OVERRIDE |
00af0ebb |
234 | { |
b3284f3b |
235 | Standard_Integer anIndex = DownCast<Standard_Integer>(theIterator); |
00af0ebb |
236 | myFunctor(anIndex); |
237 | } |
238 | |
239 | private: |
240 | FunctorWrapperInt (const FunctorWrapperInt&); |
241 | void operator = (const FunctorWrapperInt&); |
242 | const Functor& myFunctor; |
243 | }; |
244 | |
fc867b96 |
245 | //! Wrapper redirecting functor taking element index to functor taking also thread index. |
246 | template<class Functor> |
247 | class FunctorWrapperForThreadPool |
248 | { |
249 | public: |
250 | FunctorWrapperForThreadPool (const Functor& theFunctor) : myFunctor(theFunctor) {} |
251 | |
252 | void operator() (int theThreadIndex, int theElemIndex) const |
253 | { |
254 | (void )theThreadIndex; |
255 | myFunctor (theElemIndex); |
256 | } |
257 | private: |
258 | FunctorWrapperForThreadPool (const FunctorWrapperForThreadPool&); |
259 | void operator= (const FunctorWrapperForThreadPool&); |
260 | const Functor& myFunctor; |
261 | }; |
262 | |
00af0ebb |
263 | private: |
c7b59798 |
264 | |
5e3047fd |
265 | //! Simple primitive for parallelization of "foreach" loops, e.g.: |
266 | //! @code |
267 | //! for (std::iterator anIter = theBegin; anIter != theEnd; ++anIter) {} |
268 | //! @endcode |
00af0ebb |
269 | //! Implementation of framework-dependent functionality should be provided by |
270 | //! forEach_impl function defined in opencascade::parallel namespace. |
fc867b96 |
271 | //! @param theBegin the first index (inclusive) |
5e3047fd |
272 | //! @param theEnd the last index (exclusive) |
00af0ebb |
273 | //! @param theFunctor functor providing an interface "void operator(InputIterator theIter){}" |
274 | //! performing task for the specified iterator position |
6f498847 |
275 | //! @param theNbItems number of items passed by iterator, -1 if unknown |
fc867b96 |
276 | Standard_EXPORT static void forEachOcct (UniversalIterator& theBegin, |
277 | UniversalIterator& theEnd, |
278 | const FunctorInterface& theFunctor, |
279 | Standard_Integer theNbItems); |
280 | |
281 | //! Same as forEachOcct() but can be implemented using external threads library. |
282 | Standard_EXPORT static void forEachExternal (UniversalIterator& theBegin, |
283 | UniversalIterator& theEnd, |
284 | const FunctorInterface& theFunctor, |
285 | Standard_Integer theNbItems); |
00af0ebb |
286 | |
287 | public: //! @name public methods |
288 | |
fc867b96 |
289 | //! Returns TRUE if OCCT threads should be used instead of auxiliary threads library; |
290 | //! default value is FALSE if alternative library has been enabled while OCCT building and TRUE otherwise. |
291 | Standard_EXPORT static Standard_Boolean ToUseOcctThreads(); |
292 | |
293 | //! Sets if OCCT threads should be used instead of auxiliary threads library. |
294 | //! Has no effect if OCCT has been built with no auxiliary threads library. |
295 | Standard_EXPORT static void SetUseOcctThreads (Standard_Boolean theToUseOcct); |
296 | |
297 | //! Returns number of logical processors. |
00af0ebb |
298 | Standard_EXPORT static Standard_Integer NbLogicalProcessors(); |
c7b59798 |
299 | |
00af0ebb |
300 | //! Simple primitive for parallelization of "foreach" loops, equivalent to: |
5e3047fd |
301 | //! @code |
00af0ebb |
302 | //! for (auto anIter = theBegin; anIter != theEnd; ++anIter) { |
303 | //! theFunctor(*anIter); |
304 | //! } |
5e3047fd |
305 | //! @endcode |
fc867b96 |
306 | //! @param theBegin the first index (inclusive) |
5e3047fd |
307 | //! @param theEnd the last index (exclusive) |
00af0ebb |
308 | //! @param theFunctor functor providing an interface "void operator(InputIterator theIter){}" |
309 | //! performing task for specified iterator position |
5e3047fd |
310 | //! @param isForceSingleThreadExecution if true, then no threads will be created |
6f498847 |
311 | //! @param theNbItems number of items passed by iterator, -1 if unknown |
00af0ebb |
312 | template <typename InputIterator, typename Functor> |
313 | static void ForEach(InputIterator theBegin, |
314 | InputIterator theEnd, |
315 | const Functor& theFunctor, |
6f498847 |
316 | const Standard_Boolean isForceSingleThreadExecution = Standard_False, |
317 | Standard_Integer theNbItems = -1) |
c7b59798 |
318 | { |
6f498847 |
319 | if (isForceSingleThreadExecution || theNbItems == 1) |
c7b59798 |
320 | { |
00af0ebb |
321 | for (InputIterator it(theBegin); it != theEnd; ++it) |
322 | theFunctor(*it); |
c7b59798 |
323 | } |
00af0ebb |
324 | else |
c7b59798 |
325 | { |
00af0ebb |
326 | UniversalIterator aBegin(new IteratorWrapper<InputIterator>(theBegin)); |
327 | UniversalIterator aEnd (new IteratorWrapper<InputIterator>(theEnd)); |
328 | FunctorWrapperIter<InputIterator,Functor> aFunctor (theFunctor); |
fc867b96 |
329 | if (ToUseOcctThreads()) |
330 | { |
331 | forEachOcct (aBegin, aEnd, aFunctor, theNbItems); |
332 | } |
333 | else |
334 | { |
335 | forEachExternal (aBegin, aEnd, aFunctor, theNbItems); |
336 | } |
c7b59798 |
337 | } |
338 | } |
c7b59798 |
339 | |
00af0ebb |
340 | //! Simple primitive for parallelization of "for" loops, equivalent to: |
341 | //! @code |
342 | //! for (int anIter = theBegin; anIter != theEnd; ++anIter) { |
343 | //! theFunctor(anIter); |
344 | //! } |
345 | //! @endcode |
fc867b96 |
346 | //! @param theBegin the first index (inclusive) |
00af0ebb |
347 | //! @param theEnd the last index (exclusive) |
348 | //! @param theFunctor functor providing an interface "void operator(int theIndex){}" |
349 | //! performing task for specified index |
350 | //! @param isForceSingleThreadExecution if true, then no threads will be created |
351 | template <typename Functor> |
352 | static void For(const Standard_Integer theBegin, |
353 | const Standard_Integer theEnd, |
354 | const Functor& theFunctor, |
355 | const Standard_Boolean isForceSingleThreadExecution = Standard_False) |
c7b59798 |
356 | { |
fc867b96 |
357 | const Standard_Integer aRange = theEnd - theBegin; |
358 | if (isForceSingleThreadExecution || aRange == 1) |
c7b59798 |
359 | { |
00af0ebb |
360 | for (Standard_Integer it (theBegin); it != theEnd; ++it) |
361 | theFunctor(it); |
c7b59798 |
362 | } |
fc867b96 |
363 | else if (ToUseOcctThreads()) |
364 | { |
365 | const Handle(OSD_ThreadPool)& aThreadPool = OSD_ThreadPool::DefaultPool(); |
366 | OSD_ThreadPool::Launcher aPoolLauncher (*aThreadPool, aRange); |
367 | FunctorWrapperForThreadPool<Functor> aFunctor (theFunctor); |
368 | aPoolLauncher.Perform (theBegin, theEnd, aFunctor); |
369 | } |
00af0ebb |
370 | else |
c7b59798 |
371 | { |
00af0ebb |
372 | UniversalIterator aBegin(new IteratorWrapper<Standard_Integer>(theBegin)); |
373 | UniversalIterator aEnd (new IteratorWrapper<Standard_Integer>(theEnd)); |
374 | FunctorWrapperInt<Functor> aFunctor (theFunctor); |
fc867b96 |
375 | forEachExternal (aBegin, aEnd, aFunctor, aRange); |
c7b59798 |
376 | } |
377 | } |
c7b59798 |
378 | |
00af0ebb |
379 | }; |
c7b59798 |
380 | |
381 | #endif |