Loading...
Searching...
No Matches
Parallel-Impl.hpp
Go to the documentation of this file.
1// This code is based on Jet framework.
2// Copyright (c) 2018 Doyub Kim
3// CubbyFlow is voxel-based fluid simulation engine for computer games.
4// Copyright (c) 2020 CubbyFlow Team
5// Core Part: Chris Ohk, Junwoo Hwang, Jihong Sin, Seungwoo Yoo
6// AI Part: Dongheon Cho, Minseo Kim
7// We are making my contributions/submissions to this project solely in our
8// personal capacity and are not conveying any rights to any intellectual
9// property of any third parties.
10
11#ifndef CUBBYFLOW_PARALLEL_IMPL_HPP
12#define CUBBYFLOW_PARALLEL_IMPL_HPP
13
16
17#if defined(CUBBYFLOW_TASKING_HPX)
18#include <hpx/include/future.hpp>
19#include <hpx/include/parallel_fill.hpp>
20#include <hpx/include/parallel_for_each.hpp>
21#include <hpx/include/parallel_for_loop.hpp>
22#include <hpx/include/parallel_reduce.hpp>
23#include <hpx/include/parallel_sort.hpp>
24#endif
25
26#if defined(CUBBYFLOW_TASKING_TBB)
27#include <tbb/parallel_for.h>
28#include <tbb/parallel_reduce.h>
29#include <tbb/parallel_sort.h>
30#include <tbb/task_arena.h>
31#elif defined(CUBBYFLOW_TASKING_CPP11THREAD)
32#include <thread>
33#endif
34
35#include <algorithm>
36#include <cmath>
37#include <future>
38#include <vector>
39
40namespace CubbyFlow
41{
42namespace Internal
43{
44#if defined(CUBBYFLOW_TASKING_HPX)
45template <typename Task>
46using future = hpx::future<Task>;
47#else
48template <typename Task>
49using future = std::future<Task>;
50#endif
51
52template <typename TASK>
53using operator_return_t = typename std::invoke_result_t<TASK>;
54
55template <typename TASK>
57{
58#if defined(CUBBYFLOW_TASKING_HPX)
59 return hpx::async(std::forward<TASK>(fn));
60#elif defined(CUBBYFLOW_TASKING_TBB)
61 using package_t = std::packaged_task<operator_return_t<TASK>()>;
62 package_t task(std::forward<TASK>(fn));
63 auto result = task.get_future();
64
65 struct LocalTBBTask
66 {
67 // TBB invokes queued functors through a const call operator.
68 mutable package_t task;
69
70 void operator()() const
71 {
72 task();
73 }
74 };
75
76 tbb::task_arena arena{ tbb::task_arena::attach{} };
77 arena.enqueue(LocalTBBTask{ std::move(task) });
78
79 return result;
80#elif defined(CUBBYFLOW_TASKING_CPP11THREAD)
81 return std::async(std::launch::async, std::forward<TASK>(fn));
82#else
83 return std::async(std::launch::deferred, std::forward<TASK>(fn));
84#endif
85}
86
87// Adopted from:
88// Radenski, A.
89// Shared Memory, Message Passing, and Hybrid Merge Sorts for Standalone and
90// Clustered SMPs. Proc PDPTA'11, the 2011 International Conference on Parallel
91// and Distributed Processing Techniques and Applications, CSREA Press
92// (H. Arabnia, Ed.), 2011, pp. 367 - 373.
93template <typename RandomIterator, typename RandomIterator2,
94 typename CompareFunction>
97{
98 size_t i1 = 0;
99 size_t i2 = size / 2;
100 size_t tempi = 0;
101
102 while (i1 < size / 2 && i2 < size)
103 {
104 if (compareFunction(a[i1], a[i2]))
105 {
106 temp[tempi] = a[i1];
107 i1++;
108 }
109 else
110 {
111 temp[tempi] = a[i2];
112 i2++;
113 }
114
115 tempi++;
116 }
117
118 while (i1 < size / 2)
119 {
120 temp[tempi] = a[i1];
121 i1++;
122 tempi++;
123 }
124
125 while (i2 < size)
126 {
127 temp[tempi] = a[i2];
128 i2++;
129 tempi++;
130 }
131
132 // Copy sorted temp array into main array, a
133 ParallelFor(ZERO_SIZE, size, [&a, &temp](size_t i) { a[i] = temp[i]; });
134}
135
136template <typename RandomIterator, typename RandomIterator2,
137 typename CompareFunction>
140{
141 if (numThreads == 1)
142 {
143 std::sort(a, a + size, compareFunction);
144 }
145 else if (numThreads > 1)
146 {
147 std::vector<future<void>> pool;
148 pool.reserve(2);
149
150 auto launchRange = [compareFunction](RandomIterator begin, size_t k2,
152 unsigned int numThreads) {
154 };
155
156 pool.emplace_back(
158 launchRange(a, size / 2, temp, numThreads / 2);
159 }));
160
161 pool.emplace_back(
163 launchRange(a + size / 2, size - size / 2, temp + size / 2,
164 numThreads - numThreads / 2);
165 }));
166
167 // Wait for jobs to finish
168 for (auto& f : pool)
169 {
170 if (f.valid())
171 {
172 f.wait();
173 }
174 }
175
177 }
178}
179} // namespace Internal
180
181template <typename RandomIterator, typename T>
182void ParallelFill(const RandomIterator& begin, const RandomIterator& end,
183 const T& value, ExecutionPolicy policy)
184{
185 auto diff = end - begin;
186 if (diff <= 0)
187 {
188 return;
189 }
190
191#if defined(CUBBYFLOW_TASKING_HPX)
192 hpx::parallel::fill(hpx::parallel::execution::par, begin, end, value);
193#else
194 size_t size = static_cast<size_t>(diff);
196 ZERO_SIZE, size, [begin, value](size_t i) { begin[i] = value; },
197 policy);
198#endif
199}
200
201// Adopted from http://ideone.com/Z7zldb
202template <typename IndexType, typename Function>
205{
206 if (beginIndex > endIndex)
207 {
208 return;
209 }
210
212 {
213#if defined(CUBBYFLOW_TASKING_TBB)
214 (void)policy;
215 tbb::parallel_for(beginIndex, endIndex, function);
216#elif defined(CUBBYFLOW_TASKING_HPX)
217 (void)policy;
218 hpx::parallel::for_loop(hpx::parallel::execution::par, beginIndex,
220#elif defined(CUBBYFLOW_TASKING_CPP11THREAD)
221 // Estimate number of threads in the pool
222 const unsigned int numThreadsHint = GetMaxNumberOfThreads();
223 const unsigned int numThreads =
224 (numThreadsHint == 0u) ? 8u : numThreadsHint;
225
226 // Size of a slice for the range functions
228 IndexType slice = static_cast<IndexType>(
229 std::round(n / static_cast<double>(numThreads)));
230 slice = (std::max)(slice, IndexType(1));
231
232 // [Helper] Inner loop
234 for (IndexType k = k1; k < k2; ++k)
235 {
236 function(k);
237 }
238 };
239
240 // Create pool and launch jobs
241 std::vector<std::thread> pool;
242 pool.reserve(numThreads);
244 IndexType i2 = (std::min)(beginIndex + slice, endIndex);
245
246 for (unsigned int i = 0; i + 1 < numThreads && i1 < endIndex; ++i)
247 {
248 pool.emplace_back(launchRange, i1, i2);
249 i1 = i2;
250 i2 = (std::min)(i2 + slice, endIndex);
251 }
252
253 if (i1 < endIndex)
254 {
255 pool.emplace_back(launchRange, i1, endIndex);
256 }
257
258 // Wait for jobs to finish
259 for (std::thread& t : pool)
260 {
261 if (t.joinable())
262 {
263 t.join();
264 }
265 }
266#else
267 (void)policy;
268
269#if defined(CUBBYFLOW_TASKING_OPENMP)
270#pragma omp parallel for
271#if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
273 {
274#else // !MSVC || Intel
275 for (auto i = beginIndex; i < endIndex; ++i)
276 {
277#endif // MSVC && !Intel
278 function(i);
279 }
280#else // CUBBYFLOW_TASKING_SERIAL
281 for (auto i = beginIndex; i < endIndex; ++i)
282 {
283 function(i);
284 }
285#endif // CUBBYFLOW_TASKING_OPENMP
286#endif
287 }
288 else
289 {
290 for (auto i = beginIndex; i < endIndex; ++i)
291 {
292 function(i);
293 }
294 }
295}
296
297template <typename IndexType, typename Function>
300{
301 if (beginIndex > endIndex)
302 {
303 return;
304 }
305
307 {
308#if defined(CUBBYFLOW_TASKING_TBB)
309 tbb::parallel_for(
310 tbb::blocked_range<IndexType>(beginIndex, endIndex),
311 [&function](const tbb::blocked_range<IndexType>& range) {
313 });
314#else
315 // Estimate number of threads in the pool
316 const unsigned int numThreadsHint = GetMaxNumberOfThreads();
317 const unsigned int numThreads =
319
320 // Size of a slice for the range functions
322 IndexType slice = static_cast<IndexType>(
323 std::round(n / static_cast<double>(numThreads)));
324 slice = (std::max)(slice, IndexType(1));
325
326 // Create pool and launch jobs
327 std::vector<CubbyFlow::Internal::future<void>> pool;
328 pool.reserve(numThreads);
330 IndexType i2 = (std::min)(beginIndex + slice, endIndex);
331
332 for (unsigned int i = 0; i + 1 < numThreads && i1 < endIndex; ++i)
333 {
334 pool.emplace_back(
335 Internal::Async([function, i1, i2]() { function(i1, i2); }));
336 i1 = i2;
337 i2 = (std::min)(i2 + slice, endIndex);
338 }
339
340 if (i1 < endIndex)
341 {
342 pool.emplace_back(Internal::Async(
343 [function, i1, endIndex]() { function(i1, endIndex); }));
344 }
345
346 // Wait for jobs to finish
347 for (auto& f : pool)
348 {
349 if (f.valid())
350 {
351 f.wait();
352 }
353 }
354#endif
355 }
356 else
357 {
359 }
360}
361
362template <typename IndexType, typename Function>
377
378template <typename IndexType, typename Function>
391
392template <typename IndexType, typename Function>
412
413template <typename IndexType, typename Function>
428
429template <typename IndexType, typename Value, typename Function,
430 typename Reduce>
432 const Value& identity, const Function& function,
433 const Reduce& reduce, ExecutionPolicy policy)
434{
435 if (beginIndex > endIndex)
436 {
437 return identity;
438 }
439
441 {
442#if defined(CUBBYFLOW_TASKING_TBB)
443 return tbb::parallel_reduce(
444 tbb::blocked_range<IndexType>(beginIndex, endIndex), identity,
445 [&function](const tbb::blocked_range<IndexType>& range,
446 const Value& init) {
447 return function(range.begin(), range.end(), init);
448 },
449 reduce);
450#else
451 // Estimate number of threads in the pool
452 const unsigned int numThreadsHint = GetMaxNumberOfThreads();
453 const unsigned int numThreads =
454 (numThreadsHint == 0u) ? 8u : numThreadsHint;
455
456 // Size of a slice for the range functions
458 IndexType slice = static_cast<IndexType>(
459 std::round(n / static_cast<double>(numThreads)));
460 slice = (std::max)(slice, IndexType(1));
461
462 // Results
463 std::vector<Value> results(numThreads, identity);
464
465 // [Helper] Inner loop
466 auto launchRange = [&results, &function, &identity](
467 IndexType k1, IndexType k2, unsigned int tid) {
469 };
470
471 // Create pool and launch jobs
472 std::vector<CubbyFlow::Internal::future<void>> pool;
473 pool.reserve(numThreads);
474
476 IndexType i2 = (std::min)(beginIndex + slice, endIndex);
477 unsigned int threadID = 0;
478
479 for (; threadID + 1 < numThreads && i1 < endIndex; ++threadID)
480 {
481 pool.emplace_back(
484 }));
485
486 i1 = i2;
487 i2 = (std::min)(i2 + slice, endIndex);
488 }
489
490 if (i1 < endIndex)
491 {
492 pool.emplace_back(
495 }));
496 }
497
498 // Wait for jobs to finish
499 for (auto& f : pool)
500 {
501 if (f.valid())
502 {
503 f.wait();
504 }
505 }
506
507 // Gather
509 for (const Value& val : results)
510 {
512 }
513
514 return finalResult;
515#endif
516 }
517
518 (void)reduce;
520}
521
522#ifdef __CUDACC__
523template <typename RandomIterator>
524#else
525template <std::random_access_iterator RandomIterator>
526#endif
529{
531 begin, end,
532 std::less<typename std::iterator_traits<RandomIterator>::value_type>(),
533 policy);
534}
535
536#ifdef __CUDACC__
537template <typename RandomIterator, typename CompareFunction>
538#else
539template <std::random_access_iterator RandomIterator, typename CompareFunction>
540#endif
543{
544 if (begin > end)
545 {
546 return;
547 }
548
550 {
551#if defined(CUBBYFLOW_TASKING_HPX)
552 hpx::parallel::sort(hpx::parallel::execution::par, begin, end,
554#elif defined(CUBBYFLOW_TASKING_TBB)
555 tbb::parallel_sort(begin, end, compareFunction);
556#else
557
558 size_t size = static_cast<size_t>(end - begin);
559
560 using value_type =
561 typename std::iterator_traits<RandomIterator>::value_type;
562 std::vector<value_type> temp(size);
563
564 // Estimate number of threads in the pool
565 const unsigned int numThreadsHint = GetMaxNumberOfThreads();
566 const unsigned int numThreads =
567 (numThreadsHint == 0u) ? 8u : numThreadsHint;
568
571#endif
572 }
573 else
574 {
575 std::sort(begin, end, compareFunction);
576 }
577}
578} // namespace CubbyFlow
579
580#endif
Definition Matrix.hpp:30
Iterator begin()
Definition Matrix-Impl.hpp:272
Iterator end()
Definition Matrix-Impl.hpp:285
std::future< Task > future
Definition Parallel-Impl.hpp:49
void ParallelMergeSort(RandomIterator a, size_t size, RandomIterator2 temp, unsigned int numThreads, CompareFunction compareFunction)
Definition Parallel-Impl.hpp:138
typename std::invoke_result_t< TASK > operator_return_t
Definition Parallel-Impl.hpp:53
auto Async(TASK &&fn) -> future< operator_return_t< TASK > >
Definition Parallel-Impl.hpp:56
void Merge(RandomIterator a, size_t size, RandomIterator2 temp, CompareFunction compareFunction)
Definition Parallel-Impl.hpp:95
Definition pybind11Utils.hpp:22
constexpr size_t ZERO_SIZE
Zero size_t.
Definition Constants.hpp:20
void ParallelSort(RandomIterator begin, RandomIterator end, ExecutionPolicy policy)
Sorts a container in parallel.
Definition Parallel-Impl.hpp:527
void ParallelFill(const RandomIterator &begin, const RandomIterator &end, const T &value, ExecutionPolicy policy)
Fills from begin to end with value in parallel.
Definition Parallel-Impl.hpp:182
void ParallelFor(IndexType beginIndex, IndexType endIndex, const Function &function, ExecutionPolicy policy)
Makes a for-loop from beginIndex to endIndex in parallel.
Definition Parallel-Impl.hpp:203
Matrix< T, Rows, 1 > Vector
Definition Matrix.hpp:719
unsigned int GetMaxNumberOfThreads()
Returns maximum number of threads to use.
void ParallelRangeFor(IndexType beginIndex, IndexType endIndex, const Function &function, ExecutionPolicy policy)
Makes a range-loop from beginIndex to endIndex in parallel.
Definition Parallel-Impl.hpp:298
Value ParallelReduce(IndexType beginIndex, IndexType endIndex, const Value &identity, const Function &function, const Reduce &reduce, ExecutionPolicy policy)
Performs reduce operation in parallel.
Definition Parallel-Impl.hpp:431
ExecutionPolicy
Execution policy tag.
Definition Parallel.hpp:20