Skip to content

Commit 536ebb9

Browse files
committed
Refactored TopoQueue for new update weight. Did some optimizations in the process.
1 parent 5f28164 commit 536ebb9

1 file changed

Lines changed: 133 additions & 70 deletions

File tree

include/react/common/TopoQueue.h

Lines changed: 133 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@
1919
/***************************************/ REACT_IMPL_BEGIN /**************************************/
2020

2121
template <typename T>
22-
struct NodeLevelHelper
22+
struct TopoLevelFunctor
2323
{
2424
int operator()(const T& x) const { return x.Level; }
2525
};
2626

2727
template <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*>
3636
template <typename T>
3737
class TopoQueue
3838
{
39+
private:
40+
struct Entry;
41+
3942
public:
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

7085
private:
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
///////////////////////////////////////////////////////////////////////////////////////////////////
86114
template <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

92120
template <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

98126
template
@@ -105,7 +133,7 @@ class WeightedRange
105133
public:
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

152181
private:
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
165194
template <typename T, uint grain_size>
166195
class ConcurrentTopoQueue
167196
{
197+
private:
198+
struct Entry;
199+
168200
public:
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

237282
private:
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

Comments
 (0)