HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
loops.h
Go to the documentation of this file.
1 //
2 // Copyright 2016 Pixar
3 //
4 // Licensed under the terms set forth in the LICENSE.txt file available at
5 // https://openusd.org/license.
6 //
7 #ifndef PXR_BASE_WORK_LOOPS_H
8 #define PXR_BASE_WORK_LOOPS_H
9 
10 /// \file work/loops.h
11 #include "pxr/pxr.h"
12 #include "pxr/base/work/api.h"
14 #include "pxr/base/work/impl.h"
16 
18 #include "pxr/base/tf/mallocTag.h"
19 
20 #include <algorithm>
21 
23 
24 using Work_ErrorTransports = tbb::concurrent_vector<TfErrorTransport>;
25 
26 template <class Fn>
28 {
29 public:
31  Fn &&callback,
32  Work_ErrorTransports *errors)
33  : _callback(callback)
34  , _errors(errors) {}
35 
36  template <typename ... Args>
37  void operator()(Args&&... args) const {
38  TfErrorMark m;
39  _callback(std::forward<Args>(args)...);
40  if (!m.IsClean()) {
41  TfErrorTransport transport = m.Transport();
42  _errors->grow_by(1)->swap(transport);
43  }
44  }
45 
46 private:
47  Fn & _callback;
48  Work_ErrorTransports *_errors;
49 };
50 
51 template <class Fn>
53 {
54 public:
56  Fn &&callback,
57  Work_ErrorTransports *errors)
58  : _callback(callback)
59  , _errors(errors)
60  , _mallocTagStack(TfMallocTag::GetCurrentStackState()) {}
61 
62  template <typename ... Args>
63  void operator()(Args&&... args) const {
64  TfErrorMark m;
65  TfMallocTag::StackOverride ovr(_mallocTagStack);
66  _callback(std::forward<Args>(args)...);
67  if (!m.IsClean()) {
68  TfErrorTransport transport = m.Transport();
69  _errors->grow_by(1)->swap(transport);
70  }
71  }
72 
73 private:
74  Fn & _callback;
75  Work_ErrorTransports *_errors;
76  TfMallocTag::StackState _mallocTagStack;
77 };
78 
79 template <class Fn>
81 {
82 public:
84  Fn &&callback,
85  Work_ErrorTransports *errors)
86  : _callback(callback)
87  , _errors(errors) {}
88 
89  template <typename Arg>
90  void operator()(Arg &&arg) const {
91  TfErrorMark m;
92  _callback(std::forward<Arg>(arg));
93  if (!m.IsClean()) {
94  TfErrorTransport transport = m.Transport();
95  _errors->grow_by(1)->swap(transport);
96  }
97  }
98 
99 private:
100  Fn & _callback;
101  Work_ErrorTransports *_errors;
102 };
103 
104 template <class Fn>
106 {
107 public:
109  Fn &&callback,
110  Work_ErrorTransports *errors)
111  : _callback(callback)
112  , _errors(errors)
113  , _mallocTagStack(TfMallocTag::GetCurrentStackState()) {}
114 
115  template <typename Arg>
116  void operator()(Arg &&arg) const {
117  TfErrorMark m;
118  TfMallocTag::StackOverride ovr(_mallocTagStack);
119  _callback(std::forward<Arg>(arg));
120  if (!m.IsClean()) {
121  TfErrorTransport transport = m.Transport();
122  _errors->grow_by(1)->swap(transport);
123  }
124  }
125 
126 private:
127  Fn & _callback;
128  Work_ErrorTransports *_errors;
129  TfMallocTag::StackState _mallocTagStack;
130 };
131 
132 ///////////////////////////////////////////////////////////////////////////////
133 ///
134 /// WorkSerialForN(size_t n, CallbackType callback)
135 ///
136 /// A serial version of WorkParallelForN as a drop in replacement to
137 /// selectively turn off multithreading for a single parallel loop for easier
138 /// debugging.
139 ///
140 /// Callback must be of the form:
141 ///
142 /// void LoopCallback(size_t begin, size_t end);
143 ///
144 template<typename Fn>
145 void
146 WorkSerialForN(size_t n, Fn &&fn)
147 {
148  std::forward<Fn>(fn)(0, n);
149 }
150 
151 ///////////////////////////////////////////////////////////////////////////////
152 ///
153 /// WorkParallelForN(size_t n, CallbackType callback, size_t grainSize = 1)
154 ///
155 /// Runs \p callback in parallel over the range 0 to n.
156 ///
157 /// Callback must be of the form:
158 ///
159 /// void LoopCallback(size_t begin, size_t end);
160 ///
161 /// grainSize specifies a minimum amount of work to be done per-thread. There
162 /// is overhead to launching a thread (or task) and a typical guideline is that
163 /// you want to have at least 10,000 instructions to count for the overhead of
164 /// launching a thread.
165 ///
166 template <typename Fn>
167 void
168 WorkParallelForN(size_t n, Fn &&callback, size_t grainSize)
169 {
170  if (n == 0)
171  return;
172 
173  // Don't bother with parallel_for, if concurrency is limited to 1.
174  if (WorkHasConcurrency()) {
176  Work_ErrorTransports errorTransports;
177  if (TfMallocTag::IsInitialized()) {
179  task(std::forward<Fn>(callback), &errorTransports);
180  WorkImpl_ParallelForN(n, task, grainSize);
181  }
182  else {
184  task(std::forward<Fn>(callback), &errorTransports);
185  WorkImpl_ParallelForN(n, task, grainSize);
186  }
187 
188  for (auto &et: errorTransports) {
189  et.Post();
190  }
191  } else {
192  // If concurrency is limited to 1, execute serially.
193  WorkSerialForN(n, std::forward<Fn>(callback));
194  }
195 }
196 
197 ///////////////////////////////////////////////////////////////////////////////
198 ///
199 /// WorkParallelForN(size_t n, CallbackType callback, size_t grainSize = 1)
200 ///
201 /// Runs \p callback in parallel over the range 0 to n.
202 ///
203 /// Callback must be of the form:
204 ///
205 /// void LoopCallback(size_t begin, size_t end);
206 ///
207 ///
208 template <typename Fn>
209 void
210 WorkParallelForN(size_t n, Fn &&callback)
211 {
212  WorkParallelForN(n, std::forward<Fn>(callback), 1);
213 }
214 
215 ///////////////////////////////////////////////////////////////////////////////
216 ///
217 /// WorkParallelForTBBRange(const RangeType &r, Fn &&callback)
218 ///
219 /// Runs \p callback in parallel over a RangeType that adheres to TBB's
220 /// splittable range requirements:
221 /// https://oneapi-spec.uxlfoundation.org/specifications/oneapi/latest/elements/onetbb/source/named_requirements/algorithms/range
222 ///
223 /// Callback must be of the form:
224 ///
225 /// void LoopCallback(RangeType range);
226 ///
227 ///
228 template <typename RangeType, typename Fn>
229 void
230 WorkParallelForTBBRange(const RangeType &range, Fn &&callback)
231 {
232  // Don't bother with parallel_for, if concurrency is limited to 1.
233  if (WorkHasConcurrency()) {
235  // Use the work backend's ParallelForTBBRange if one exists
236  // otherwise use the default implementation below that builds off of the
237  // dispatcher.
238 #if defined WORK_IMPL_HAS_PARALLEL_FOR_TBB_RANGE
239  Work_ErrorTransports errorTransports;
240  if (TfMallocTag::IsInitialized()) {
242  task(std::forward<Fn>(callback), &errorTransports);
243  WorkImpl_ParallelForTBBRange(range, task);
244  }
245  else {
247  task(std::forward<Fn>(callback), &errorTransports);
248  WorkImpl_ParallelForTBBRange(range, task);
249  }
250  for (auto &et: errorTransports) {
251  et.Post();
252  }
253 #else
254  // The parallel task responsible for recursively sub-dividing the range
255  // and invoking the callback on the sub-ranges.
256  class _RangeTask
257  {
258  public:
259  _RangeTask(
260  WorkDispatcher &dispatcher,
261  RangeType &&range,
262  const Fn &callback)
263  : _dispatcher(dispatcher)
264  , _range(std::move(range))
265  , _callback(callback) {}
266 
267  void operator()() const {
268  // Subdivide the given range until it is no longer divisible, and
269  // recursively spawn _RangeTasks for the right side of the split.
270  RangeType &leftRange = _range;
271  while (leftRange.is_divisible()) {
272  RangeType rightRange(leftRange, tbb::split());
273  _dispatcher.Run(_RangeTask(
274  _dispatcher, std::move(rightRange), _callback));
275  }
276 
277  // If there are any more entries remaining in the left-most side
278  // of the given range, invoke the callback on the left-most range.
279  if (!leftRange.empty()) {
280  std::invoke(_callback, leftRange);
281  }
282  }
283 
284  private:
285  WorkDispatcher &_dispatcher;
286  mutable RangeType _range;
287  const Fn &_callback;
288  };
289 
290  WorkDispatcher dispatcher;
291  RangeType range = range;
292  dispatcher.Run(_RangeTask(
293  dispatcher, range, std::forward<Fn>(callback)));
294 #endif
295  } else {
296  // If concurrency is limited to 1, execute serially.
297  std::forward<Fn>(callback)(range);
298  }
299 }
300 
301 ///////////////////////////////////////////////////////////////////////////////
302 ///
303 /// WorkParallelForEach(Iterator first, Iterator last, CallbackType callback)
304 ///
305 /// Callback must be of the form:
306 ///
307 /// void LoopCallback(T elem);
308 ///
309 /// where the type T is deduced from the type of the InputIterator template
310 /// argument.
311 ///
312 ///
313 template <typename InputIterator, typename Fn>
314 inline void
316  InputIterator first, InputIterator last, Fn &&fn)
317 {
318  if (WorkHasConcurrency()) {
320  Work_ErrorTransports errorTransports;
321  if (TfMallocTag::IsInitialized()) {
323  task(std::forward<Fn>(fn), &errorTransports);
324  WorkImpl_ParallelForEach(first, last, task);
325  }
326  else {
328  task(std::forward<Fn>(fn), &errorTransports);
329  WorkImpl_ParallelForEach(first, last, task);
330  }
331  for (auto &et: errorTransports) {
332  et.Post();
333  }
334  } else {
335  std::for_each(first, last, std::forward<Fn>(fn));
336  }
337 }
338 
340 
341 #endif // PXR_BASE_WORK_LOOPS_H
GLint first
Definition: glcorearb.h:405
GLenum GLint * range
Definition: glcorearb.h:1925
void operator()(Args &&...args) const
Definition: loops.h:37
#define PXR_NAMESPACE_OPEN_SCOPE
Definition: pxr.h:73
void WorkImpl_ParallelForTBBRange(const RangeType &range, Fn &&callback)
Definition: loops_impl.h:63
void Run(Callable &&c)
Definition: dispatcher.h:63
void WorkSerialForN(size_t n, Fn &&fn)
Definition: loops.h:146
void operator()(Arg &&arg) const
Definition: loops.h:90
auto arg(const Char *name, const T &arg) -> detail::named_arg< Char, T >
Definition: core.h:1859
TfErrorTransport Transport() const
Definition: errorMark.h:109
void WorkImpl_ParallelForEach(InputIterator first, InputIterator last, Fn &&fn)
Definition: loops_impl.h:73
void WorkParallelForN(size_t n, Fn &&callback, size_t grainSize)
Definition: loops.h:168
GLdouble n
Definition: glcorearb.h:2008
Work_MallocTagsLoopsTaskWrapper(Fn &&callback, Work_ErrorTransports *errors)
Definition: loops.h:55
void WorkParallelForTBBRange(const RangeType &range, Fn &&callback)
Definition: loops.h:230
Work_MallocTagsLoopsForEachTaskWrapper(Fn &&callback, Work_ErrorTransports *errors)
Definition: loops.h:108
tbb::concurrent_vector< TfErrorTransport > Work_ErrorTransports
Definition: loops.h:24
void operator()(Args &&...args) const
Definition: loops.h:63
bool IsClean() const
Definition: errorMark.h:82
WORK_API bool WorkHasConcurrency()
Work_LoopsForEachTaskWrapper(Fn &&callback, Work_ErrorTransports *errors)
Definition: loops.h:83
void WorkParallelForEach(InputIterator first, InputIterator last, Fn &&fn)
Definition: loops.h:315
__hostdev__ uint64_t last(uint32_t i) const
Definition: NanoVDB.h:5976
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:74
**If you just want to fire and args
Definition: thread.h:618
PXR_NAMESPACE_OPEN_SCOPE void WorkImpl_ParallelForN(size_t n, Fn &&callback, size_t grainSize)
Definition: loops_impl.h:31
Work_LoopsTaskWrapper(Fn &&callback, Work_ErrorTransports *errors)
Definition: loops.h:30
void OIIO_UTIL_API split(string_view str, std::vector< string_view > &result, string_view sep=string_view(), int maxsplit=-1)
int invoke(const Func &taskFunc1, Rest...taskFuncN)
Definition: Invoke.h:64
#define PXR_WORK_IMPL_NAMESPACE_USING_DIRECTIVE
Definition: impl.h:17
void operator()(Arg &&arg) const
Definition: loops.h:116