Skip to content

Commit e719129

Browse files
committed
Groups of small subtrees
1 parent d13d3df commit e719129

File tree

4 files changed

+90
-33
lines changed

4 files changed

+90
-33
lines changed

highs/ipm/hipo/factorhighs/Analyse.cpp

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1293,13 +1293,10 @@ void Analyse::computeBlockStart() {
12931293

12941294
void Analyse::findTreeSplitting() {
12951295
// Split the tree into single nodes and subtrees.
1296-
// The subtrees have at most 1% of total operations.
1296+
// The subtrees have at most 1% of total operations. They are group together
1297+
// so that each group of subtrees has between 1% and 2% of total operations.
12971298
// The tree is parallelised by creating a task for each single node and a task
1298-
// for each subtree.
1299-
1300-
// linked lists of children
1301-
std::vector<Int> head, next;
1302-
childrenLinkedList(sn_parent_, head, next);
1299+
// for each group of subtrees.
13031300

13041301
// compute number of operations for each supernode
13051302
std::vector<double> sn_ops(sn_count_);
@@ -1337,23 +1334,65 @@ void Analyse::findTreeSplitting() {
13371334
std::vector<Int> first_desc;
13381335
firstDescendant(sn_parent_, first_desc);
13391336

1337+
// linked lists of children
1338+
std::vector<Int> head, next;
1339+
childrenLinkedList(sn_parent_, head, next);
1340+
13401341
// Divide the tree into single nodes and subtrees, such that each subtree has
1341-
// at most small_thresh operations overall.
1342+
// at most small_thresh operations overall. Group subtrees together, so that
1343+
// groups have 1-2% operations.
13421344
const double small_thresh = kSmallSubtreesCoeff * total_ops;
13431345
for (Int sn = 0; sn < sn_count_; ++sn) {
13441346
if (subtree_ops[sn] > small_thresh) {
13451347
// sn is a single node
1346-
tree_splitting_.insert({sn, {NodeType::single, -1}});
1348+
auto res_insert = tree_splitting_.insert({sn, {}});
1349+
res_insert.first->second.type = NodeType::single;
1350+
1351+
// The children of this sn are either single nodes or head of subtrees.
1352+
// Divide the head of subtrees in groups, so that each group has 1% to 2%
1353+
// of total operations. Each group corresponds to one task executed in
1354+
// parallel.
13471355

1348-
} else if (sn_parent_[sn] == -1 ||
1349-
subtree_ops[sn_parent_[sn]] > small_thresh) {
1350-
// sn is head of a subtree
1351-
tree_splitting_.insert({sn, {NodeType::subtree, first_desc[sn]}});
1356+
double current_ops = 0.0;
1357+
NodeData* current_nodedata = nullptr;
1358+
Int child = head[sn];
1359+
while (child != -1) {
1360+
bool is_small = subtree_ops[child] <= small_thresh;
1361+
1362+
if (is_small) {
1363+
if (!current_nodedata) {
1364+
auto res_insert = tree_splitting_.insert({child, {}});
1365+
current_nodedata = &res_insert.first->second;
1366+
current_nodedata->type = NodeType::subtree;
1367+
current_ops = 0.0;
1368+
}
1369+
1370+
current_ops += subtree_ops[child];
1371+
current_nodedata->group.push_back(child);
1372+
current_nodedata->firstdesc.push_back(first_desc[child]);
1373+
1374+
if (current_ops > small_thresh) current_nodedata = nullptr;
1375+
}
13521376

1377+
child = next[child];
1378+
}
1379+
1380+
} else if (sn_parent_[sn] == -1) {
1381+
// sn is small root: single task with whole subtree
1382+
auto res_insert = tree_splitting_.insert({sn, {}});
1383+
res_insert.first->second.type = NodeType::subtree;
1384+
res_insert.first->second.group.push_back(sn);
1385+
res_insert.first->second.firstdesc.push_back(first_desc[sn]);
1386+
}
1387+
/*
1388+
else if (subtree_ops[sn_parent_[sn]] > small_thresh) {
1389+
// sn is head of a subtree, processed as part of a group of subtrees
1390+
continue;
13531391
} else {
13541392
// sn is part of a subtree, but not the head
13551393
continue;
13561394
}
1395+
*/
13571396
}
13581397
}
13591398

highs/ipm/hipo/factorhighs/Factorise.cpp

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -199,14 +199,14 @@ void Factorise::processSupernode(Int sn, bool parallelise) {
199199
// spawn children of this supernode in reverse order
200200
Int child_to_spawn = first_child_reverse_[sn];
201201
while (child_to_spawn != -1) {
202-
if (spawnNode(child_to_spawn, tg)) return;
202+
spawnNode(child_to_spawn, tg);
203203

204204
child_to_spawn = next_child_reverse_[child_to_spawn];
205205
}
206206

207207
// wait for first child to finish, before starting the parent (if there is a
208208
// first child)
209-
if (first_child_reverse_[sn] != -1) tg.sync();
209+
if (first_child_[sn] != -1) syncNode(first_child_[sn], tg);
210210
}
211211

212212
#if HIPO_TIMING_LEVEL >= 2
@@ -262,7 +262,7 @@ void Factorise::processSupernode(Int sn, bool parallelise) {
262262

263263
if (parallelise) {
264264
// sync with spawned child, apart from the first one
265-
if (child_sn != first_child_[sn]) tg.sync();
265+
if (child_sn != first_child_[sn]) syncNode(child_sn, tg);
266266

267267
if (flag_stop_) return;
268268

@@ -373,31 +373,42 @@ void Factorise::processSupernode(Int sn, bool parallelise) {
373373
#endif
374374
}
375375

376-
bool Factorise::spawnNode(Int sn, const TaskGroupSpecial& tg) {
376+
void Factorise::spawnNode(Int sn, const TaskGroupSpecial& tg) {
377377
auto it = S_.treeSplitting().find(sn);
378378

379379
if (it == S_.treeSplitting().end()) {
380-
log_->printDevInfo("Missing supernode from tree splitting\n");
381-
flag_stop_ = true;
382-
return true;
380+
// sn is head of small subtree, but not the first subtree in the group.
381+
// It will be processed in another task.
382+
return;
383383
}
384384

385385
if (it->second.type == NodeType::single) {
386-
// sn is single node, spawn only that
387-
tg.spawn([=]() { processSupernode(sn, true); });
386+
// sn is single node; spawn only that
387+
tg.spawn([this, sn]() { processSupernode(sn, true); });
388388

389389
} else {
390-
// sn is subtree, spawn the whole subtree
391-
Int start = it->second.first;
392-
Int end = sn + 1;
393-
tg.spawn([=]() {
394-
for (Int sn = start; sn < end; ++sn) {
395-
processSupernode(sn, false);
390+
// sn is head of the first subtree in a group of small subtrees; spawn all
391+
// of them
392+
393+
tg.spawn([this, it]() {
394+
for (Int i = 0; i < it->second.group.size(); ++i) {
395+
Int st_head = it->second.group[i];
396+
Int start = it->second.firstdesc[i];
397+
Int end = st_head + 1;
398+
for (Int sn = start; sn < end; ++sn) {
399+
processSupernode(sn, false);
400+
}
396401
}
397402
});
398403
}
404+
}
399405

400-
return false;
406+
void Factorise::syncNode(Int sn, const TaskGroupSpecial& tg) {
407+
// If spawnNode(sn,tg) created a task, then sync it.
408+
// This happens only if sn is found in the treeSplitting data structure.
409+
410+
auto it = S_.treeSplitting().find(sn);
411+
if (it != S_.treeSplitting().end()) tg.sync();
401412
}
402413

403414
bool Factorise::run(Numeric& num) {
@@ -421,9 +432,7 @@ bool Factorise::run(Numeric& num) {
421432
if (S_.parTree()) {
422433
// spawn tasks for root supernodes
423434
for (Int sn = 0; sn < S_.sn(); ++sn) {
424-
if (S_.snParent(sn) == -1) {
425-
if (spawnNode(sn, tg)) return true;
426-
}
435+
if (S_.snParent(sn) == -1) spawnNode(sn, tg);
427436
}
428437

429438
// sync tasks for root supernodes

highs/ipm/hipo/factorhighs/Factorise.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ class Factorise {
7575
public:
7676
void permute(const std::vector<Int>& iperm);
7777
void processSupernode(Int sn, bool parallelise);
78-
bool spawnNode(Int sn, const TaskGroupSpecial& tg);
78+
void spawnNode(Int sn, const TaskGroupSpecial& tg);
79+
void syncNode(Int sn, const TaskGroupSpecial& tg);
7980

8081
public:
8182
Factorise(const Symbolic& S, const std::vector<Int>& rowsA,

highs/ipm/hipo/factorhighs/Symbolic.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ namespace hipo {
1212
enum NodeType { single, subtree };
1313
struct NodeData {
1414
NodeType type;
15-
Int first;
15+
std::vector<Int> firstdesc;
16+
std::vector<Int> group;
1617
};
1718

1819
// Symbolic factorisation object
@@ -101,6 +102,13 @@ class Symbolic {
101102
// Starting position of diagonal blocks for hybrid formats
102103
std::vector<std::vector<Int>> clique_block_start_{};
103104

105+
// Information to split the elimination tree. Each entry in tree_splitting_
106+
// correspond to a task that is executed in parallel.
107+
// tree_splitting_ contains pairs (sn, data):
108+
// - If data.type is single, then the task processes only the supernode sn.
109+
// - If data.type is subtree, then the task processes each subtree rooted at
110+
// data.group[i]. Each subtree requires processing supernodes j,
111+
// data.firstdesc[i] <= j <= data.group[i].
104112
std::map<Int, NodeData> tree_splitting_;
105113

106114
friend class Analyse;

0 commit comments

Comments
 (0)