1919/* **************************************/ REACT_IMPL_BEGIN /* *************************************/
2020
2121template <typename T>
22- struct NodeLevelHelper
22+ struct TopoLevelFunctor
2323{
2424 int operator ()(const T& x) const { return x.Level ; }
2525};
2626
2727template <typename T>
28- struct NodeLevelHelper <T*>
28+ struct TopoLevelFunctor <T*>
2929{
3030 int operator ()(const T* x) const { return x->Level ; }
3131};
@@ -36,63 +36,91 @@ struct NodeLevelHelper<T*>
3636template <typename T>
3737class TopoQueue
3838{
39+ private:
40+ struct Entry ;
41+
3942public:
40- using DataT = std::vector<T>;
41- using LevelFunctorT = NodeLevelHelper<T>;
43+ // Store the level as part of the entry for cheap comparisons
44+ using QueueDataT = std::vector<Entry>;
45+ using NextDataT = std::vector<T>;
46+ using LevelFunctorT = TopoLevelFunctor<T>;
4247
43- void Push (const T& node )
48+ void Push (const T& value )
4449 {
45- data_. push_back (node );
50+ queueData_. emplace_back (value, LevelFunctorT{}(value) );
4651 }
4752
4853 bool FetchNext ()
4954 {
50- next_.clear ();
55+ // Throw away previous values
56+ nextData_.clear ();
5157
52- minLevel_ = std::numeric_limits<int >::max ();
53- for (const auto & e : data_)
54- {
55- auto l = LevelFunctorT{}(e);
56- if (minLevel_ > l)
57- minLevel_ = l;
58- }
58+ // Find min level of nodes in queue data
59+ minLevel_ = (std::numeric_limits<int >::max)();
60+ for (const auto & e : queueData_)
61+ if (minLevel_ > e.Level )
62+ minLevel_ = e.Level ;
5963
60- auto p = std::partition (data_.begin (), data_.end (), CompFunctor{ minLevel_ });
64+ // Swap entries with min level to the end
65+ auto p = std::partition (
66+ queueData_.begin (),
67+ queueData_.end (),
68+ LevelCompFunctor{ minLevel_ });
6169
62- next_. insert (next_. end (), p, data_. end ());
63- data_. resize (std::distance (data_. begin (), p ));
70+ // Reserve once to avoid multiple re-allocations
71+ nextData_. reserve (std::distance (p, queueData_. end () ));
6472
65- return !next_.empty ();
73+ // Move min level values to next data
74+ for (auto it = p; it != queueData_.end (); ++it)
75+ nextData_.push_back (std::move (it->Value ));
76+
77+ // Truncate moved entries
78+ queueData_.resize (std::distance (queueData_.begin (), p));
79+
80+ return !nextData_.empty ();
6681 }
6782
68- const DataT& NextNodes () const { return next_ ; }
83+ const NextDataT& NextValues () const { return nextData_ ; }
6984
7085private:
71- struct CompFunctor
86+ struct Entry
87+ {
88+ Entry () = default ;
89+ Entry (const Entry&) = default ;
90+
91+ Entry (const T& value, int level) : Value{ value }, Level{ level } {}
92+
93+ T Value;
94+ int Level;
95+ };
96+
97+ struct LevelCompFunctor
7298 {
73- CompFunctor (int level) : Level{ level } {}
74- bool operator ()(const T& x) { return LevelFunctorT{}(x) != Level; }
99+ LevelCompFunctor (int level) : Level{ level } {}
100+
101+ bool operator ()(const Entry& e) const { return e.Level != Level; }
102+
75103 const int Level;
76104 };
77105
78- DataT next_ ;
79- DataT data_ ;
80- int minLevel_ = std::numeric_limits<int >::max();
106+ NextDataT nextData_ ;
107+ QueueDataT queueData_ ;
108+ int minLevel_ = ( std::numeric_limits<int >::max) ();
81109};
82110
83111// /////////////////////////////////////////////////////////////////////////////////////////////////
84- // / WeightedRange
112+ // / WeightedRange - Implements tbb range concept
85113// /////////////////////////////////////////////////////////////////////////////////////////////////
86114template <typename T>
87- struct NodeWeightHelper
115+ struct RangeWeightFunctor
88116{
89- int operator ()(const T& x) const { return x.Weight ; }
117+ uint operator ()(const T& x) const { return x.IsHeavyweight () ? grain_size : 1 ; }
90118};
91119
92120template <typename T>
93- struct NodeWeightHelper <T*>
121+ struct RangeWeightFunctor <T*>
94122{
95- int operator ()(const T* x) const { return x->Weight ; }
123+ uint operator ()(const T* x) const { return x->IsHeavyweight () ; }
96124};
97125
98126template
@@ -105,7 +133,7 @@ class WeightedRange
105133public:
106134 using const_iterator = TIt;
107135 using ValueT = typename TIt::value_type;
108- using WeightFunctorT = NodeWeightHelper <ValueT>;
136+ using WeightFunctorT = RangeWeightFunctor <ValueT>;
109137
110138 WeightedRange () = default ;
111139 WeightedRange (const WeightedRange&) = default ;
@@ -122,7 +150,8 @@ class WeightedRange
122150 TIt p = source.begin_ ;
123151 while (p != source.end_ )
124152 {
125- sum += WeightFunctorT{}(*p);
153+ // Note: assuming a pair with weight as second until more flexibility is needed
154+ sum += p->second ;
126155 ++p;
127156 if (sum >= grain_size)
128157 break ;
@@ -150,9 +179,9 @@ class WeightedRange
150179 uint Weight () const { return weight_; }
151180
152181private:
153- TIt begin_;
154- TIt end_;
155- uint weight_ = 0 ;
182+ TIt begin_;
183+ TIt end_;
184+ uint weight_ = 0 ;
156185};
157186
158187// /////////////////////////////////////////////////////////////////////////////////////////////////
@@ -165,30 +194,39 @@ class WeightedRange
165194template <typename T, uint grain_size>
166195class ConcurrentTopoQueue
167196{
197+ private:
198+ struct Entry ;
199+
168200public:
169- using DataT = std::vector<T>;
170- using RangeT = WeightedRange<typename DataT::const_iterator, grain_size>;
171- using LevelFunctorT = NodeLevelHelper<T>;
172- using WeightFunctorT = NodeWeightHelper<T>;
201+ using QueueDataT = std::vector<Entry>;
202+ using NextDataT = std::vector<std::pair<T,uint>>;
203+ using NextRangeT = WeightedRange<typename NextDataT::const_iterator, grain_size>;
204+
205+ using LevelFunctorT = TopoLevelFunctor<T>;
206+ using WeightFunctorT = RangeWeightFunctor<T>;
173207
174- void Push (const T& node )
208+ void Push (const T& value )
175209 {
176210 auto & t = collectBuffer_.local ();
177-
178- t.Data .push_back (node);
179- t.Weight += WeightFunctorT{}(node);
180- auto l = LevelFunctorT{}(node);
181- if (t.MinLevel > l)
182- t.MinLevel = l;
211+
212+ auto level = LevelFunctorT{}(value);
213+ auto weight = WeightFunctorT{}(value);
214+
215+ t.Data .emplace_back (value,level,weight);
216+
217+ t.Weight += weight;
218+
219+ if (t.MinLevel > level)
220+ t.MinLevel = level;
183221 }
184222
185223 bool FetchNext ()
186224 {
187- nodes_ .clear ();
225+ nextData_ .clear ();
188226 uint totalWeight = 0 ;
189227
190228 // Determine current min level
191- minLevel_ = std::numeric_limits<int >::max ();
229+ minLevel_ = ( std::numeric_limits<int >::max) ();
192230 for (const auto & buf : collectBuffer_)
193231 if (minLevel_ > buf.MinLevel )
194232 minLevel_ = buf.MinLevel ;
@@ -199,59 +237,84 @@ class ConcurrentTopoQueue
199237 auto & v = buf.Data ;
200238
201239 // Swap min level nodes to end of v
202- auto p = std::partition (v.begin (), v.end (), CompFunctor{ minLevel_ });
240+ auto p = std::partition (
241+ v.begin (),
242+ v.end (),
243+ LevelCompFunctor{ minLevel_ });
244+
245+ // Reserve once to avoid multiple re-allocations
246+ nextData_.reserve (std::distance (p, v.end ()));
203247
204- // Copy them to global nodes_
205- std::copy (p, v.end (), std::back_inserter (nodes_));
248+ // Move min level values to global next data
249+ for (auto it = p; it != v.end (); ++it)
250+ nextData_.emplace_back (std::move (it->Value ), it->Weight );
206251
207252 // Truncate remaining
208253 v.resize (std::distance (v.begin (), p));
209254
210255 // Calc new min level and weight for this buffer
211- buf.MinLevel = std::numeric_limits<int >::max ();
256+ buf.MinLevel = ( std::numeric_limits<int >::max) ();
212257 int oldWeight = buf.Weight ;
213258 buf.Weight = 0 ;
214- for (const T & x : v)
259+ for (const auto & x : v)
215260 {
216- buf.Weight += WeightFunctorT{}(x) ;
217- auto l = LevelFunctorT{}(x);
218- if (buf.MinLevel > l )
219- buf.MinLevel = l ;
261+ buf.Weight += x. Weight ;
262+
263+ if (buf.MinLevel > x. Level )
264+ buf.MinLevel = x. Level ;
220265 }
221266
222267 // Add diff to nodes_ weight
223268 totalWeight += oldWeight - buf.Weight ;
224269 }
225270
226- range_ = RangeT (nodes_ .begin (), nodes_ .end (), totalWeight) ;
271+ nextRange_ = NextRangeT{ nextData_ .begin (), nextData_ .end (), totalWeight } ;
227272
228273 // Found more nodes?
229- return !nodes_ .empty ();
274+ return !nextData_ .empty ();
230275 }
231276
232- const RangeT & NextRange () const
277+ const NextRangeT & NextRange () const
233278 {
234- return range_ ;
279+ return nextRange_ ;
235280 }
236281
237282private:
238- struct CompFunctor
283+ struct Entry
284+ {
285+ Entry () = default ;
286+ Entry (const Entry&) = default ;
287+
288+ Entry (const T& value, int level, uint weight) :
289+ Value{ value },
290+ Level{ level },
291+ Weight{ weight }
292+ {}
293+
294+ T Value;
295+ int Level;
296+ uint Weight;
297+ };
298+
299+ struct LevelCompFunctor
239300 {
240- CompFunctor (int level) : Level{ level } {}
241- bool operator ()(const T& x) { return LevelFunctorT{}(x) != Level; }
301+ LevelCompFunctor (int level) : Level{ level } {}
302+
303+ bool operator ()(const Entry& e) const { return e.Level != Level; }
304+
242305 const int Level;
243306 };
244307
245308 struct ThreadLocalBuffer
246309 {
247- DataT Data;
248- int MinLevel = std::numeric_limits<int >::max();
249- uint Weight = 0 ;
310+ QueueDataT Data;
311+ int MinLevel = ( std::numeric_limits<int >::max) ();
312+ uint Weight = 0 ;
250313 };
251314
252- int minLevel_ = std::numeric_limits<int >::max();
253- DataT nodes_ ;
254- RangeT range_ ;
315+ int minLevel_ = ( std::numeric_limits<int >::max) ();
316+ NextDataT nextData_ ;
317+ NextRangeT nextRange_ ;
255318
256319 tbb::enumerable_thread_specific<ThreadLocalBuffer> collectBuffer_;
257320};
0 commit comments