HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
UT_ParallelUtil.h
Go to the documentation of this file.
1 /*
2  * PROPRIETARY INFORMATION. This software is proprietary to
3  * Side Effects Software Inc., and is not to be reproduced,
4  * transmitted, or disclosed in any way without written permission.
5  *
6  * NAME: UT_ParallelUtil.h ( UT Library, C++)
7  *
8  * COMMENTS: Simple wrappers on tbb interface
9  *
10  * RELATION TO THE STL:
11  *
12  * Use UT_ParallelUtil.h (or if necessary, UT_StdThread) instead
13  * of std::thread.
14  *
15  * Reasoning:
16  *
17  * Houdini requires tight control over the number of threads as
18  * we try to follow the command line -j option.
19  * This is important for Houdini to play nicely on farms where
20  * we may get a slice of a machine.
21  * Some oversubscription is a feature, but too much is not.
22  * We use TBB currently to ensure composability of threading -
23  * your algorithm does not run in a vacuum but must thread nicely
24  * with other algorithms at the same time, so you should never
25  * assume you get # CPU threads.
26  *
27  * We also need careful control of task stealing, which requires
28  * setting up thread groups. We thus must have a centralized
29  * location where all threads are created.
30  */
31 
32 #ifndef __UT_ParallelUtil__
33 #define __UT_ParallelUtil__
34 
35 #include "UT_API.h"
36 
37 #include "UT_Array.h"
38 #include "UT_PerformanceThread.h"
39 #include "UT_Span.h"
40 #include "UT_TaskScope.h"
41 #include "UT_TBBParallelInvoke.h"
42 #include "UT_Thread.h"
43 #include "UT_IteratorRange.h"
44 #include "UT_Optional.h"
45 
46 #include <oneapi/tbb/blocked_range.h>
47 #include <oneapi/tbb/blocked_range2d.h>
48 #include <oneapi/tbb/parallel_for.h>
49 #include <oneapi/tbb/parallel_reduce.h>
50 #include <oneapi/tbb/parallel_sort.h>
51 #include <oneapi/tbb/task.h>
52 #include <oneapi/tbb/task_arena.h>
53 
54 /// Typedef to denote the "split" constructor of a range
56 
57 /// Declare prior to use.
58 template <typename T>
60 
61 template <typename RowT, typename ColT=RowT>
63 
64 // Default implementation that calls range.size()
65 template< typename RANGE >
67 {
69 
70  size_t operator()(const RANGE& range) const
71  {
72  return range.size();
73  }
74 };
75 
76 // Partial specialization for UT_BlockedRange2D<T>
77 template< typename T >
79 {
81 
82  size_t operator()(const UT_BlockedRange2D<T>& range) const
83  {
84  return range.rows().size() * range.cols().size();
85  }
86 };
87 
88 /// This is needed by UT_CoarsenedRange
89 template <typename RANGE>
90 inline size_t UTestimatedNumItems(const RANGE& range)
91 {
92  return UT_EstimatorNumItems<RANGE>()(range);
93 }
94 
95 /// UT_CoarsenedRange: This should be used only inside
96 /// UT_ParallelFor and UT_ParallelReduce
97 /// This class wraps an existing range with a new range.
98 /// This allows us to use simple_partitioner, rather than
99 /// auto_partitioner, which has disastrous performance with
100 /// the default grain size in ttb 4.
101 template< typename RANGE >
102 class UT_CoarsenedRange : public RANGE
103 {
104 public:
105  // Compiler-generated versions are fine:
106  // ~UT_CoarsenedRange();
107  // UT_CoarsenedRange(const UT_CoarsenedRange&);
108 
109  // Split into two sub-ranges:
111  RANGE(range, spl),
112  myGrainSize(range.myGrainSize)
113  {
114  }
115 
116  // Inherited: bool empty() const
117 
118  bool is_divisible() const
119  {
120  return
121  RANGE::is_divisible() &&
122  (UTestimatedNumItems(static_cast<const RANGE&>(*this)) > myGrainSize);
123  }
124 
125 private:
126  size_t myGrainSize;
127 
128  UT_CoarsenedRange(const RANGE& base_range, const size_t grain_size) :
129  RANGE(base_range),
130  myGrainSize(grain_size)
131  {
132  }
133 
134  template <typename Range, typename Body>
135  friend void UTparallelFor(
136  const Range &range, const Body &body,
137  const int subscribe_ratio, const int min_grain_size,
138  const bool force_use_task_scope
139  );
140  template <typename Range, typename Body>
141  friend void UTparallelReduce(
142  const Range &range, Body &body,
143  const int subscribe_ratio, const int min_grain_size,
144  const bool force_use_taskscope
145  );
146  template <typename Range, typename Body>
147  friend void UTparallelDeterministicReduce(
148  const Range &range, Body &body, const int grain_size,
149  const bool force_use_taskscope
150  );
151 };
152 
153 /// Helper class for UTparallelFor().
154 /// Wraps the thread body in a task scope so that thread stats are collected
155 /// by the performance monitor, and child tasks can inherit task scope locks
156 /// from the parent task.
157 template<typename Range, typename Body>
159 {
160 public:
161  ut_TaskScopedBody(const Body *body)
162  : myBody(body),
163  myParentTaskScope(UT_TaskScope::getCurrent())
164  {
165  }
166 
168  : myBody(src.myBody),
169  myParentTaskScope(src.myParentTaskScope)
170  {
171  }
172 
173  void operator()(const Range &r) const
174  {
175  UT_TaskScope task_scope(myParentTaskScope);
176  (*myBody)(r);
177  }
178 
179 private:
180  const Body *myBody;
181  const UT_TaskScope *myParentTaskScope;
182 };
183 
184 /// Helper class for UTparallelFor().
185 /// Wraps the thread body allowing non-copyable bodies to be used with
186 /// UTparallelFor().
187 template<typename Range, typename Body>
189 {
190 public:
191  ut_TaskBody(const Body *body) : myBody(body) {}
192  void operator()(const Range &r) const { (*myBody)(r); }
193 
194 private:
195  const Body *myBody;
196 };
197 
198 /// Helper class for UTparallelForEachNumber()
199 /// This wraps the thread body to perform different load balancing based on
200 /// peeling off tasks using an atomic int to iterate over the range.
201 /// @c IntType must be an integer type supported by @c SYS_AtomicInt (currently
202 /// int32 or int64).
203 template <typename IntType, typename Body>
205 {
206 public:
207  ut_ForEachNumberBody(const Body &body,
208  SYS_AtomicInt<IntType> &it, IntType end)
209  : myBody(body)
210  , myIt(it)
211  , myEnd(end)
212  {
213  }
215  {
216  while (true)
217  {
218  IntType it = myIt.exchangeAdd(1);
219  if (it >= myEnd)
220  break;
221  myBody(UT_BlockedRange<IntType>(it, it+1));
222  }
223  }
224 private:
225  const Body &myBody;
227  IntType myEnd;
228 };
229 
230 /// Run the @c body function over a range in parallel.
231 /// UTparallelFor attempts to spread the range out over at most
232 /// subscribe_ratio * num_processor tasks.
233 /// The factor subscribe_ratio can be used to help balance the load.
234 /// UTparallelFor() uses tbb for its implementation.
235 /// The used grain size is the maximum of min_grain_size and
236 /// if UTestimatedNumItems(range) / (subscribe_ratio * num_processor).
237 /// If subscribe_ratio == 0, then a grain size of min_grain_size will be used.
238 /// A range can be split only when UTestimatedNumItems(range) exceeds the
239 /// grain size the range is divisible.
240 
241 ///
242 /// Requirements for the Range functor are:
243 /// - the requirements of the tbb Range Concept
244 /// - UT_estimatorNumItems<Range> must return the the estimated number of work items
245 /// for the range. When Range::size() is not the correct estimate, then a
246 /// (partial) specialization of UT_estimatorNumItemsimatorRange must be provided
247 /// for the type Range.
248 ///
249 /// Requirements for the Body function are:
250 /// - @code Body(const Body &); @endcode @n
251 /// Copy Constructor
252 /// - @code Body()::~Body(); @endcode @n
253 /// Destructor
254 /// - @code void Body::operator()(const Range &range) const; @endcode
255 /// Function call to perform operation on the range. Note the operator is
256 /// @b const.
257 ///
258 /// The requirements for a Range object are:
259 /// - @code Range::Range(const Range&); @endcode @n
260 /// Copy constructor
261 /// - @code Range::~Range(); @endcode @n
262 /// Destructor
263 /// - @code bool Range::is_divisible() const; @endcode @n
264 /// True if the range can be partitioned into two sub-ranges
265 /// - @code bool Range::empty() const; @endcode @n
266 /// True if the range is empty
267 /// - @code Range::Range(Range &r, UT_Split) const; @endcode @n
268 /// Split the range @c r into two sub-ranges (i.e. modify @c r and *this)
269 ///
270 /// Example: @code
271 /// class Square
272 /// {
273 /// public:
274 /// Square(fpreal *data) : myData(data) {}
275 /// ~Square();
276 /// void operator()(const UT_BlockedRange<int64> &range) const
277 /// {
278 /// for (int64 i = range.begin(); i != range.end(); ++i)
279 /// myData[i] *= myData[i];
280 /// }
281 /// fpreal *myData;
282 /// };
283 /// ...
284 ///
285 /// void
286 /// parallel_square(fpreal *array, int64 length)
287 /// {
288 /// UTparallelFor(UT_BlockedRange<int64>(0, length), Square(array));
289 /// }
290 /// @endcode
291 ///
292 /// @see UTparallelReduce(), UT_BlockedRange()
293 
294 template <typename Range, typename Body>
296  const Range &range, const Body &body,
297  const int subscribe_ratio = 2,
298  const int min_grain_size = 1,
299  const bool force_use_task_scope = true
300 )
301 {
302  const size_t num_processors( UT_Thread::getNumProcessors() );
303 
304  UT_ASSERT( num_processors >= 1 );
305  UT_ASSERT( min_grain_size >= 1 );
306  UT_ASSERT( subscribe_ratio >= 0 );
307 
308  const size_t est_range_size( UTestimatedNumItems(range) );
309 
310  // Don't run on an empty range!
311  if (est_range_size == 0)
312  return;
313 
314  // Avoid tbb overhead if entire range needs to be single threaded
315  if (num_processors == 1 || est_range_size <= min_grain_size ||
317  {
318  body(range);
319  return;
320  }
321 
322  size_t grain_size(min_grain_size);
323  if( subscribe_ratio > 0 )
324  grain_size = std::max(
325  grain_size,
326  est_range_size / (subscribe_ratio * num_processors)
327  );
328 
329  UT_CoarsenedRange< Range > coarsened_range(range, grain_size);
330 
331  if (force_use_task_scope || UTperformanceIsRecordingThreadStats())
332  {
334  coarsened_range, ut_TaskScopedBody<Range, Body>(&body),
335  tbb::simple_partitioner());
336  }
337  else
338  {
340  coarsened_range, ut_TaskBody<Range, Body>(&body),
341  tbb::simple_partitioner());
342  }
343 }
344 
345 /// Version of UTparallelFor that always creates a task scope to prevent
346 /// deadlocking of child tasks that might acquire UT_TaskLocks.
347 template <typename Range, typename Body>
349  const Range &range, const Body &body,
350  const int subscribe_ratio = 2,
351  const int min_grain_size = 1
352 )
353 {
354  UTparallelFor(range, body, subscribe_ratio, min_grain_size, true);
355 }
356 
357 /// Version of UTparallelFor that is tuned for the case where the range
358 /// consists of lightweight items, for example,
359 /// float additions or matrix-vector multiplications.
360 template <typename Range, typename Body>
361 void
362 UTparallelForLightItems(const Range &range, const Body &body,
363  const bool force_use_task_scope = true)
364 {
365  UTparallelFor(range, body, 2, 1024, force_use_task_scope);
366 }
367 
368 /// Version of UTparallelFor that is tuned for the case where the range
369 /// consists of heavy items, for example, defragmenting an entire attribute.
370 ///
371 /// If possible, UTparallelForEachNumber() is preferred over use of
372 /// UTparallelForHeavyItems().
373 ///
374 /// Note, when the range is guaranteed to be small, you might prefer to run
375 /// <tt>UTparallelFor(range, body, 0, 1)</tt>. That form of the loop would
376 /// guarantee that a separate task is started for each iteration of the body.
377 /// However, that form can cause issues when the range gets large, in that a @b
378 /// large number of tasks may be created.
379 ///
380 template <typename Range, typename Body>
381 SYS_DEPRECATED_REPLACE(16.5, "UTparallelForEachNumber||UTparallelFor(r,b,0,1)")
382 void
383 UTparallelForHeavyItems(const Range &range, const Body &body)
384 {
385  // By oversubscribing by 32, small ranges will still be split into
386  // individual tasks. However, large ranges will be chunked, causing fewer
387  // tasks, but potentially worse load balancing.
388  //
389  // Consider using UTparallelForEachNumber() instead.
390  UTparallelFor(range, body, 32, 1, /*force_use_task=*/true);
391 }
392 
393 /// Version of UTparallelFor tuned for a range consists of heavy items, for
394 /// example, defragmenting an entire attribute.
395 ///
396 /// This approach uses "ideal" load balancing across threads and doesn't rely
397 /// on the TBB task scheduler for splitting the range. Instead, it iterates
398 /// from @c 0 to @c nitems, calling @c body with a UT_BlockedRange<IntType>
399 /// containing a list of tasks to execute.
400 ///
401 /// @note The @c IntType must work with @c SYS_AtomicInt (currently int32 or
402 /// int64). If you get a boost static assertion, please make sure the @c body
403 /// range takes the proper integer type.
404 template <typename IntType, typename Body>
405 void
406 UTparallelForEachNumber(IntType nitems, const Body &body, const bool force_use_task_scope = true)
407 {
408  const size_t num_processors(UT_Thread::getNumProcessors());
409 
410  UT_ASSERT(num_processors >= 1);
411  if (nitems == 0)
412  return;
413  if (num_processors == 1)
414  {
415  body(UT_BlockedRange<IntType>(0, nitems));
416  return;
417  }
418  if (nitems <= num_processors)
419  {
420  // When there are a small number of tasks, split into a single task per
421  // thread.
422  UTparallelFor(UT_BlockedRange<IntType>(0, nitems), body, 0, 1, force_use_task_scope);
423  return;
424  }
425 
426  // Split across number of processors, with each thread using the atomic int
427  // to query the next task to be run (similar to UT_ThreadedAlgorithm)
429  UTparallelFor(UT_BlockedRange<IntType>(0, num_processors),
430  ut_ForEachNumberBody<IntType, Body>(body, it, nitems), 0, 1, force_use_task_scope);
431 }
432 
433 /// UTserialForEachNumber can be used as a debugging tool to quickly replace a
434 /// parallel for with a serial for.
435 template <typename IntType, typename Body>
436 void
437 UTserialForEachNumber(IntType nitems, const Body &body, bool usetaskscope=true)
438 {
439  for (IntType i = 0; i < nitems; ++i)
440  body(UT_BlockedRange<IntType>(i, i + 1));
441 }
442 
443 /// Version of UTparallelForEachNumber that wraps the body in a UT_TaskScope
444 /// that makes it safe to use UT_TaskLock objects that are currently locked by
445 /// the parent scope.
446 template <typename IntType, typename Body>
447 void
448 UTparallelForEachNumberTaskScope(IntType nitems, const Body &body)
449 {
450  UTparallelForEachNumber(nitems, body, /*force_use_task_scope=*/true);
451 }
452 
453 /// UTserialFor can be used as a debugging tool to quickly replace a parallel
454 /// for with a serial for.
455 template <typename Range, typename Body>
456 void UTserialFor(const Range &range, const Body &body)
457  { body(range); }
458 
459 /// Helper class for UTparallelInvoke().
460 /// Wraps the thread body in a task scope so that thread stats are collected
461 /// by the performance monitor, and child tasks can inherit task scope locks
462 /// from the parent task.
463 template<typename Body>
465 {
466 public:
467  ut_TaskScopedInvokeBody(const Body &body)
468  : myBody(body),
469  myParentTaskScope(UT_TaskScope::getCurrent())
470  {
471  }
472 
474  : myBody(src.myBody),
475  myParentTaskScope(src.myParentTaskScope)
476  {
477  }
478 
479  void operator()() const
480  {
481  UT_TaskScope task_scope(myParentTaskScope);
482  myBody();
483  }
484 
485 private:
486  const Body &myBody;
487  const UT_TaskScope *myParentTaskScope;
488 };
489 
490 /// Takes a functor for passing to UTparallelInvoke, and wraps it in a
491 /// ut_TaskScopeInvokeBody object so the functor will be invoked wrapped in
492 /// a UT_TaskScope that makes it safe to use UT_TaskLock objects that are
493 /// currently locked by the parent scope.
494 template <typename Body>
496 UTmakeTaskScopedInvokeBody(const Body &body)
497 {
498  return ut_TaskScopedInvokeBody<Body>(body);
499 }
500 
501 /// UTparallelInvoke() executes the given functions in parallel when the
502 /// parallel flag is true - otherwise it runs them serially. F1 and F2
503 /// should be void functors.
504 template <typename F1, typename F2>
505 inline void UTparallelInvoke(bool parallel, F1 &&f1, F2 &&f2)
506 {
507  if (parallel && UT_Thread::isThreadingEnabled())
508  {
509  tbb::parallel_invoke(UTmakeTaskScopedInvokeBody(std::forward<F1>(f1)),
510  UTmakeTaskScopedInvokeBody(std::forward<F2>(f2)));
511  }
512  else
513  {
514  f1();
515  f2();
516  }
517 }
518 
519 template <typename F1, typename F2, typename... Rest>
520 inline void UTparallelInvoke(bool parallel, F1 &&f1, F2 &&f2, Rest&&... rest)
521 {
522  if (parallel && UT_Thread::isThreadingEnabled())
523  {
524  tbb::parallel_invoke(UTmakeTaskScopedInvokeBody(std::forward<F1>(f1)),
525  UTmakeTaskScopedInvokeBody(std::forward<F2>(f2)),
526  UTmakeTaskScopedInvokeBody(std::forward<Rest>(rest))...);
527  }
528  else
529  {
530  f1();
531  UTparallelInvoke(parallel, f2, std::forward<Rest>(rest)...);
532  }
533 }
534 
535 template <typename F1>
537 {
538 public:
540  : myFunctions(functions) {}
541  void operator()(const tbb::blocked_range<int>& r ) const
542  {
543  for (int i = r.begin(); i != r.end(); ++i)
544  (*myFunctions(i))();
545  }
546 private:
547  const UT_Array<F1 *> &myFunctions;
548 };
549 
550 /// UTparallelInvoke() executes the array of functions in parallel when the
551 /// parallel flag is true - otherwise it runs them serially. F1 should be
552 /// a void functor.
553 template <typename F1>
554 inline void UTparallelInvoke(bool parallel, const UT_Array<F1 *> &funs)
555 {
556  if (parallel && funs.entries() > 1 && UT_Thread::isThreadingEnabled())
557  {
558  UTparallelFor(tbb::blocked_range<int>(0, funs.entries(), 1),
560  32, 1); // oversubscribe to force forking
561  }
562  else
563  {
564  for (int i = 0; i < funs.entries(); i++)
565  (*funs(i))();
566  }
567 }
568 
569 template <typename F1>
571 {
572 public:
574  : myFunctions(functions) {}
575  void operator()(const tbb::blocked_range<int>& r ) const
576  {
577  for (int i = r.begin(); i != r.end(); ++i)
578  myFunctions(i)();
579  }
580 private:
581  const UT_Array<F1> &myFunctions;
582 };
583 
584 /// UTparallelInvoke() executes the array of functions in parallel when the
585 /// parallel flag is true - otherwise it runs them serially. F1 should be
586 /// a void functor.
587 template <typename F1>
588 inline void UTparallelInvoke(bool parallel, const UT_Array<F1> &funs)
589 {
590  if (parallel && funs.entries() > 1 && UT_Thread::isThreadingEnabled())
591  {
592  UTparallelFor(tbb::blocked_range<int>(0, funs.entries(), 1),
594  32, 1); // oversubscribe to force forking
595  }
596  else
597  {
598  for (int i = 0; i < funs.entries(); i++)
599  funs(i)();
600  }
601 }
602 
603 /// Helper class for UTparallelReduce().
604 /// Wraps the thread body in a task scope so that thread stats are collected
605 /// by the performance monitor, and child tasks can inherit task scope locks
606 /// from the parent task.
607 template<typename Range, typename Body>
609 {
610 public:
611  // Construct from base type pointer, holds a pointer to it.
613  : myParentTaskScope(UT_TaskScope::getCurrent())
614  {
615  myBodyPtr = body;
616  }
617 
619  : myParentTaskScope(src.myParentTaskScope)
620  , myBodyPtr(nullptr)
621  {
622  UT_TaskScope task_scope(myParentTaskScope);
623  myBody.emplace(src.body(), UT_Split());
624  }
625 
626  void operator()(const Range &r)
627  {
628  UT_TaskScope task_scope(myParentTaskScope);
629  body()(r);
630  }
631 
633  {
634  UT_TaskScope task_scope(myParentTaskScope);
635  body().join(other.body());
636  }
637 
638  const Body &body() const { return myBodyPtr ? *myBodyPtr : *myBody; }
639  Body &body() { return myBodyPtr ? *myBodyPtr : *myBody; }
640 private:
641  UT_Optional<Body> myBody;
642  Body *myBodyPtr;
643  const UT_TaskScope *myParentTaskScope;
644 };
645 
646 /// UTparallelReduce() is a simple wrapper that uses tbb for its implementation.
647 /// Run the @c body function over a range in parallel.
648 ///
649 /// WARNING: The @c operator()() and @c join() functions MUST @b NOT initialize
650 /// data! @b Both of these functions MUST ONLY accumulate data! This
651 /// is because TBB may re-use body objects for multiple ranges.
652 /// Effectively, operator()() must act as an in-place join operation
653 /// for data as it comes in. Initialization must be kept to the
654 /// constructors of Body.
655 ///
656 /// Requirements for the Body function are:
657 /// - @code Body()::~Body(); @endcode @n
658 /// Destructor
659 /// - @code Body::Body(Body &r, UT_Split) const; @endcode @n
660 /// The splitting constructor.
661 /// WARNING: This must be able to run concurrently with calls to
662 /// @c r.operator()() and @c r.join(), so this should not copy
663 /// values accumulating in r.
664 /// - @code void Body::operator()(const Range &range); @endcode
665 /// Function call to perform operation on the range. Note the operator is
666 /// @b not const.
667 /// - @code void Body::join(const Body &other); @endcode
668 /// Join the results from another operation with this operation.
669 /// @b not const.
670 ///
671 /// The requirements for a Range object are:
672 /// - @code Range::Range(const Range&); @endcode @n
673 /// Copy constructor
674 /// - @code Range::~Range(); @endcode @n
675 /// Destructor
676 /// - @code bool Range::is_divisible() const; @endcode @n
677 /// True if the range can be partitioned into two sub-ranges
678 /// - @code bool Range::empty() const; @endcode @n
679 /// True if the range is empty
680 /// - @code Range::Range(Range &r, UT_Split) const; @endcode @n
681 /// Split the range @c r into two sub-ranges (i.e. modify @c r and *this)
682 ///
683 /// Example: @code
684 /// class Dot
685 /// {
686 /// public:
687 /// Dot(const fpreal *a, const fpreal *b)
688 /// : myA(a)
689 /// , myB(b)
690 /// , mySum(0)
691 /// {}
692 /// Dot(Dot &src, UT_Split)
693 /// : myA(src.myA)
694 /// , myB(src.myB)
695 /// , mySum(0)
696 /// {}
697 /// void operator()(const UT_BlockedRange<int64> &range)
698 /// {
699 /// for (int64 i = range.begin(); i != range.end(); ++i)
700 /// mySum += myA[i] * myB[i];
701 /// }
702 /// void join(const Dot &other)
703 /// {
704 /// mySum += other.mySum;
705 /// }
706 /// fpreal mySum;
707 /// const fpreal *myA, *myB;
708 /// };
709 ///
710 /// fpreal
711 /// parallel_dot(const fpreal *a, const fpreal *b, int64 length)
712 /// {
713 /// Dot body(a, b);
714 /// UTparallelReduce(UT_BlockedRange<int64>(0, length), body);
715 /// return body.mySum;
716 /// }
717 /// @endcode
718 /// @see UTparallelFor(), UT_BlockedRange()
719 template <typename Range, typename Body>
721  const Range &range,
722  Body &body,
723  const int subscribe_ratio = 2,
724  const int min_grain_size = 1,
725  const bool force_use_task_scope = true
726 )
727 {
728  const size_t num_processors( UT_Thread::getNumProcessors() );
729 
730  UT_ASSERT( num_processors >= 1 );
731  UT_ASSERT( min_grain_size >= 1 );
732  UT_ASSERT( subscribe_ratio >= 0 );
733 
734  const size_t est_range_size( UTestimatedNumItems(range) );
735 
736  // Don't run on an empty range!
737  if (est_range_size == 0)
738  return;
739 
740  // Avoid tbb overhead if entire range needs to be single threaded
741  if (num_processors == 1 || est_range_size <= min_grain_size ||
743  {
744  body(range);
745  return;
746  }
747 
748  size_t grain_size(min_grain_size);
749  if( subscribe_ratio > 0 )
750  grain_size = std::max(
751  grain_size,
752  est_range_size / (subscribe_ratio * num_processors)
753  );
754 
755  UT_CoarsenedRange< Range > coarsened_range(range, grain_size);
756  if (force_use_task_scope || UTperformanceIsRecordingThreadStats())
757  {
758  ut_ReduceTaskScopedBody<Range, Body> bodywrapper(&body);
759  tbb::parallel_reduce(coarsened_range,
760  bodywrapper,
761  tbb::simple_partitioner());
762  }
763  else
764  {
765  tbb::parallel_reduce(coarsened_range, body, tbb::simple_partitioner());
766  }
767 }
768 
769 /// This is a simple wrapper for deterministic reduce that uses tbb. It
770 /// works in the same manner as UTparallelReduce, with the following
771 /// differences:
772 /// - reduction and join order is deterministic (devoid of threading
773 /// uncertainty;
774 /// - a fixed grain size must be provided by the caller; grain size is
775 /// not adjusted based on the available resources (this is required to
776 /// satisfy determinism).
777 /// This version should be used when task joining is not associative (such
778 /// as accumulation of a floating point residual).
779 template <typename Range, typename Body>
781  const Range &range,
782  Body &body,
783  const int grain_size,
784  const bool force_use_task_scope = true
785 )
786 {
787  UT_ASSERT( grain_size >= 1 );
788 
789  const size_t est_range_size( UTestimatedNumItems(range) );
790 
791  // Don't run on an empty range!
792  if (est_range_size == 0)
793  return;
794 
796  "FIXME: There needs to be a way to do identical splits and joins when single-threading,"
797  " to avoid having different roundoff error from when multi-threading. "
798  " Something using simple_partitioner() might work.");
799 
800  UT_CoarsenedRange< Range > coarsened_range(range, grain_size);
801  if (force_use_task_scope || UTperformanceIsRecordingThreadStats())
802  {
803  ut_ReduceTaskScopedBody<Range, Body> bodywrapper(&body);
804  tbb::parallel_deterministic_reduce(coarsened_range,
805  bodywrapper,
806  tbb::simple_partitioner());
807  }
808  else
809  {
810  tbb::parallel_deterministic_reduce(coarsened_range, body);
811  }
812 }
813 
814 /// Version of UTparallelReduce that is tuned for the case where the range
815 /// consists of lightweight items, for example, finding the min/max in a set of
816 /// integers.
817 template <typename Range, typename Body>
818 void UTparallelReduceLightItems(const Range &range, Body &body)
819 {
820  UTparallelReduce(range, body, 2, 1024);
821 }
822 
823 /// Version of UTparallelReduce that is tuned for the case where the range
824 /// consists of heavy items, for example, computing the bounding box of a list
825 /// of geometry objects.
826 template <typename Range, typename Body>
827 void UTparallelReduceHeavyItems(const Range &range, Body &body)
828 {
829  UTparallelReduce(range, body, 0, 1);
830 }
831 
832 /// UTserialReduce can be used as a debugging tool to quickly replace a
833 /// parallel reduce with a serial for.
834 template <typename Range, typename Body>
835 void UTserialReduce(const Range &range, Body &body)
836  { body(range); }
837 
838 /// Cancel the entire current task group context when run within a task
839 static inline void
840 UTparallelCancelGroupExecution()
841 {
842  tbb::task::current_context()->cancel_group_execution();
843 }
844 
845 /// UTparallelSort() is a simple wrapper that uses tbb for its implementation.
846 ///
847 /// WARNING: UTparallelSort is UNSTABLE! You must explicitly force stability
848 /// if needed.
849 template <typename RandomAccessIterator, typename Compare>
850 void UTparallelSort(RandomAccessIterator begin, RandomAccessIterator end, const Compare &compare)
851 {
853  tbb::parallel_sort(begin, end, compare);
854  else
855  std::sort(begin, end, compare);
856 }
857 
858 /// UTparallelSort() is a simple wrapper that uses tbb for its implementation.
859 ///
860 /// WARNING: UTparallelSort is UNSTABLE! You must explicitly force stability
861 /// if needed.
862 template <typename RandomAccessIterator>
863 void UTparallelSort(RandomAccessIterator begin, RandomAccessIterator end)
864 {
866  tbb::parallel_sort(begin, end);
867  else
868  std::sort(begin, end);
869 }
870 
871 /// UTparallelSort() is a simple wrapper that uses tbb for its implementation.
872 ///
873 /// WARNING: UTparallelSort is UNSTABLE! You must explicitly force stability
874 /// if needed.
875 template <typename T>
877 {
879  tbb::parallel_sort(begin, end);
880  else
881  std::sort(begin, end);
882 }
883 
884 // Forward declaration of parallel_stable_sort; implementation at end of file.
885 namespace pss
886 {
887 template<typename RandomAccessIterator, typename Compare>
888 void parallel_stable_sort( RandomAccessIterator xs, RandomAccessIterator xe,
889  Compare comp );
890 
891 //! Wrapper for sorting with default comparator.
892 template<class RandomAccessIterator>
893 void parallel_stable_sort( RandomAccessIterator xs, RandomAccessIterator xe )
894 {
896  parallel_stable_sort( xs, xe, std::less<T>() );
897 }
898 }
899 
900 /// UTparalleStableSort() is a stable parallel merge sort.
901 ///
902 /// NOTE: UTparallelStableSort requires a temporary buffer of size end-begin.
903 /// On allocation failure it falls back to calling @c std::stable_sort.
904 /// NOTE: Element initialization is done via @c std::move, so non-POD element
905 /// types should implement c++11 move semantics.
906 template <typename RandomAccessIterator, typename Compare>
907 void UTparallelStableSort(RandomAccessIterator begin, RandomAccessIterator end,
908  const Compare &compare)
909 {
910  pss::parallel_stable_sort(begin, end, compare);
911 }
912 
913 /// UTparalleStableSort() is a stable parallel merge sort.
914 ///
915 /// NOTE: UTparallelStableSort requires a temporary buffer of size end-begin.
916 /// On allocation failure it falls back to calling @c std::stable_sort.
917 /// NOTE: Element initialization is done via @c std::move, so non-POD element
918 /// types should implement c++11 move semantics.
919 template <typename RandomAccessIterator>
920 void UTparallelStableSort(RandomAccessIterator begin, RandomAccessIterator end)
921 {
922  pss::parallel_stable_sort(begin, end);
923 }
924 
925 /// UTparalleStableSort() is a stable parallel merge sort.
926 ///
927 /// NOTE: UTparallelStableSort requires a temporary buffer of size end-begin.
928 /// On allocation failure it falls back to calling @c std::stable_sort.
929 /// NOTE: Element initialization is done via @c std::move, so non-POD element
930 /// types should implement c++11 move semantics.
931 template <typename T>
933 {
934  pss::parallel_stable_sort(begin, end);
935 }
936 
937 /// UTparalleStableSort() is a stable parallel merge sort.
938 ///
939 /// NOTE: UTparallelStableSort requires a temporary buffer of size end-begin.
940 /// On allocation failure it falls back to calling @c std::stable_sort.
941 /// NOTE: Element initialization is done via @c std::move, so non-POD element
942 /// types should implement c++11 move semantics.
943 template <typename T, typename Compare>
944 void UTparallelStableSort(T *begin, T *end, const Compare &compare)
945 {
946  pss::parallel_stable_sort(begin, end, compare);
947 }
948 
949 
950 /// UTparalleStableSort() is a stable parallel merge sort.
951 /// This form works with UT_Array and other containers with begin/end members.
952 ///
953 /// NOTE: UTparallelStableSort requires a temporary buffer of size end-begin.
954 /// On allocation failure it falls back to calling @c std::stable_sort.
955 /// NOTE: Element initialization is done via @c std::move, so non-POD element
956 /// types should implement c++11 move semantics.
957 template <typename T>
958 void
960 {
961  pss::parallel_stable_sort(a.begin(), a.end());
962 }
963 
964 
965 /// UTparalleStableSort() is a stable parallel merge sort.
966 /// This form works with UT_Array and other containers with begin/end members.
967 ///
968 /// NOTE: UTparallelStableSort requires a temporary buffer of size end-begin.
969 /// On allocation failure it falls back to calling @c std::stable_sort.
970 /// NOTE: Element initialization is done via @c std::move, so non-POD element
971 /// types should implement c++11 move semantics.
972 template <typename T, typename Compare>
973 void
974 UTparallelStableSort(T &a, const Compare &compare)
975 {
976  pss::parallel_stable_sort(a.begin(), a.end(), compare);
977 }
978 
979 /// UT_BlockedRange() is a simple wrapper using tbb for its implementation
980 /// This meets the requirements for a Range object, which are:
981 /// - @code Range::Range(const Range&); @endcode @n
982 /// Copy constructor
983 /// - @code Range::~Range(); @endcode @n
984 /// Destructor
985 /// - @code bool Range::is_divisible() const; @endcode @n
986 /// True if the range can be partitioned into two sub-ranges
987 /// - @code bool Range::empty() const; @endcode @n
988 /// True if the range is empty
989 /// - @code Range::Range(Range &r, UT_Split) const; @endcode @n
990 /// Split the range @c r into two sub-ranges (i.e. modify @c r and *this)
991 template <typename T>
992 class UT_BlockedRange : public tbb::blocked_range<T>
993 {
994 public:
995  // TBB 2018 U3 no longer supports default blocked_range constructors
996  UT_BlockedRange() = delete;
997 
998  UT_BlockedRange(T begin_value, T end_value, size_t grainsize=1)
999  : tbb::blocked_range<T>(begin_value, end_value, grainsize)
1000  {}
1002  : tbb::blocked_range<T>(R, split)
1003  {}
1004 
1005 
1006  // Because the VALUE of a blocked range may be a simple
1007  // type like int, the range-based for will fail to do a
1008  // dereference on it. This iterator-like wrapper will
1009  // allow * to work.
1011  {
1012  public:
1014  explicit ValueWrapper(const T &it)
1015  : myCurrent(it)
1016  {}
1017 
1019  T operator*() { return myCurrent; }
1020 
1022  bool operator==(const ValueWrapper &cmp) const
1023  { return (myCurrent == cmp.myCurrent); }
1025  bool operator!=(const ValueWrapper &cmp) const
1026  { return !(*this == cmp); }
1027 
1030  {
1031  ++myCurrent;
1032  return *this;
1033  }
1034  private:
1035  T myCurrent;
1036  };
1037 
1038  // Allows for:
1039  // for (T value : range.items())
1040  auto items() const
1041  {
1042  return UT_IteratorRange<ValueWrapper>(ValueWrapper(this->begin()), ValueWrapper(this->end()));
1043  }
1044 
1045 };
1046 
1047 /// UT_BlockedRange2D() is a simple wrapper using tbb for its implementation
1048 /// This meets the requirements for a Range object, which are:
1049 /// - @code Range::Range(const Range&); @endcode @n
1050 /// Copy constructor
1051 /// - @code Range::~Range(); @endcode @n
1052 /// Destructor
1053 /// - @code bool Range::is_divisible() const; @endcode @n
1054 /// True if the range can be partitioned into two sub-ranges
1055 /// - @code bool Range::empty() const; @endcode @n
1056 /// True if the range is empty
1057 /// - @code Range::Range(Range &r, UT_Split) const; @endcode @n
1058 /// Split the range @c r into two sub-ranges (i.e. modify @c r and *this)
1059 template <typename RowT, typename ColT>
1060 class UT_BlockedRange2D : public tbb::blocked_range2d<RowT, ColT>
1061 {
1062 public:
1063  // TBB 2018 U3 no longer supports default blocked_range constructors
1064  UT_BlockedRange2D() = delete;
1065 
1066  /// NB: The arguments are in a different order than tbb
1067  UT_BlockedRange2D(RowT row_begin, RowT row_end,
1068  ColT col_begin, ColT col_end,
1069  size_t row_grainsize=1, size_t col_grainsize=1)
1070  : tbb::blocked_range2d<RowT, ColT>(row_begin, row_end, row_grainsize,
1071  col_begin, col_end, col_grainsize)
1072  {}
1074  : tbb::blocked_range2d<RowT, ColT>(R, split)
1075  {}
1076 };
1077 
1078 /// Performs a prefix sum across all the entries of the array.
1079 /// Ie,
1080 /// for (int i = 1; i < array.entries(); i++)
1081 /// array(i) = OP(array(i-1), array(i));
1082 /// tbb has this as tbb_parallel_scan but does not guarantee determinism.
1083 /// Note determinism is based on grain size, so that must be fixed.
1084 template <typename Op, typename T>
1085 void
1087  UT_Span<T> &array,
1088  const T identity,
1089  const Op &op,
1090  const int grain_size = 1024,
1091  const bool force_use_task_scope = true
1092 )
1093 {
1094  const exint asize = array.size();
1095 
1096  // Check serial. We need to have a enough grains to make
1097  // this worthwhile.
1098  if (asize < grain_size * 10)
1099  {
1100  T total = identity;
1101  for (exint i = 0, n = asize; i < n; i++)
1102  {
1103  total = op(total, array[i]);
1104  array[i] = total;
1105  }
1106  return;
1107  }
1108 
1109  // We could use the actual destination array to store the block
1110  // totals with some cleverness... For example, perhaps a stride &
1111  // offset so we could still recurse on prefix summing those totals?
1112  UT_Array<T> blocktotals;
1113  const exint nblocks = (asize + grain_size-1) / grain_size;
1114  blocktotals.setSizeNoInit(nblocks);
1115 
1116  // Scan for total for each block & compute the prefix sum
1117  // within the block
1118  UTparallelForEachNumber(nblocks, [&](const UT_BlockedRange<exint> &r)
1119  {
1120  for (exint block = r.begin(); block < r.end(); block++)
1121  {
1122  exint start = block * grain_size;
1123  exint end = SYSmin((block+1)*grain_size, asize);
1124  T total = identity;
1125  for (exint i = start; i < end; i++)
1126  {
1127  total = op(total, array[i]);
1128  array[i] = total;
1129  }
1130  // TODO: False sharing here?
1131  blocktotals(block) = total;
1132  }
1133  }, force_use_task_scope);
1134 
1135  // Prefix sum our block totals.
1137  identity, op,
1138  grain_size, force_use_task_scope);
1139 
1140  // Apply them back...
1141  UTparallelForEachNumber(nblocks, [&](const UT_BlockedRange<exint> &r)
1142  {
1143  for (exint block = r.begin(); block < r.end(); block++)
1144  {
1145  exint start = block * grain_size;
1146  exint end = SYSmin((block+1)*grain_size, asize);
1147  if (block > 0)
1148  {
1149  T total = blocktotals(block-1);
1150  for (exint i = start; i < end; i++)
1151  {
1152  array[i] = op(total, array[i]);
1153  }
1154  }
1155  }
1156  }, force_use_task_scope);
1157 }
1158 
1159 template <typename Op, typename T>
1160 void
1162  UT_Array<T> &array,
1163  const T identity,
1164  const Op &op,
1165  const int grain_size = 1024,
1166  const bool force_use_task_scope = true
1167 )
1168 {
1169  UT_Span<T> tempspan(array);
1171  tempspan, identity, op, grain_size, force_use_task_scope);
1172 }
1173 
1174 /// @{
1175 /// Wrapper around TBB's task isolation. In versions of TBB that don't support
1176 /// isolate, this uses a task arena.
1177 #if TBB_VERSION_MAJOR >= 2018
1178 template <typename F> static inline void
1179 UTisolate(F &f) { tbb::this_task_arena::isolate(f); }
1180 
1181 template <typename F> static inline void
1182 UTisolate(const F &f) { tbb::this_task_arena::isolate(f); }
1183 #else
1184 template <typename F> static inline void
1185 UTisolate(F &f)
1186 {
1187  tbb::task_arena __nested;
1188  __nested.execute(f);
1189 }
1190 template <typename F> static inline void
1191 UTisolate(const F &f)
1192 {
1193  tbb::task_arena __nested;
1194  __nested.execute(f);
1195 }
1196 #endif
1197 /// @}
1198 
1199 // The code below is originally from:
1200 // https://software.intel.com/en-us/articles/a-parallel-stable-sort-using-c11-for-tbb-cilk-plus-and-openmp
1201 // and is covered by the following copyright:
1202 /*
1203  Copyright (C) 2014 Intel Corporation
1204  All rights reserved.
1205 
1206  Redistribution and use in source and binary forms, with or without
1207  modification, are permitted provided that the following conditions
1208  are met:
1209 
1210  * Redistributions of source code must retain the above copyright
1211  notice, this list of conditions and the following disclaimer.
1212  * Redistributions in binary form must reproduce the above copyright
1213  notice, this list of conditions and the following disclaimer in
1214  the documentation and/or other materials provided with the
1215  distribution.
1216  * Neither the name of Intel Corporation nor the names of its
1217  contributors may be used to endorse or promote products derived
1218  from this software without specific prior written permission.
1219 
1220  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1221  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1222  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1223  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1224  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
1225  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
1226  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
1227  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
1228  AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
1229  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
1230  WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
1231  POSSIBILITY OF SUCH DAMAGE.
1232 */
1233 #include <utility>
1234 #include <iterator>
1235 #include <algorithm>
1236 
1237 namespace pss {
1238 
1239 namespace internal {
1240 
1241 //! Destroy sequence [xs,xe)
1242 template<class RandomAccessIterator>
1243 void serial_destroy( RandomAccessIterator zs, RandomAccessIterator ze ) {
1245  while( zs!=ze ) {
1246  --ze;
1247  (*ze).~T();
1248  }
1249 }
1250 
1251 //! Merge sequences [xs,xe) and [ys,ye) to output sequence [zs,(xe-xs)+(ye-ys)), using std::move
1252 template<class RandomAccessIterator1, class RandomAccessIterator2, class RandomAccessIterator3, class Compare>
1253 void serial_move_merge( RandomAccessIterator1 xs, RandomAccessIterator1 xe, RandomAccessIterator2 ys, RandomAccessIterator2 ye, RandomAccessIterator3 zs, Compare comp ) {
1254  if( xs!=xe ) {
1255  if( ys!=ye )
1256  {
1257  for(;;)
1258  {
1259  if( comp(*ys,*xs) ) {
1260  *zs = std::move(*ys);
1261  ++zs;
1262  if( ++ys==ye ) break;
1263  } else {
1264  *zs = std::move(*xs);
1265  ++zs;
1266  if( ++xs==xe ) goto movey;
1267  }
1268  }
1269  }
1270  ys = xs;
1271  ye = xe;
1272  }
1273 movey:
1274  std::move( ys, ye, zs );
1275 }
1276 
1277 template<typename RandomAccessIterator1, typename RandomAccessIterator2, typename Compare>
1278 void stable_sort_base_case( RandomAccessIterator1 xs, RandomAccessIterator1 xe, RandomAccessIterator2 zs, int inplace, Compare comp) {
1279  std::stable_sort( xs, xe, comp );
1280  if( inplace!=2 ) {
1281  RandomAccessIterator2 ze = zs + (xe-xs);
1283  if( inplace )
1284  // Initialize the temporary buffer
1285  for( ; zs<ze; ++zs )
1286  new(&*zs) T;
1287  else
1288  // Initialize the temporary buffer and move keys to it.
1289  for( ; zs<ze; ++xs, ++zs )
1290  new(&*zs) T(std::move(*xs));
1291  }
1292 }
1293 
1294 //! Raw memory buffer with automatic cleanup.
1296 {
1297  void* ptr;
1298 public:
1299  //! Try to obtain buffer of given size.
1300  raw_buffer( size_t bytes ) : ptr( operator new(bytes,std::nothrow) ) {}
1301  //! True if buffer was successfully obtained, zero otherwise.
1302  operator bool() const {return ptr;}
1303  //! Return pointer to buffer, or NULL if buffer could not be obtained.
1304  void* get() const {return ptr;}
1305  //! Destroy buffer
1306  ~raw_buffer() {operator delete(ptr);}
1307 };
1308 
1309 template<typename RandomAccessIterator1, typename RandomAccessIterator2, typename RandomAccessIterator3, typename Compare>
1310 void parallel_merge( RandomAccessIterator1 xs, RandomAccessIterator1 xe, RandomAccessIterator2 ys,
1311  RandomAccessIterator2 ye, RandomAccessIterator3 zs, bool destroy, Compare comp );
1312 
1313 template<typename RandomAccessIterator1, typename RandomAccessIterator2, typename RandomAccessIterator3, typename Compare>
1315 {
1316  RandomAccessIterator1 _xs, _xe;
1317  RandomAccessIterator2 _ys, _ye;
1318  RandomAccessIterator3 _zs;
1319  bool _destroy;
1320  Compare _comp;
1321  parallel_merge_invoke( RandomAccessIterator1 xs, RandomAccessIterator1 xe, RandomAccessIterator2 ys, RandomAccessIterator2 ye,
1322  RandomAccessIterator3 zs, bool destroy, Compare comp):
1323  _xs(xs), _xe(xe), _ys(ys), _ye(ye), _zs(zs), _destroy(destroy), _comp(comp) {}
1324 
1326 
1327 };
1328 
1329 // Merge sequences [xs,xe) and [ys,ye) to output sequence [zs,zs+(xe-xs)+(ye-ys))
1330 template<typename RandomAccessIterator1, typename RandomAccessIterator2, typename RandomAccessIterator3, typename Compare>
1331 void parallel_merge( RandomAccessIterator1 xs, RandomAccessIterator1 xe, RandomAccessIterator2 ys,
1332  RandomAccessIterator2 ye, RandomAccessIterator3 zs, bool destroy, Compare comp ) {
1333  const size_t MERGE_CUT_OFF = 2000;
1334  if( (xe-xs) + (ye-ys) <= MERGE_CUT_OFF ) {
1335  serial_move_merge( xs, xe, ys, ye, zs, comp );
1336  if( destroy ) {
1337  serial_destroy( xs, xe );
1338  serial_destroy( ys, ye );
1339  }
1340  } else {
1341  RandomAccessIterator1 xm;
1342  RandomAccessIterator2 ym;
1343  if( xe-xs < ye-ys ) {
1344  ym = ys+(ye-ys)/2;
1345  xm = std::upper_bound(xs,xe,*ym,comp);
1346  } else {
1347  xm = xs+(xe-xs)/2;
1348  ym = std::lower_bound(ys,ye,*xm,comp);
1349  }
1350  RandomAccessIterator3 zm = zs + ((xm-xs) + (ym-ys));
1351  tbb::parallel_invoke( parallel_merge_invoke<RandomAccessIterator1, RandomAccessIterator2, RandomAccessIterator3, Compare>( xs, xm, ys, ym, zs, destroy, comp ),
1353  }
1354 }
1355 
1356 template<typename RandomAccessIterator1, typename RandomAccessIterator2, typename Compare>
1357 void parallel_stable_sort_aux( RandomAccessIterator1 xs, RandomAccessIterator1 xe, RandomAccessIterator2 zs, int inplace, Compare comp );
1358 
1359 template<typename RandomAccessIterator1, typename RandomAccessIterator2, typename Compare>
1361 {
1362  RandomAccessIterator1 _xs, _xe;
1363  RandomAccessIterator2 _zs;
1364  bool _inplace;
1365  Compare _comp;
1366  parallel_stable_sort_aux_invoke( RandomAccessIterator1 xs, RandomAccessIterator1 xe, RandomAccessIterator2 zs, int inplace, Compare comp ):
1367  _xs(xs), _xe(xe), _zs(zs), _inplace(inplace), _comp(comp) {}
1368 
1370 
1371 };
1372 
1373 // Sorts [xs,xe), where zs[0:xe-xs) is temporary buffer supplied by caller.
1374 // Result is in [xs,xe) if inplace==true, otherwise in [zs,zs+(xe-xs))
1375 template<typename RandomAccessIterator1, typename RandomAccessIterator2, typename Compare>
1376 void parallel_stable_sort_aux( RandomAccessIterator1 xs, RandomAccessIterator1 xe, RandomAccessIterator2 zs, int inplace, Compare comp ) {
1377  const size_t SORT_CUT_OFF = 500;
1378  if( xe-xs<=SORT_CUT_OFF ) {
1379  stable_sort_base_case(xs, xe, zs, inplace, comp);
1380  } else {
1381  RandomAccessIterator1 xm = xs + (xe-xs)/2;
1382  RandomAccessIterator2 zm = zs + (xm-xs);
1383  RandomAccessIterator2 ze = zs + (xe-xs);
1384  tbb::parallel_invoke( parallel_stable_sort_aux_invoke<RandomAccessIterator1, RandomAccessIterator2, Compare>( xs, xm, zs, !inplace, comp ),
1386  if( inplace )
1387  parallel_merge( zs, zm, zm, ze, xs, inplace==2, comp );
1388  else
1389  parallel_merge( xs, xm, xm, xe, zs, false, comp );
1390  }
1391 }
1392 } // namespace internal
1393 
1394 template<typename RandomAccessIterator, typename Compare>
1395 void parallel_stable_sort( RandomAccessIterator xs, RandomAccessIterator xe, Compare comp ) {
1397  internal::raw_buffer z = internal::raw_buffer( sizeof(T)*(xe-xs) );
1398  if( z && UT_Thread::isThreadingEnabled() )
1399  internal::parallel_stable_sort_aux( xs, xe, (T*)z.get(), 2, comp );
1400  else
1401  // Not enough memory available - fall back on serial sort
1402  std::stable_sort( xs, xe, comp );
1403 }
1404 
1405 } // namespace pss
1406 
1407 
1408 #endif
ut_TaskScopedInvokeBody(const Body &body)
void UTparallelSort(RandomAccessIterator begin, RandomAccessIterator end, const Compare &compare)
UT_BlockedRange2D()=delete
SYS_FORCE_INLINE bool operator==(const ValueWrapper &cmp) const
UT_BlockedRange(T begin_value, T end_value, size_t grainsize=1)
SYS_FORCE_INLINE ValueWrapper & operator++()
void UTparallelFor(const Range &range, const Body &body, const int subscribe_ratio=2, const int min_grain_size=1, const bool force_use_task_scope=true)
void UTparallelDeterministicReduce(const Range &range, Body &body, const int grain_size, const bool force_use_task_scope=true)
size_t operator()(const RANGE &range) const
GLenum GLint * range
Definition: glcorearb.h:1925
tbb::split UT_Split
Definition: GA_PolyCounts.h:25
void UTparallelForTaskScope(const Range &range, const Body &body, const int subscribe_ratio=2, const int min_grain_size=1)
SYS_FORCE_INLINE bool operator!=(const ValueWrapper &cmp) const
friend void UTparallelDeterministicReduce(const Range &range, Body &body, const int grain_size, const bool force_use_taskscope)
void operator()(const Range &r)
void
Definition: png.h:1083
void UTparallelForEachNumber(IntType nitems, const Body &body, const bool force_use_task_scope=true)
GLuint start
Definition: glcorearb.h:475
void setSizeNoInit(exint newsize)
Definition: UT_Array.h:719
void UTserialReduce(const Range &range, Body &body)
ut_ReduceTaskScopedBody(Body *body)
CompareResults OIIO_API compare(const ImageBuf &A, const ImageBuf &B, float failthresh, float warnthresh, float failrelative, float warnrelative, ROI roi={}, int nthreads=0)
GLdouble GLdouble GLdouble z
Definition: glcorearb.h:848
int64 exint
Definition: SYS_Types.h:125
GLboolean GLboolean GLboolean GLboolean a
Definition: glcorearb.h:1222
void serial_destroy(RandomAccessIterator zs, RandomAccessIterator ze)
Destroy sequence [xs,xe)
PUGI__FN void sort(I begin, I end, const Pred &pred)
Definition: pugixml.cpp:7550
void UTparallelForLightItems(const Range &range, const Body &body, const bool force_use_task_scope=true)
void UTserialForEachNumber(IntType nitems, const Body &body, bool usetaskscope=true)
T exchangeAdd(T val)
uint64 value_type
Definition: GA_PrimCompat.h:29
void parallel_stable_sort_aux(RandomAccessIterator1 xs, RandomAccessIterator1 xe, RandomAccessIterator2 zs, int inplace, Compare comp)
static bool isThreadingEnabled()
OutGridT const XformOp bool bool
ut_ReduceTaskScopedBody(ut_ReduceTaskScopedBody &src, UT_Split)
std::optional< T > UT_Optional
Definition: UT_Optional.h:26
size_t UTestimatedNumItems(const RANGE &range)
This is needed by UT_CoarsenedRange.
IMATH_HOSTDEVICE constexpr int cmp(T a, T b) IMATH_NOEXCEPT
Definition: ImathFun.h:84
size_t operator()(const UT_BlockedRange2D< T > &range) const
#define UT_ASSERT_MSG(ZZ,...)
Definition: UT_Assert.h:168
#define SYS_DEPRECATED_REPLACE(__V__, __R__)
void join(ut_ReduceTaskScopedBody &other)
constexpr size_type size() const noexcept
Definition: UT_Span.h:484
UT_ParallelInvokeFunctors(const UT_Array< F1 > &functions)
Raw memory buffer with automatic cleanup.
GLdouble n
Definition: glcorearb.h:2008
GLfloat f
Definition: glcorearb.h:1926
void parallel_merge(RandomAccessIterator1 xs, RandomAccessIterator1 xe, RandomAccessIterator2 ys, RandomAccessIterator2 ye, RandomAccessIterator3 zs, bool destroy, Compare comp)
ut_TaskBody(const Body *body)
ut_TaskScopedInvokeBody(const ut_TaskScopedInvokeBody &src)
~raw_buffer()
Destroy buffer.
void operator()(const UT_BlockedRange< IntType > &range) const
ut_TaskScopedBody(const ut_TaskScopedBody &src)
const Body & body() const
GLuint GLuint end
Definition: glcorearb.h:475
static int getNumProcessors()
#define SYS_FORCE_INLINE
Definition: SYS_Inline.h:45
void UTparallelReduceHeavyItems(const Range &range, Body &body)
OIIO_UTIL_API void parallel_for(int32_t begin, int32_t end, function_view< void(int32_t)> task, paropt opt=0)
UT_BlockedRange2D(RowT row_begin, RowT row_end, ColT col_begin, ColT col_end, size_t row_grainsize=1, size_t col_grainsize=1)
NB: The arguments are in a different order than tbb.
SYS_FORCE_INLINE T operator*()
tbb::split UT_Split
Typedef to denote the "split" constructor of a range.
void operator()(const tbb::blocked_range< int > &r) const
friend void UTparallelFor(const Range &range, const Body &body, const int subscribe_ratio, const int min_grain_size, const bool force_use_task_scope)
UT_BlockedRange(UT_BlockedRange &R, UT_Split split)
void operator()(const Range &r) const
void operator()(const tbb::blocked_range< int > &r) const
ut_TaskScopedBody(const Body *body)
exint entries() const
Alias of size(). size() is preferred.
Definition: UT_Array.h:669
void operator()(const Range &r) const
UT_ParallelInvokePointers(const UT_Array< F1 * > &functions)
UT_BlockedRange()=delete
void UTparallelInvoke(bool parallel, F1 &&f1, F2 &&f2)
PcpNodeRef_ChildrenIterator begin(const PcpNodeRef::child_const_range &r)
Support for range-based for loops for PcpNodeRef children ranges.
Definition: node.h:587
void UTparallelStableSort(RandomAccessIterator begin, RandomAccessIterator end, const Compare &compare)
raw_buffer(size_t bytes)
Try to obtain buffer of given size.
void parallel_stable_sort(RandomAccessIterator xs, RandomAccessIterator xe, Compare comp)
auto items() const
ImageBuf OIIO_API max(Image_or_Const A, Image_or_Const B, ROI roi={}, int nthreads=0)
void * get() const
Return pointer to buffer, or NULL if buffer could not be obtained.
parallel_stable_sort_aux_invoke(RandomAccessIterator1 xs, RandomAccessIterator1 xe, RandomAccessIterator2 zs, int inplace, Compare comp)
UT_BlockedRange2D(UT_BlockedRange2D &R, UT_Split split)
UT_API bool UTperformanceIsRecordingThreadStats()
Determine if we're currently recording thread stats.
void serial_move_merge(RandomAccessIterator1 xs, RandomAccessIterator1 xe, RandomAccessIterator2 ys, RandomAccessIterator2 ye, RandomAccessIterator3 zs, Compare comp)
Merge sequences [xs,xe) and [ys,ye) to output sequence [zs,(xe-xs)+(ye-ys)), using std::move...
void UTparallelForHeavyItems(const Range &range, const Body &body)
#define UT_ASSERT(ZZ)
Definition: UT_Assert.h:165
GLboolean r
Definition: glcorearb.h:1222
void OIIO_UTIL_API split(string_view str, std::vector< string_view > &result, string_view sep=string_view(), int maxsplit=-1)
ut_ForEachNumberBody(const Body &body, SYS_AtomicInt< IntType > &it, IntType end)
void stable_sort_base_case(RandomAccessIterator1 xs, RandomAccessIterator1 xe, RandomAccessIterator2 zs, int inplace, Compare comp)
void UTparallelForEachNumberTaskScope(IntType nitems, const Body &body)
UT_CoarsenedRange(UT_CoarsenedRange &range, tbb::split spl)
void UTparallelDeterministicPrefixSumInPlace(UT_Span< T > &array, const T identity, const Op &op, const int grain_size=1024, const bool force_use_task_scope=true)
#define SYSmin(a, b)
Definition: SYS_Math.h:1953
GA_API const UT_StringHolder rest
Declare prior to use.
const ut_TaskScopedInvokeBody< Body > UTmakeTaskScopedInvokeBody(const Body &body)
SYS_FORCE_INLINE ValueWrapper(const T &it)
void UTparallelReduce(const Range &range, Body &body, const int subscribe_ratio=2, const int min_grain_size=1, const bool force_use_task_scope=true)
Definition: format.h:4365
friend void UTparallelReduce(const Range &range, Body &body, const int subscribe_ratio, const int min_grain_size, const bool force_use_taskscope)
void UTserialFor(const Range &range, const Body &body)
bool is_divisible() const
void UTparallelReduceLightItems(const Range &range, Body &body)
parallel_merge_invoke(RandomAccessIterator1 xs, RandomAccessIterator1 xe, RandomAccessIterator2 ys, RandomAccessIterator2 ye, RandomAccessIterator3 zs, bool destroy, Compare comp)
GLenum src
Definition: glcorearb.h:1793