OpenVDB: nanovdb/util/Reduce.h Source File

OpenVDB 13.0.0

Loading...
Searching...
No Matches
Reduce.h
Go to the documentation of this file.
1// Copyright Contributors to the OpenVDB Project
2// SPDX-License-Identifier: Apache-2.0
3
4/*!
5 \file nanovdb/util/Reduce.h
6
7 \author Ken Museth
8
9 \date March 4, 2021
10
11 \brief A unified wrapper for tbb::parallel_reduce and a naive std::future analog
12*/
13
14#ifndef NANOVDB_UTIL_REDUCE_H_HAS_BEEN_INCLUDED
15#define NANOVDB_UTIL_REDUCE_H_HAS_BEEN_INCLUDED
16
17#include <nanovdb/util/Range.h>// for util::Range1D
18
19#ifdef NANOVDB_USE_TBB
20#include <tbb/parallel_reduce.h>
21#else
22#include <thread>
23#include <future>
24#include <vector>
25#endif
26
27namespace nanovdb {
28
29namespace util {
30
31/// @return reduction
32///
33/// @param range RangeT can be Range<dim,T>, CoordBBox, tbb::blocked_range, blocked_range2D, or blocked_range3D.
34/// @param identity initial value
35/// @param func functor with signature T FuncT::operator()(const RangeT& range, const T& a) const
36/// @param join functor with the signature T JoinT::operator()(const T& a, const T& b) const
37/// @code
38/// std::vector<int> array(100, 1);
39/// auto func = [&array](auto &r, int a){for (auto i=r.begin(); i!=r.end(); ++i) a+=array[i]; return a;};
40/// int sum = reduce(array, 0, func, [](int a, int b){return a + b;});
41/// @endcode
42template <typename RangeT, typename T, typename FuncT, typename JoinT>
43 inline T reduce(RangeT range, const T& identity, const FuncT &func, const JoinT &join)
44{
45 if (range.empty()) return identity;
46#ifdef NANOVDB_USE_TBB
47 return tbb::parallel_reduce(range, identity, func, join);
48#else// naive and likely slow alternative based on std::future
49 if (const size_t threadCount = std::thread::hardware_concurrency()>>1) {
50 std::vector<RangeT> rangePool{ range };
51 while(rangePool.size() < threadCount) {
52 const size_t oldSize = rangePool.size();
53 for (size_t i = 0; i < oldSize && rangePool.size() < threadCount; ++i) {
54 auto &r = rangePool[i];
55 if (r.is_divisible()) rangePool.push_back(RangeT(r, Split()));
56 }
57 if (rangePool.size() == oldSize) break;// none of the ranges were divided so stop
58 }
59 std::vector< std::future<T> > futurePool;
60 for (auto &r : rangePool) {
61 auto task = std::async(std::launch::async, [&](){return func(r, identity);});
62 futurePool.push_back( std::move(task) );// launch tasks
63 }
64 T result = identity;
65 for (auto &f : futurePool) {
66 result = join(result, f.get());// join results
67 }
68 return result;
69 } else {// serial
70 return static_cast<T>(func(range, identity));
71 }
72#endif
73 return identity;// should never happen
74}
75
76/// @brief Simple wrapper to the function defined above
77template <typename T, typename FuncT, typename JoinT>
78 inline T reduce(size_t begin, size_t end, size_t grainSize, const T& identity, const FuncT& func, const JoinT& join)
79{
80 Range1D range(begin, end, grainSize);
81 return reduce( range, identity, func, join );
82}
83
84/// @brief Simple wrapper that works with std::containers
85template <template<typename...> class ContainerT, typename... ArgT, typename T, typename FuncT, typename JoinT >
86 inline T reduce(const ContainerT<ArgT...> &c, const T& identity, const FuncT& func, const JoinT& join)
87{
88 Range1D range(0, c.size(), 1);
89 return reduce( range, identity, func, join );
90
91}
92
93/// @brief Simple wrapper that works with std::containers
94template <template<typename...> class ContainerT, typename... ArgT, typename T, typename FuncT, typename JoinT >
95 inline T reduce(const ContainerT<ArgT...> &c, size_t grainSize, const T& identity, const FuncT& func, const JoinT& join)
96{
97 Range1D range(0, c.size(), grainSize);
98 return reduce( range, identity, func, join );
99}
100
101}// namespace util
102
103/// @brief Simple wrapper to the function defined above
104template <typename T, typename FuncT, typename JoinT>
105[[deprecated("Use nanovdb::util::reduce instead")]]
106 inline T reduce(size_t begin, size_t end, size_t grainSize, const T& identity, const FuncT& func, const JoinT& join)
107{
108 util::Range1D range(begin, end, grainSize);
109 return util::reduce( range, identity, func, join );
110}
111
112/// @brief Simple wrapper that works with std::containers
113template <template<typename...> class ContainerT, typename... ArgT, typename T, typename FuncT, typename JoinT >
114[[deprecated("Use nanovdb::util::reduce instead")]]
115 inline T reduce(const ContainerT<ArgT...> &c, const T& identity, const FuncT& func, const JoinT& join)
116{
117 util::Range1D range(0, c.size(), 1);
118 return util::reduce( range, identity, func, join );
119
120}
121
122/// @brief Simple wrapper that works with std::containers
123template <template<typename...> class ContainerT, typename... ArgT, typename T, typename FuncT, typename JoinT >
124[[deprecated("Use nanovdb::util::reduce instead")]]
125T reduce(const ContainerT<ArgT...> &c, size_t grainSize, const T& identity, const FuncT& func, const JoinT& join)
126{
127 util::Range1D range(0, c.size(), grainSize);
128 return util::reduce( range, identity, func, join );
129}
130
131}// namespace nanovdb
132
133#endif // NANOVDB_UTIL_REDUCE_H_HAS_BEEN_INCLUDED
Range.h
Custom Range class that is compatible with the tbb::blocked_range classes.
nanovdb::util::Split
Definition Range.h:28
nanovdb::util
Definition ForEach.h:29
nanovdb::util::reduce
T reduce(RangeT range, const T &identity, const FuncT &func, const JoinT &join)
Definition Reduce.h:43
nanovdb::util::Range1D
Range< 1, size_t > Range1D
Definition Range.h:33
nanovdb
Definition GridHandle.h:27
nanovdb::reduce
T reduce(size_t begin, size_t end, size_t grainSize, const T &identity, const FuncT &func, const JoinT &join)
Simple wrapper to the function defined above.
Definition Reduce.h:106