Rewrite the PBQP graph data structure.

The new graph structure replaces the node and edge linked lists with vectors.
Free lists (well, free vectors) are used for fast insertion/deletion.

The ultimate aim is to make PBQP graphs cheap to clone. The motivation is that
the PBQP solver destructively consumes input graphs while computing a solution,
forcing the graph to be fully reconstructed for each round of PBQP. This
imposes a high cost on large functions, which often require several rounds of
solving/spilling to find a final register allocation. If we can cheaply clone
the PBQP graph and incrementally update it between rounds then hopefully we can
reduce this cost. Further, once we begin pooling matrix/vector values (future
work), we can cache some PBQP solver metadata and share it between cloned
graphs, allowing the PBQP solver to re-use some of the computation done in
earlier rounds.

For now this is just a data structure update. The allocator and solver still
use the graph the same way as before, fully reconstructing it between each
round. I expect no material change from this update, although it may change
the iteration order of the nodes, causing ties in the solver to break in
different directions, and this could perturb the generated allocations
(hopefully in a completely benign way).

Thanks very much to Arnaud Allard de Grandmaison for encouraging me to get back
to work on this, and for a lot of discussion and many useful PBQP test cases.



git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@194300 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Lang Hames
2013-11-09 00:14:07 +00:00
parent 623d2e618f
commit fc93ae629e
7 changed files with 446 additions and 416 deletions

View File

