1313
1414namespace RcppParallel {
1515
16- struct TBBWorker
17- {
18- explicit TBBWorker (Worker& worker) : worker_(worker) {}
19-
20- void operator ()(const tbb::blocked_range<size_t >& r) const {
21- worker_ (r.begin (), r.end ());
22- }
16+ // This class is primarily used to implement type erasure. The goals here were:
17+ //
18+ // 1. Hide the tbb symbols / implementation details from client R packages.
19+ // That is, they should get the tools they need only via RcppParallel.
20+ //
21+ // 2. Do this in a way that preserves binary compatibility with pre-existing
22+ // classes that make use of parallelReduce().
23+ //
24+ // 3. Ensure that those packages, when re-compiled without source changes,
25+ // can still function as expected.
26+ //
27+ // The downside here is that all the indirection through std::function<>
28+ // and the requirement for RTTI is probably expensive, but I couldn't find
29+ // a better way forward that could also preserve binary compatibility with
30+ // existing pre-built pacakges.
31+ //
32+ // Hopefully, in a future release, we can do away with this wrapper, once
33+ // packages have been rebuilt and no longer implicitly depend on TBB internals.
34+ struct ReducerWrapper {
2335
24- private:
25- Worker& worker_;
26- };
27-
28- template <typename Reducer>
29- struct TBBReducer
30- {
31- explicit TBBReducer (Reducer& reducer)
32- : pSplitReducer_(NULL ), reducer_(reducer)
33- {
34- }
35-
36- TBBReducer (TBBReducer& tbbReducer, tbb::split)
37- : pSplitReducer_(new Reducer(tbbReducer.reducer_, RcppParallel::Split())),
38- reducer_ (*pSplitReducer_)
36+ template <typename T>
37+ ReducerWrapper (T* reducer)
3938 {
40- }
41-
42- virtual ~TBBReducer () { delete pSplitReducer_; }
39+ self_ = reinterpret_cast <void *>(reducer);
40+ owned_ = false ;
4341
44- void operator ()(const tbb::blocked_range<size_t >& r) {
45- reducer_ (r.begin (), r.end ());
46- }
47-
48- void join (const TBBReducer& tbbReducer) {
49- reducer_.join (tbbReducer.reducer_ );
42+ work_ = [&](void * self, std::size_t begin, std::size_t end)
43+ {
44+ (*reinterpret_cast <T*>(self))(begin, end);
45+ };
46+
47+ split_ = [&](void * object, Split split)
48+ {
49+ return new T (*reinterpret_cast <T*>(object), split);
50+ };
51+
52+ join_ = [&](void * self, void * other)
53+ {
54+ (*reinterpret_cast <T*>(self)).join (*reinterpret_cast <T*>(other));
55+ };
56+
57+ deleter_ = [&](void * object)
58+ {
59+ delete (T*) object;
60+ };
5061 }
51-
52- private:
53- Reducer* pSplitReducer_;
54- Reducer& reducer_;
55- };
5662
57- template <typename Reducer>
58- class TBBParallelReduceExecutor
59- {
60- public:
61-
62- TBBParallelReduceExecutor (Reducer& reducer,
63- std::size_t begin,
64- std::size_t end,
65- std::size_t grainSize)
66- : reducer_(reducer),
67- begin_ (begin),
68- end_(end),
69- grainSize_(grainSize)
63+ ~ReducerWrapper ()
7064 {
65+ if (owned_)
66+ {
67+ deleter_ (self_);
68+ self_ = nullptr ;
69+ }
7170 }
72-
73- void operator ()() const
71+
72+ void operator ()(std:: size_t begin, std:: size_t end ) const
7473 {
75- TBBReducer<Reducer> tbbReducer (reducer_);
76- tbb::parallel_reduce (
77- tbb::blocked_range<std::size_t >(begin_, end_, grainSize_),
78- tbbReducer
79- );
74+ work_ (self_, begin, end);
8075 }
81-
82- private:
83- Reducer& reducer_;
84- std::size_t begin_;
85- std::size_t end_;
86- std::size_t grainSize_;
87- };
8876
89- template <typename Reducer>
90- class TBBArenaParallelReduceExecutor
91- {
92- public:
93-
94- TBBArenaParallelReduceExecutor (tbb::task_group& group,
95- Reducer& reducer,
96- std::size_t begin,
97- std::size_t end,
98- std::size_t grainSize)
99- : group_(group),
100- reducer_ (reducer),
101- begin_(begin),
102- end_(end),
103- grainSize_(grainSize)
77+ ReducerWrapper (const ReducerWrapper& rhs, Split split)
10478 {
79+ self_ = rhs.split_ (rhs.self_ , split);
80+ owned_ = true ;
81+
82+ work_ = rhs.work_ ;
83+ split_ = rhs.split_ ;
84+ join_ = rhs.join_ ;
85+ deleter_ = rhs.deleter_ ;
10586 }
106-
107- void operator ()( ) const
87+
88+ void join ( const ReducerWrapper& rhs ) const
10889 {
109- TBBParallelReduceExecutor<Reducer> executor (reducer_, begin_, end_, grainSize_);
110- group_.run_and_wait (executor);
90+ join_ (self_, rhs.self_ );
11191 }
112-
92+
11393private:
114-
115- tbb::task_group& group_;
116- Reducer& reducer_;
117- std::size_t begin_;
118- std::size_t end_;
119- std::size_t grainSize_;
94+ void * self_ = nullptr ;
95+ bool owned_ = false ;
96+
97+ std::function<void (void *, std::size_t , std::size_t )> work_;
98+ std::function<void *(void *, Split)> split_;
99+ std::function<void (void *, void *)> join_;
100+ std::function<void (void *)> deleter_;
120101};
121102
122103void tbbParallelFor (std::size_t begin,
@@ -125,20 +106,21 @@ void tbbParallelFor(std::size_t begin,
125106 std::size_t grainSize = 1 ,
126107 int numThreads = -1 );
127108
109+ void tbbParallelReduceImpl (std::size_t begin,
110+ std::size_t end,
111+ ReducerWrapper& wrapper,
112+ std::size_t grainSize = 1 ,
113+ int numThreads = -1 );
114+
128115template <typename Reducer>
129- inline void tbbParallelReduce (std::size_t begin,
130- std::size_t end,
131- Reducer& reducer,
132- std::size_t grainSize = 1 ,
133- int numThreads = -1 )
116+ void tbbParallelReduce (std::size_t begin,
117+ std::size_t end,
118+ Reducer& reducer,
119+ std::size_t grainSize = 1 ,
120+ int numThreads = -1 )
134121{
135- ThreadStackSizeControl control;
136-
137- tbb::task_group group;
138- TBBArenaParallelReduceExecutor<Reducer> executor (group, reducer, begin, end, grainSize);
139-
140- tbb::task_arena arena (numThreads);
141- arena.execute (executor);
122+ ReducerWrapper wrapper (&reducer);
123+ tbbParallelReduceImpl (begin, end, wrapper, grainSize, numThreads);
142124}
143125
144126} // namespace RcppParallel
0 commit comments