OpenVDB  12.0.0
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 
27 namespace nanovdb {
28 
29 namespace 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
42 template <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
77 template <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
85 template <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
94 template <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
104 template <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
113 template <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
123 template <template<typename...> class ContainerT, typename... ArgT, typename T, typename FuncT, typename JoinT >
124 [[deprecated("Use nanovdb::util::reduce instead")]]
125 T 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
T reduce(RangeT range, const T &identity, const FuncT &func, const JoinT &join)
Definition: Reduce.h:43
Definition: Range.h:28
Definition: GridHandle.h:27
Custom Range class that is compatible with the tbb::blocked_range classes.
Definition: Range.h:31