Reuse MTaskEdge instances in MT scheduling

Instead of deleting then re-allocating MTaskEdge instances when merging
two MTasks, just redirect the edged of the donor MTask to the recipient
MTask. This is both faster as it avoids an allocation and a deletion,
together with one update of the sibling maps, and also makes the
algorithm more stable due to MergeCandidate IDs being stable and
allocated up front for all MTaskEdges, before any SiblingMCs are
allocated.

Perturbations in output are expected as the IDs used to break ties
between merge candidates with equal costs are not updated when
redirecting an edge (on purpose). The relinking of only one end of the
graph edges also perturbs the order in which they are enumerated, which
does change candidate opportunities when the number of edges is larger
than PART_SIBLING_EDGE_LIMIT. Confirmed output is identical when
IDs are updated and edges are updated to appear in their original order.
This commit is contained in:
Geza Lore 2022-08-15 20:28:10 +01:00
parent f0040c7b9a
commit cd50949a7e
3 changed files with 47 additions and 27 deletions

View File

@ -182,6 +182,14 @@ V3GraphEdge* V3GraphEdge::relinkFromp(V3GraphVertex* newFromp) {
return oldNxt;
}
V3GraphEdge* V3GraphEdge::relinkTop(V3GraphVertex* newTop) {
V3GraphEdge* oldNxt = inNextp();
m_ins.unlink(m_top->m_ins, this);
m_top = newTop;
inPushBack();
return oldNxt;
}
void V3GraphEdge::unlinkDelete() {
// Unlink from side
m_outs.unlink(m_fromp->m_outs, this);

View File

@ -320,6 +320,7 @@ public:
}
void unlinkDelete();
V3GraphEdge* relinkFromp(V3GraphVertex* newFromp);
V3GraphEdge* relinkTop(V3GraphVertex* newTop);
// ACCESSORS
int weight() const { return m_weight; }
void weight(int weight) { m_weight = weight; }

View File

