32 #ifndef __UT_ParallelUtil__
33 #define __UT_ParallelUtil__
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>
61 template <
typename RowT,
typename ColT=RowT>
65 template<
typename RANGE >
77 template<
typename T >
84 return range.rows().size() * range.cols().size();
89 template <
typename RANGE>
101 template<
typename RANGE >
112 myGrainSize(range.myGrainSize)
121 RANGE::is_divisible() &&
130 myGrainSize(grain_size)
134 template <
typename Range,
typename Body>
136 const Range &
range,
const Body &body,
137 const int subscribe_ratio,
const int min_grain_size,
138 const bool force_use_task_scope
140 template <
typename Range,
typename Body>
142 const Range &
range, Body &body,
143 const int subscribe_ratio,
const int min_grain_size,
144 const bool force_use_taskscope
146 template <
typename Range,
typename Body>
148 const Range &
range, Body &body,
const int grain_size,
149 const bool force_use_taskscope
157 template<
typename Range,
typename Body>
168 : myBody(src.myBody),
169 myParentTaskScope(src.myParentTaskScope)
187 template<
typename Range,
typename Body>
203 template <
typename IntType,
typename Body>
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
311 if (est_range_size == 0)
315 if (num_processors == 1 || est_range_size <= min_grain_size ||
322 size_t grain_size(min_grain_size);
323 if( subscribe_ratio > 0 )
326 est_range_size / (subscribe_ratio * num_processors)
335 tbb::simple_partitioner());
341 tbb::simple_partitioner());
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
354 UTparallelFor(range, body, subscribe_ratio, min_grain_size,
true);
360 template <
typename Range,
typename Body>
363 const bool force_use_task_scope =
true)
380 template <
typename Range,
typename Body>
404 template <
typename IntType,
typename Body>
413 if (num_processors == 1)
418 if (nitems <= num_processors)
435 template <
typename IntType,
typename Body>
439 for (IntType i = 0; i < nitems; ++i)
446 template <
typename IntType,
typename Body>
455 template <
typename Range,
typename Body>
463 template<
typename Body>
474 : myBody(src.myBody),
475 myParentTaskScope(src.myParentTaskScope)
494 template <
typename Body>
504 template <
typename F1,
typename F2>
519 template <
typename F1,
typename F2,
typename... Rest>
535 template <
typename F1>
540 : myFunctions(functions) {}
543 for (
int i = r.begin(); i != r.end(); ++i)
553 template <
typename F1>
564 for (
int i = 0; i < funs.
entries(); i++)
569 template <
typename F1>
574 : myFunctions(functions) {}
577 for (
int i = r.begin(); i != r.end(); ++i)
587 template <
typename F1>
598 for (
int i = 0; i < funs.
entries(); i++)
607 template<
typename Range,
typename Body>
619 : myParentTaskScope(src.myParentTaskScope)
638 const Body &
body()
const {
return myBodyPtr ? *myBodyPtr : *myBody; }
639 Body &
body() {
return myBodyPtr ? *myBodyPtr : *myBody; }
719 template <
typename Range,
typename Body>
723 const int subscribe_ratio = 2,
724 const int min_grain_size = 1,
725 const bool force_use_task_scope =
true
737 if (est_range_size == 0)
741 if (num_processors == 1 || est_range_size <= min_grain_size ||
748 size_t grain_size(min_grain_size);
749 if( subscribe_ratio > 0 )
752 est_range_size / (subscribe_ratio * num_processors)
759 tbb::parallel_reduce(coarsened_range,
761 tbb::simple_partitioner());
765 tbb::parallel_reduce(coarsened_range, body, tbb::simple_partitioner());
779 template <
typename Range,
typename Body>
783 const int grain_size,
784 const bool force_use_task_scope =
true
792 if (est_range_size == 0)
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.");
804 tbb::parallel_deterministic_reduce(coarsened_range,
806 tbb::simple_partitioner());
810 tbb::parallel_deterministic_reduce(coarsened_range, body);
817 template <
typename Range,
typename Body>
826 template <
typename Range,
typename Body>
834 template <
typename Range,
typename Body>
840 UTparallelCancelGroupExecution()
842 tbb::task::current_context()->cancel_group_execution();
849 template <
typename RandomAccessIterator,
typename Compare>
853 tbb::parallel_sort(begin, end, compare);
862 template <
typename RandomAccessIterator>
866 tbb::parallel_sort(begin, end);
875 template <
typename T>
879 tbb::parallel_sort(begin, end);
887 template<
typename RandomAccessIterator,
typename Compare>
892 template<
class RandomAccessIterator>
906 template <
typename RandomAccessIterator,
typename Compare>
919 template <
typename RandomAccessIterator>
931 template <
typename T>
943 template <
typename T,
typename Compare>
957 template <
typename T>
972 template <
typename T,
typename Compare>
991 template <
typename T>
999 : tbb::blocked_range<
T>(begin_value, end_value, grainsize)
1002 : tbb::blocked_range<
T>(R, split)
1023 {
return (myCurrent == cmp.myCurrent); }
1026 {
return !(*
this ==
cmp); }
1059 template <
typename RowT,
typename ColT>
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)
1074 : tbb::blocked_range2d<RowT, ColT>(R, split)
1084 template <
typename Op,
typename T>
1090 const int grain_size = 1024,
1091 const bool force_use_task_scope =
true
1098 if (asize < grain_size * 10)
1101 for (
exint i = 0,
n = asize; i <
n; i++)
1103 total =
op(total, array[i]);
1113 const exint nblocks = (asize + grain_size-1) / grain_size;
1120 for (
exint block = r.begin(); block < r.end(); block++)
1125 for (
exint i = start; i <
end; i++)
1127 total =
op(total, array[i]);
1131 blocktotals(block) = total;
1133 }, force_use_task_scope);
1138 grain_size, force_use_task_scope);
1143 for (
exint block = r.begin(); block < r.end(); block++)
1149 T total = blocktotals(block-1);
1150 for (
exint i = start; i <
end; i++)
1152 array[i] =
op(total, array[i]);
1156 }, force_use_task_scope);
1159 template <
typename Op,
typename T>
1165 const int grain_size = 1024,
1166 const bool force_use_task_scope =
true
1171 tempspan, identity, op, grain_size, force_use_task_scope);
1177 #if TBB_VERSION_MAJOR >= 2018
1178 template <
typename F>
static inline void
1179 UTisolate(F &
f) { tbb::this_task_arena::isolate(f); }
1181 template <
typename F>
static inline void
1182 UTisolate(
const F &
f) { tbb::this_task_arena::isolate(f); }
1184 template <
typename F>
static inline void
1187 tbb::task_arena __nested;
1188 __nested.execute(f);
1190 template <
typename F>
static inline void
1191 UTisolate(
const F &f)
1193 tbb::task_arena __nested;
1194 __nested.execute(f);
1235 #include <algorithm>
1239 namespace internal {
1242 template<
class RandomAccessIterator>
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 ) {
1259 if( comp(*ys,*xs) ) {
1260 *zs = std::move(*ys);
1262 if( ++ys==ye )
break;
1264 *zs = std::move(*xs);
1266 if( ++xs==xe )
goto movey;
1274 std::move( ys, ye, zs );
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 );
1281 RandomAccessIterator2 ze = zs + (xe-xs);
1285 for( ; zs<ze; ++zs )
1289 for( ; zs<ze; ++xs, ++zs )
1290 new(&*zs) T(std::move(*xs));
1302 operator bool()
const {
return ptr;}
1304 void*
get()
const {
return ptr;}
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 );
1313 template<
typename RandomAccessIterator1,
typename RandomAccessIterator2,
typename RandomAccessIterator3,
typename Compare>
1321 parallel_merge_invoke( RandomAccessIterator1 xs, RandomAccessIterator1 xe, RandomAccessIterator2 ys, RandomAccessIterator2 ye,
1322 RandomAccessIterator3 zs,
bool destroy, Compare comp):
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 ) {
1341 RandomAccessIterator1 xm;
1342 RandomAccessIterator2 ym;
1343 if( xe-xs < ye-ys ) {
1345 xm = std::upper_bound(xs,xe,*ym,comp);
1348 ym = std::lower_bound(ys,ye,*xm,comp);
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 ),
1356 template<
typename RandomAccessIterator1,
typename RandomAccessIterator2,
typename Compare>
1357 void parallel_stable_sort_aux( RandomAccessIterator1 xs, RandomAccessIterator1 xe, RandomAccessIterator2 zs,
int inplace, Compare comp );
1359 template<
typename RandomAccessIterator1,
typename RandomAccessIterator2,
typename Compare>
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 ) {
1381 RandomAccessIterator1 xm = xs + (xe-xs)/2;
1382 RandomAccessIterator2 zm = zs + (xm-xs);
1383 RandomAccessIterator2 ze = zs + (xe-xs);
1394 template<
typename RandomAccessIterator,
typename Compare>
1402 std::stable_sort( xs, xe, comp );
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
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 UTparallelForEachNumber(IntType nitems, const Body &body, const bool force_use_task_scope=true)
void setSizeNoInit(exint newsize)
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)
RandomAccessIterator1 _xe
GLdouble GLdouble GLdouble z
RandomAccessIterator1 _xe
GLboolean GLboolean GLboolean GLboolean a
void serial_destroy(RandomAccessIterator zs, RandomAccessIterator ze)
Destroy sequence [xs,xe)
PUGI__FN void sort(I begin, I end, const Pred &pred)
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)
RandomAccessIterator2 _ye
void parallel_stable_sort_aux(RandomAccessIterator1 xs, RandomAccessIterator1 xe, RandomAccessIterator2 zs, int inplace, Compare comp)
static bool isThreadingEnabled()
ut_ReduceTaskScopedBody(ut_ReduceTaskScopedBody &src, UT_Split)
std::optional< T > UT_Optional
size_t UTestimatedNumItems(const RANGE &range)
This is needed by UT_CoarsenedRange.
IMATH_HOSTDEVICE constexpr int cmp(T a, T b) IMATH_NOEXCEPT
size_t operator()(const UT_BlockedRange2D< T > &range) const
#define UT_ASSERT_MSG(ZZ,...)
#define SYS_DEPRECATED_REPLACE(__V__, __R__)
void join(ut_ReduceTaskScopedBody &other)
RandomAccessIterator2 _ys
constexpr size_type size() const noexcept
UT_ParallelInvokeFunctors(const UT_Array< F1 > &functions)
RandomAccessIterator2 _zs
Raw memory buffer with automatic cleanup.
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
static int getNumProcessors()
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*()
RandomAccessIterator1 _xs
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.
void operator()(const Range &r) const
UT_ParallelInvokePointers(const UT_Array< F1 * > &functions)
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.
void UTparallelStableSort(RandomAccessIterator begin, RandomAccessIterator end, const Compare &compare)
RandomAccessIterator3 _zs
raw_buffer(size_t bytes)
Try to obtain buffer of given size.
void parallel_stable_sort(RandomAccessIterator xs, RandomAccessIterator xe, Compare comp)
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)
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)
RandomAccessIterator1 _xs
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)
GA_API const UT_StringHolder rest
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)
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)