@@ -47,8 +47,8 @@ namespace PBQP {
class LinkDegreeComparator {
public:
LinkDegreeComparator(HeuristicSolverImpl<Briggs> &s) : s(&s) {}
bool operator()(Graph::NodeItr n1Itr, Graph::NodeItr n2Itr) const {
if (s->getSolverDegree(n1Itr) > s->getSolverDegree(n2Itr))
bool operator()(Graph::NodeId n1Id, Graph::NodeId n2Id) const {
if (s->getSolverDegree(n1Id) > s->getSolverDegree(n2Id))
return true;
return false;
}
@@ -60,12 +60,12 @@ namespace PBQP {
public:
SpillCostComparator(HeuristicSolverImpl<Briggs> &s)
: s(&s), g(&s.getGraph()) {}
bool operator()(Graph::NodeItr n1Itr, Graph::NodeItr n2Itr) const {
const PBQP::Vector &cv1 = g->getNodeCosts(n1Itr);
const PBQP::Vector &cv2 = g->getNodeCosts(n2Itr);
bool operator()(Graph::NodeId n1Id, Graph::NodeId n2Id) const {
const PBQP::Vector &cv1 = g->getNodeCosts(n1Id);
const PBQP::Vector &cv2 = g->getNodeCosts(n2Id);
PBQPNum cost1 = cv1[0] / s->getSolverDegree(n1Itr);
PBQPNum cost2 = cv2[0] / s->getSolverDegree(n2Itr);
PBQPNum cost1 = cv1[0] / s->getSolverDegree(n1Id);
PBQPNum cost2 = cv2[0] / s->getSolverDegree(n2Id);
if (cost1 < cost2)
return true;
@@ -77,10 +77,10 @@ namespace PBQP {
Graph *g;
};
typedef std::list<Graph::NodeItr> RNAllocableList;
typedef std::list<Graph::NodeId> RNAllocableList;
typedef RNAllocableList::iterator RNAllocableListItr;
typedef std::list<Graph::NodeItr> RNUnallocableList;
typedef std::list<Graph::NodeId> RNUnallocableList;
typedef RNUnallocableList::iterator RNUnallocableListItr;
public:
@@ -123,8 +123,8 @@ namespace PBQP {
/// infinite are checked for allocability first. Allocable nodes may be
/// optimally reduced, but nodes whose allocability cannot be proven are
/// selected for heuristic reduction instead.
bool shouldOptimallyReduce(Graph::NodeItr nItr) {
if (getSolver().getSolverDegree(nItr) < 3) {
bool shouldOptimallyReduce(Graph::NodeId nId) {
if (getSolver().getSolverDegree(nId) < 3) {
return true;
}
// else
@@ -133,14 +133,14 @@ namespace PBQP {
/// \brief Add a node to the heuristic reduce list.
/// @param nItr Node iterator to add to the heuristic reduce list.
void addToHeuristicReduceList(Graph::NodeItr nItr) {
NodeData &nd = getHeuristicNodeData(nItr);
initializeNode(nItr);
void addToHeuristicReduceList(Graph::NodeId nId) {
NodeData &nd = getHeuristicNodeData(nId);
initializeNode(nId);
nd.isHeuristic = true;
if (nd.isAllocable) {
nd.rnaItr = rnAllocableList.insert(rnAllocableList.end(), nItr);
nd.rnaItr = rnAllocableList.insert(rnAllocableList.end(), nId);
} else {
nd.rnuItr = rnUnallocableList.insert(rnUnallocableList.end(), nItr);
nd.rnuItr = rnUnallocableList.insert(rnUnallocableList.end(), nId);
}
}
@@ -159,19 +159,19 @@ namespace PBQP {
RNAllocableListItr rnaItr =
min_element(rnAllocableList.begin(), rnAllocableList.end(),
LinkDegreeComparator(getSolver()));
Graph::NodeItr nItr = *rnaItr;
Graph::NodeId nId = *rnaItr;
rnAllocableList.erase(rnaItr);
handleRemoveNode(nItr);
getSolver().pushToStack(nItr);
handleRemoveNode(nId);
getSolver().pushToStack(nId);
return true;
} else if (!rnUnallocableList.empty()) {
RNUnallocableListItr rnuItr =
min_element(rnUnallocableList.begin(), rnUnallocableList.end(),
SpillCostComparator(getSolver()));
Graph::NodeItr nItr = *rnuItr;
Graph::NodeId nId = *rnuItr;
rnUnallocableList.erase(rnuItr);
handleRemoveNode(nItr);
getSolver().pushToStack(nItr);
handleRemoveNode(nId);
getSolver().pushToStack(nId);
return true;
}
// else
@@ -180,28 +180,28 @@ namespace PBQP {
/// \brief Prepare a change in the costs on the given edge.
/// @param eItr Edge iterator.
void preUpdateEdgeCosts(Graph::EdgeItr eItr) {
void preUpdateEdgeCosts(Graph::EdgeId eId) {
Graph &g = getGraph();
Graph::NodeItr n1Itr = g.getEdgeNode1(eItr),
n2Itr = g.getEdgeNode2(eItr);
NodeData &n1 = getHeuristicNodeData(n1Itr),
&n2 = getHeuristicNodeData(n2Itr);
Graph::NodeId n1Id = g.getEdgeNode1(eId),
n2Id = g.getEdgeNode2(eId);
NodeData &n1 = getHeuristicNodeData(n1Id),
&n2 = getHeuristicNodeData(n2Id);
if (n1.isHeuristic)
subtractEdgeContributions(eItr, getGraph().getEdgeNode1(eItr));
subtractEdgeContributions(eId, getGraph().getEdgeNode1(eId));
if (n2.isHeuristic)
subtractEdgeContributions(eItr, getGraph().getEdgeNode2(eItr));
subtractEdgeContributions(eId, getGraph().getEdgeNode2(eId));
EdgeData &ed = getHeuristicEdgeData(eItr);
EdgeData &ed = getHeuristicEdgeData(eId);
ed.isUpToDate = false;
}
/// \brief Handle the change in the costs on the given edge.
/// @param eItr Edge iterator.
void postUpdateEdgeCosts(Graph::EdgeItr eItr) {
void postUpdateEdgeCosts(Graph::EdgeId eId) {
// This is effectively the same as adding a new edge now, since
// we've factored out the costs of the old one.
handleAddEdge(eItr);
handleAddEdge(eId);
}
/// \brief Handle the addition of a new edge into the PBQP graph.
@@ -210,12 +210,12 @@ namespace PBQP {
/// Updates allocability of any nodes connected by this edge which are
/// being managed by the heuristic. If allocability changes they are
/// moved to the appropriate list.
void handleAddEdge(Graph::EdgeItr eItr) {
void handleAddEdge(Graph::EdgeId eId) {
Graph &g = getGraph();
Graph::NodeItr n1Itr = g.getEdgeNode1(eItr),
n2Itr = g.getEdgeNode2(eItr);
NodeData &n1 = getHeuristicNodeData(n1Itr),
&n2 = getHeuristicNodeData(n2Itr);
Graph::NodeId n1Id = g.getEdgeNode1(eId),
n2Id = g.getEdgeNode2(eId);
NodeData &n1 = getHeuristicNodeData(n1Id),
&n2 = getHeuristicNodeData(n2Id);
// If neither node is managed by the heuristic there's nothing to be
// done.
@@ -223,29 +223,29 @@ namespace PBQP {
return;
// Ok - we need to update at least one node.
computeEdgeContributions(eItr);
computeEdgeContributions(eId);
// Update node 1 if it's managed by the heuristic.
if (n1.isHeuristic) {
bool n1WasAllocable = n1.isAllocable;
addEdgeContributions(eItr, n1Itr);
updateAllocability(n1Itr);
addEdgeContributions(eId, n1Id);
updateAllocability(n1Id);
if (n1WasAllocable && !n1.isAllocable) {
rnAllocableList.erase(n1.rnaItr);
n1.rnuItr =
rnUnallocableList.insert(rnUnallocableList.end(), n1Itr);
rnUnallocableList.insert(rnUnallocableList.end(), n1Id);
}
}
// Likewise for node 2.
if (n2.isHeuristic) {
bool n2WasAllocable = n2.isAllocable;
addEdgeContributions(eItr, n2Itr);
updateAllocability(n2Itr);
addEdgeContributions(eId, n2Id);
updateAllocability(n2Id);
if (n2WasAllocable && !n2.isAllocable) {
rnAllocableList.erase(n2.rnaItr);
n2.rnuItr =
rnUnallocableList.insert(rnUnallocableList.end(), n2Itr);
rnUnallocableList.insert(rnUnallocableList.end(), n2Id);
}
}
}
@@ -256,27 +256,27 @@ namespace PBQP {
///
/// Updates allocability of the given node and, if appropriate, moves the
/// node to a new list.
void handleRemoveEdge(Graph::EdgeItr eItr, Graph::NodeItr nItr) {
NodeData &nd = getHeuristicNodeData(nItr);
void handleRemoveEdge(Graph::EdgeId eId, Graph::NodeId nId) {
NodeData &nd =getHeuristicNodeData(nId);
// If the node is not managed by the heuristic there's nothing to be
// done.
if (!nd.isHeuristic)
return;
EdgeData &ed = getHeuristicEdgeData(eItr);
EdgeData &ed = getHeuristicEdgeData(eId);
(void)ed;
assert(ed.isUpToDate && "Edge data is not up to date.");
// Update node.
bool ndWasAllocable = nd.isAllocable;
subtractEdgeContributions(eItr, nItr);
updateAllocability(nItr);
subtractEdgeContributions(eId, nId);
updateAllocability(nId);
// If the node has gone optimal...
if (shouldOptimallyReduce(nItr)) {
if (shouldOptimallyReduce(nId)) {
nd.isHeuristic = false;
addToOptimalReduceList(nItr);
addToOptimalReduceList(nId);
if (ndWasAllocable) {
rnAllocableList.erase(nd.rnaItr);
} else {
@@ -287,30 +287,30 @@ namespace PBQP {
// from "unallocable" to "allocable".
if (!ndWasAllocable && nd.isAllocable) {
rnUnallocableList.erase(nd.rnuItr);
nd.rnaItr = rnAllocableList.insert(rnAllocableList.end(), nItr);
nd.rnaItr = rnAllocableList.insert(rnAllocableList.end(), nId);
}
}
}
private:
NodeData& getHeuristicNodeData(Graph::NodeItr nItr) {
return getSolver().getHeuristicNodeData(nItr);
NodeData& getHeuristicNodeData(Graph::NodeId nId) {
return getSolver().getHeuristicNodeData(nId);
}
EdgeData& getHeuristicEdgeData(Graph::EdgeItr eItr) {
return getSolver().getHeuristicEdgeData(eItr);
EdgeData& getHeuristicEdgeData(Graph::EdgeId eId) {
return getSolver().getHeuristicEdgeData(eId);
}
// Work out what this edge will contribute to the allocability of the
// nodes connected to it.
void computeEdgeContributions(Graph::EdgeItr eItr) {
EdgeData &ed = getHeuristicEdgeData(eItr);
void computeEdgeContributions(Graph::EdgeId eId) {
EdgeData &ed = getHeuristicEdgeData(eId);
if (ed.isUpToDate)
return; // Edge data is already up to date.
Matrix &eCosts = getGraph().getEdgeCosts(eItr);
Matrix &eCosts = getGraph().getEdgeCosts(eId);
unsigned numRegs = eCosts.getRows() - 1,
numReverseRegs = eCosts.getCols() - 1;
@@ -352,15 +352,15 @@ namespace PBQP {
// numDenied and safe members. No action is taken other than to update
// these member values. Once updated these numbers can be used by clients
// to update the node's allocability.
void addEdgeContributions(Graph::EdgeItr eItr, Graph::NodeItr nItr) {
EdgeData &ed = getHeuristicEdgeData(eItr);
void addEdgeContributions(Graph::EdgeId eId, Graph::NodeId nId) {
EdgeData &ed = getHeuristicEdgeData(eId);
assert(ed.isUpToDate && "Using out-of-date edge numbers.");
NodeData &nd = getHeuristicNodeData(nItr);
unsigned numRegs = getGraph().getNodeCosts(nItr).getLength() - 1;
NodeData &nd = getHeuristicNodeData(nId);
unsigned numRegs = getGraph().getNodeCosts(nId).getLength() - 1;
bool nIsNode1 = nItr == getGraph().getEdgeNode1(eItr);
bool nIsNode1 = nId == getGraph().getEdgeNode1(eId);
EdgeData::UnsafeArray &unsafe =
nIsNode1 ? ed.unsafe : ed.reverseUnsafe;
nd.numDenied += nIsNode1 ? ed.worst : ed.reverseWorst;
@@ -379,15 +379,15 @@ namespace PBQP {
// numDenied and safe members. No action is taken other than to update
// these member values. Once updated these numbers can be used by clients
// to update the node's allocability.
void subtractEdgeContributions(Graph::EdgeItr eItr, Graph::NodeItr nItr) {
EdgeData &ed = getHeuristicEdgeData(eItr);
void subtractEdgeContributions(Graph::EdgeId eId, Graph::NodeId nId) {
EdgeData &ed = getHeuristicEdgeData(eId);
assert(ed.isUpToDate && "Using out-of-date edge numbers.");
NodeData &nd = getHeuristicNodeData(nItr);
unsigned numRegs = getGraph().getNodeCosts(nItr).getLength() - 1;
NodeData &nd = getHeuristicNodeData(nId);
unsigned numRegs = getGraph().getNodeCosts(nId).getLength() - 1;
bool nIsNode1 = nItr == getGraph().getEdgeNode1(eItr);
bool nIsNode1 = nId == getGraph().getEdgeNode1(eId);
EdgeData::UnsafeArray &unsafe =
nIsNode1 ? ed.unsafe : ed.reverseUnsafe;
nd.numDenied -= nIsNode1 ? ed.worst : ed.reverseWorst;
@@ -402,22 +402,22 @@ namespace PBQP {
}
}
void updateAllocability(Graph::NodeItr nItr) {
NodeData &nd = getHeuristicNodeData(nItr);
unsigned numRegs = getGraph().getNodeCosts(nItr).getLength() - 1;
void updateAllocability(Graph::NodeId nId) {
NodeData &nd = getHeuristicNodeData(nId);
unsigned numRegs = getGraph().getNodeCosts(nId).getLength() - 1;
nd.isAllocable = nd.numDenied < numRegs || nd.numSafe > 0;
}
void initializeNode(Graph::NodeItr nItr) {
NodeData &nd = getHeuristicNodeData(nItr);
void initializeNode(Graph::NodeId nId) {
NodeData &nd = getHeuristicNodeData(nId);
if (nd.isInitialized)
return; // Node data is already up to date.
unsigned numRegs = getGraph().getNodeCosts(nItr).getLength() - 1;
unsigned numRegs = getGraph().getNodeCosts(nId).getLength() - 1;
nd.numDenied = 0;
const Vector& nCosts = getGraph().getNodeCosts(nItr);
const Vector& nCosts = getGraph().getNodeCosts(nId);
for (unsigned i = 1; i < nCosts.getLength(); ++i) {
if (nCosts[i] == std::numeric_limits<PBQPNum>::infinity())
++nd.numDenied;
@@ -428,27 +428,27 @@ namespace PBQP {
typedef HeuristicSolverImpl<Briggs>::SolverEdgeItr SolverEdgeItr;
for (SolverEdgeItr aeItr = getSolver().solverEdgesBegin(nItr),
aeEnd = getSolver().solverEdgesEnd(nItr);
for (SolverEdgeItr aeItr = getSolver().solverEdgesBegin(nId),
aeEnd = getSolver().solverEdgesEnd(nId);
aeItr != aeEnd; ++aeItr) {
Graph::EdgeItr eItr = *aeItr;
computeEdgeContributions(eItr);
addEdgeContributions(eItr, nItr);
Graph::EdgeId eId = *aeItr;
computeEdgeContributions(eId);
addEdgeContributions(eId, nId);
}
updateAllocability(nItr);
updateAllocability(nId);
nd.isInitialized = true;
}
void handleRemoveNode(Graph::NodeItr xnItr) {
void handleRemoveNode(Graph::NodeId xnId) {
typedef HeuristicSolverImpl<Briggs>::SolverEdgeItr SolverEdgeItr;
std::vector<Graph::EdgeItr> edgesToRemove;
for (SolverEdgeItr aeItr = getSolver().solverEdgesBegin(xnItr),
aeEnd = getSolver().solverEdgesEnd(xnItr);
std::vector<Graph::EdgeId> edgesToRemove;
for (SolverEdgeItr aeItr = getSolver().solverEdgesBegin(xnId),
aeEnd = getSolver().solverEdgesEnd(xnId);
aeItr != aeEnd; ++aeItr) {
Graph::NodeItr ynItr = getGraph().getEdgeOtherNode(*aeItr, xnItr);
handleRemoveEdge(*aeItr, ynItr);
Graph::NodeId ynId = getGraph().getEdgeOtherNode(*aeItr, xnId);
handleRemoveEdge(*aeItr, ynId);
edgesToRemove.push_back(*aeItr);
}
while (!edgesToRemove.empty()) {