@ -742,6 +742,7 @@ public:
bool removedFromSb() const { return (m_id & REMOVED_MASK) != 0; }
void removedFromSb(bool /*removed*/) { m_id |= REMOVED_MASK; }
void clearRemovedFromSb() { m_id &= ~REMOVED_MASK; }
bool operator<(const MergeCandidate& other) const { return m_id < other.m_id; }
};
@ -1014,13 +1015,6 @@ static void partCheckCriticalPaths(V3Graph* mtasksp) {
}
}
// Advance to nextp(way) and delete edge
static V3GraphEdge* partBlastEdgep(GraphWay way, V3GraphEdge* edgep) {
V3GraphEdge* const nextp = edgep->nextp(way);
VL_DO_DANGLING(edgep->unlinkDelete(), edgep);
return nextp;
}
// Merge edges from a LogicMtask.
//
// This code removes 'hasRelative' edges. When this occurs, mark it in need
@ -1054,31 +1048,48 @@ static V3GraphEdge* partBlastEdgep(GraphWay way, V3GraphEdge* edgep) {
//
// Another way of stating this: this code ensures that scores of
// non-transitive edges only ever increase.
static void partMergeEdgesFrom(V3Graph* mtasksp, LogicMTask* recipientp, LogicMTask* donorp,
V3Scoreboard<MergeCandidate, uint32_t>* sbp) {
static void partRedirectEdgesFrom(LogicMTask* recipientp, LogicMTask* donorp,
V3Scoreboard<MergeCandidate, uint32_t>* sbp) {
for (const auto& way : {GraphWay::FORWARD, GraphWay::REVERSE}) {
for (V3GraphEdge* edgep = donorp->beginp(way); edgep; edgep = partBlastEdgep(way, edgep)) {
const MTaskEdge* const tedgep = MTaskEdge::cast(edgep);
if (sbp && !tedgep->removedFromSb()) sbp->removeElem(tedgep);
// Existing edge; mark it in need of a rescore
if (recipientp->hasRelative(way, tedgep->furtherMTaskp(way))) {
for (V3GraphEdge *edgep = donorp->beginp(way), *nextp; edgep; edgep = nextp) {
nextp = edgep->nextp(way);
MTaskEdge* const tedgep = MTaskEdge::cast(edgep);
LogicMTask* const relativep = tedgep->furtherMTaskp(way);
if (recipientp->hasRelative(way, relativep)) {
// An edge already exists between recipient and relative of donor.
// Mark it in need of a rescore
if (sbp) {
const MTaskEdge* const existMTaskEdgep = MTaskEdge::cast(
recipientp->findConnectingEdgep(way, tedgep->furtherMTaskp(way)));
if (!tedgep->removedFromSb()) sbp->removeElem(tedgep);
const MTaskEdge* const existMTaskEdgep
= MTaskEdge::cast(recipientp->findConnectingEdgep(way, relativep));
UASSERT(existMTaskEdgep, "findConnectingEdge didn't find edge");
if (!existMTaskEdgep->removedFromSb()) {
sbp->hintScoreChanged(existMTaskEdgep);
}
}
VL_DO_DANGLING(edgep->unlinkDelete(), edgep);
} else {
// No existing edge into *this, make one.
const MTaskEdge* newEdgep;
// No existing edge between recipient and relative of donor.
// Redirect the edge from donor<->relative to recipient<->relative.
if (way == GraphWay::REVERSE) {
newEdgep = new MTaskEdge(mtasksp, tedgep->fromMTaskp(), recipientp, 1);
tedgep->relinkTop(recipientp);
relativep->removeRelative(GraphWay::FORWARD, donorp);
relativep->addRelative(GraphWay::FORWARD, recipientp);
recipientp->addRelative(GraphWay::REVERSE, relativep);
} else {
newEdgep = new MTaskEdge(mtasksp, recipientp, tedgep->toMTaskp(), 1);
tedgep->relinkFromp(recipientp);
relativep->removeRelative(GraphWay::REVERSE, donorp);
relativep->addRelative(GraphWay::REVERSE, recipientp);
recipientp->addRelative(GraphWay::FORWARD, relativep);
}
if (sbp) {
if (tedgep->removedFromSb()) {
tedgep->clearRemovedFromSb();
sbp->addElem(tedgep);
} else {
sbp->hintScoreChanged(tedgep);
}
}
if (sbp) sbp->addElem(newEdgep);
}
}
}
@ -1334,7 +1345,7 @@ private:
}
// Merge the smaller mtask into the larger mtask. If one of them
// is much larger, this will save time in partMergeEdgesFrom().
// is much larger, this will save time in partRedirectEdgesFrom().
// Assume the more costly mtask has more edges.
//
// [TODO: now that we have edge maps, we could count the edges
@ -1414,8 +1425,8 @@ private:
// to a bounded number.
removeSiblingMCsWith(recipientp);
// Merge all edges
partMergeEdgesFrom(m_mtasksp, recipientp, donorp, &m_sb);
// Redirect all edges
partRedirectEdgesFrom(recipientp, donorp, &m_sb);
// Delete the donorp mtask from the graph
VL_DO_CLEAR(donorp->unlinkDelete(m_mtasksp), donorp = nullptr);
@ -1855,7 +1866,7 @@ private:
++rankIt) {
// Find the largest node at this rank, merge into it. (If we
// happen to find a huge node, this saves time in
// partMergeEdgesFrom() versus merging into an arbitrary node.)
// partRedirectEdgesFrom() versus merging into an arbitrary node.)
LogicMTask* mergedp = nullptr;
for (LogicMTaskSet::iterator it = rankIt->second.begin(); it != rankIt->second.end();
++it) {
@ -1883,8 +1894,8 @@ private:
}
// Move all vertices from donorp to mergedp
mergedp->moveAllVerticesFrom(donorp);
// Move edges from donorp to recipientp
partMergeEdgesFrom(m_mtasksp, mergedp, donorp, nullptr);
// Redirect edges from donorp to recipientp
partRedirectEdgesFrom(mergedp, donorp, nullptr);
// Remove donorp from the graph
VL_DO_DANGLING(donorp->unlinkDelete(m_mtasksp), donorp);
++m_mergesDone;