Internals: Move critical path propagators to MTaskGraph from Contraction
Code movement only prep for further work. No functional change, output should be identical.
This commit is contained in:
parent
d58f512a85
commit
b42d7c2d9e
|
|
@ -37,7 +37,6 @@
|
|||
#include <type_traits>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
|
|
@ -480,186 +479,6 @@ static void partCheckCriticalPaths(V3Graph& mTaskGraph) {
|
|||
}
|
||||
}
|
||||
|
||||
// ######################################################################
|
||||
// PropagateCp
|
||||
|
||||
template <GraphWay::en N_Way>
|
||||
class PropagateCp final {
|
||||
// Propagate increasing critical path (CP) costs through a graph.
|
||||
//
|
||||
// Usage:
|
||||
// * Client increases the cost and/or CP at a node or small set of nodes
|
||||
// (often a pair in practice, eg. edge contraction.)
|
||||
// * Client calls PropagateCp::cpHasIncreased() one or more times.
|
||||
// Each call indicates that the inclusive CP of some "seed" vertex
|
||||
// has increased to a given value.
|
||||
// * NOTE: PropagateCp will neither read nor modify the cost
|
||||
// or CPs at the seed vertices, it only accesses and modifies
|
||||
// vertices wayward from the seeds.
|
||||
// * Client calls PropagateCp::go(). Internally, this iteratively
|
||||
// propagates the new CPs wayward through the graph.
|
||||
//
|
||||
|
||||
// TYPES
|
||||
|
||||
// We keep pending vertices in a heap during critical path propagation
|
||||
struct PendingKey final {
|
||||
LogicMTask* m_mtaskp; // The vertex in the heap
|
||||
uint64_t m_score; // The score of this entry
|
||||
void increase(uint64_t score) {
|
||||
UDEBUGONLY(UASSERT(score >= m_score, "Must increase"););
|
||||
m_score = score;
|
||||
}
|
||||
bool operator<(const PendingKey& other) const {
|
||||
if (m_score != other.m_score) return m_score < other.m_score;
|
||||
return *m_mtaskp < *other.m_mtaskp;
|
||||
}
|
||||
};
|
||||
|
||||
using PendingHeap = PairingHeap<PendingKey>;
|
||||
using PendingHeapNode = typename PendingHeap::Node;
|
||||
|
||||
// MEMBERS
|
||||
PendingHeap m_pendingHeap; // Heap of pending rescores
|
||||
|
||||
// We allocate this many heap nodes at once
|
||||
static constexpr size_t ALLOC_CHUNK_SIZE = 128;
|
||||
PendingHeapNode* m_freep = nullptr; // List of free heap nodes
|
||||
std::vector<std::unique_ptr<PendingHeapNode[]>> m_allocated; // Allocated heap nodes
|
||||
|
||||
const bool m_slowAsserts; // Enable nontrivial asserts
|
||||
// Used only with slow asserts to check MTasks visited only once
|
||||
std::unordered_set<LogicMTask*> m_seen;
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit PropagateCp(bool slowAsserts)
|
||||
: m_slowAsserts{slowAsserts} {}
|
||||
|
||||
// METHODS
|
||||
private:
|
||||
// Allocate a HeapNode for the given element
|
||||
PendingHeapNode* allocNode() {
|
||||
// If no free nodes available, then make some
|
||||
if (!m_freep) {
|
||||
// Allocate in chunks for efficiency
|
||||
m_allocated.emplace_back(new PendingHeapNode[ALLOC_CHUNK_SIZE]);
|
||||
// Set up free list pointer
|
||||
m_freep = m_allocated.back().get();
|
||||
// Set up free list chain
|
||||
for (size_t i = 1; i < ALLOC_CHUNK_SIZE; ++i) {
|
||||
m_freep[i - 1].m_next.m_ptr = &m_freep[i];
|
||||
}
|
||||
// Clear the next pointer of the last entry
|
||||
m_freep[ALLOC_CHUNK_SIZE - 1].m_next.m_ptr = nullptr;
|
||||
}
|
||||
// Free nodes are available, pick up the first one
|
||||
PendingHeapNode* const resultp = m_freep;
|
||||
m_freep = resultp->m_next.m_ptr;
|
||||
resultp->m_next.m_ptr = nullptr;
|
||||
return resultp;
|
||||
}
|
||||
|
||||
// Release a heap node (make it available for future allocation)
|
||||
void freeNode(PendingHeapNode* nodep) {
|
||||
// Re-use the existing link pointers and simply prepend it to the free list
|
||||
nodep->m_next.m_ptr = m_freep;
|
||||
m_freep = nodep;
|
||||
}
|
||||
|
||||
public:
|
||||
void cpHasIncreased(V3GraphVertex* vxp, uint64_t newInclusiveCp) {
|
||||
constexpr GraphWay way{N_Way};
|
||||
constexpr GraphWay inv{way.invert()};
|
||||
|
||||
// For *vxp, whose CP-inclusive has just increased to
|
||||
// newInclusiveCp, iterate to all wayward nodes, update the edges
|
||||
// of each, and add each to m_pending if its overall CP has grown.
|
||||
for (V3GraphEdge& graphEdge : vxp->edges<way>()) {
|
||||
MTaskEdge& edge = static_cast<MTaskEdge&>(graphEdge);
|
||||
|
||||
LogicMTask* const relativep = edge.furtherMTaskp<N_Way>();
|
||||
EdgeHeap::Node& edgeHeapNode = edge.m_edgeHeapNode[inv];
|
||||
if (newInclusiveCp > edgeHeapNode.key().m_score) {
|
||||
relativep->m_edgeHeap[inv].increaseKey(&edgeHeapNode, newInclusiveCp);
|
||||
}
|
||||
|
||||
const uint64_t critPathCost = relativep->critPathCost(way);
|
||||
|
||||
if (critPathCost >= newInclusiveCp) continue;
|
||||
|
||||
// relativep's critPathCost() is out of step with its longest !wayward edge.
|
||||
// Schedule that to be resolved.
|
||||
const uint64_t newVal = newInclusiveCp - critPathCost;
|
||||
|
||||
void*& pendingNodepRef = relativep->m_propagateHeapNodep;
|
||||
if (PendingHeapNode* const nodep = static_cast<PendingHeapNode*>(pendingNodepRef)) {
|
||||
// Already in heap. Increase score if needed.
|
||||
if (newVal > nodep->key().m_score) m_pendingHeap.increaseKey(nodep, newVal);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add to heap
|
||||
PendingHeapNode* const nodep = allocNode();
|
||||
pendingNodepRef = nodep;
|
||||
m_pendingHeap.insert(nodep, {relativep, newVal});
|
||||
}
|
||||
}
|
||||
|
||||
void go() {
|
||||
constexpr GraphWay way{N_Way};
|
||||
constexpr GraphWay inv{way.invert()};
|
||||
|
||||
// m_pending maps each pending vertex to the amount that it wayward
|
||||
// CP will grow.
|
||||
//
|
||||
// We can iterate over the pending set in reverse order, always
|
||||
// choosing the nodes with the largest pending CP-growth.
|
||||
//
|
||||
// The intuition is: if the original seed node had its CP grow by
|
||||
// 50, the most any wayward node can possibly grow is also 50. So
|
||||
// for anything pending to grow by 50, we know we can process it
|
||||
// once and we won't have to grow its CP again on the current pass.
|
||||
// After we're done with all the grow-by-50s, nothing else will
|
||||
// grow by 50 again on the current pass, and we can process the
|
||||
// grow-by-49s and we know we'll only have to process each one
|
||||
// once. And so on.
|
||||
//
|
||||
// This generalizes to multiple seed nodes also.
|
||||
while (!m_pendingHeap.empty()) {
|
||||
// Pop max element from heap
|
||||
PendingHeapNode* const maxp = m_pendingHeap.max();
|
||||
m_pendingHeap.remove(maxp);
|
||||
// Pick up values
|
||||
LogicMTask* const mtaskp = maxp->key().m_mtaskp;
|
||||
const uint64_t cpGrowBy = maxp->key().m_score;
|
||||
// Free the heap node, we are done with it
|
||||
freeNode(maxp);
|
||||
mtaskp->m_propagateHeapNodep = nullptr;
|
||||
// Update the critPathCost of mtaskp, that was out-of-date with respect to its edges
|
||||
const uint64_t startCp = mtaskp->critPathCost(way);
|
||||
const uint64_t newCp = startCp + cpGrowBy;
|
||||
if (VL_UNLIKELY(m_slowAsserts)) {
|
||||
// Check that CP matches that of the longest edge wayward of vxp.
|
||||
const uint64_t edgeCp = mtaskp->m_edgeHeap[inv].max()->key().m_score;
|
||||
UASSERT_OBJ(edgeCp == newCp, mtaskp, "CP doesn't match longest wayward edge");
|
||||
// Confirm that we only set each node's CP once. That's an
|
||||
// important property of PropagateCp which allows it to be far
|
||||
// faster than a recursive algorithm on some graphs.
|
||||
const bool first = m_seen.insert(mtaskp).second;
|
||||
UASSERT_OBJ(first, mtaskp, "Set CP on node twice");
|
||||
}
|
||||
mtaskp->setCritPathCost(way, newCp);
|
||||
cpHasIncreased(mtaskp, newCp + mtaskp->cost());
|
||||
}
|
||||
|
||||
if (VL_UNLIKELY(m_slowAsserts)) m_seen.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
VL_UNCOPYABLE(PropagateCp);
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Contraction
|
||||
|
||||
|
|
@ -686,9 +505,6 @@ class Contraction final {
|
|||
// fixed for that lifetime: merging only ever deletes vertices, never creates them.
|
||||
std::unique_ptr<MTaskContractionData[]> m_mtaskDatap;
|
||||
|
||||
PropagateCp<GraphWay::FORWARD> m_forwardPropagator{m_slowAsserts}; // Forward propagator
|
||||
PropagateCp<GraphWay::REVERSE> m_reversePropagator{m_slowAsserts}; // Reverse propagator
|
||||
|
||||
// Singular source vertex of the OrderMTaskGraph
|
||||
LogicMTask* const m_entryMTaskp = m_mTaskGraph.entryp();
|
||||
// Singular sink vertex of the dependency graph
|
||||
|
|
@ -931,20 +747,22 @@ class Contraction final {
|
|||
|
||||
recipientp->setCritPathCost(GraphWay::FORWARD, recipientNewCpFwd.cp);
|
||||
if (recipientNewCpFwd.propagate) {
|
||||
m_forwardPropagator.cpHasIncreased(recipientp, recipientNewCpFwd.propagateCp);
|
||||
m_mTaskGraph.forwardPropagator().cpHasIncreased(recipientp,
|
||||
recipientNewCpFwd.propagateCp);
|
||||
}
|
||||
recipientp->setCritPathCost(GraphWay::REVERSE, recipientNewCpRev.cp);
|
||||
if (recipientNewCpRev.propagate) {
|
||||
m_reversePropagator.cpHasIncreased(recipientp, recipientNewCpRev.propagateCp);
|
||||
m_mTaskGraph.reversePropagator().cpHasIncreased(recipientp,
|
||||
recipientNewCpRev.propagateCp);
|
||||
}
|
||||
if (donorNewCpFwd.propagate) {
|
||||
m_forwardPropagator.cpHasIncreased(donorp, donorNewCpFwd.propagateCp);
|
||||
m_mTaskGraph.forwardPropagator().cpHasIncreased(donorp, donorNewCpFwd.propagateCp);
|
||||
}
|
||||
if (donorNewCpRev.propagate) {
|
||||
m_reversePropagator.cpHasIncreased(donorp, donorNewCpRev.propagateCp);
|
||||
m_mTaskGraph.reversePropagator().cpHasIncreased(donorp, donorNewCpRev.propagateCp);
|
||||
}
|
||||
m_forwardPropagator.go();
|
||||
m_reversePropagator.go();
|
||||
m_mTaskGraph.forwardPropagator().go();
|
||||
m_mTaskGraph.reversePropagator().go();
|
||||
|
||||
// Remove all other SiblingMCs that include recipientp or donorp. We remove all siblingMCs
|
||||
// of recipientp so we do not get huge numbers of SiblingMCs. We'll recreate them below, up
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include "V3OrderMTaskGraph.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3InstrCount.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
|
@ -28,7 +29,9 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
|||
OrderMTaskGraph::OrderMTaskGraph(OrderMoveGraph& moveGraph)
|
||||
: m_moveGraph{moveGraph}
|
||||
, m_entryp{new LogicMTask{*this, nullptr}}
|
||||
, m_exitp{new LogicMTask{*this, nullptr}} {}
|
||||
, m_exitp{new LogicMTask{*this, nullptr}}
|
||||
, m_forwardPropagator{v3Global.opt.debugPartition()}
|
||||
, m_reversePropagator{v3Global.opt.debugPartition()} {}
|
||||
|
||||
uint64_t OrderMTaskGraph::totalCost() const {
|
||||
uint64_t cost = 0;
|
||||
|
|
|
|||
|
|
@ -20,6 +20,10 @@
|
|||
// candidate machinery: any auxiliary data the algorithms need is attached
|
||||
// externally via the vertex/edge user pointers.
|
||||
//
|
||||
// PropagateCp propagates increasing critical path costs through the graph.
|
||||
// OrderMTaskGraph owns one instance for each direction, which the algorithms
|
||||
// operating on the graph use to keep the critical paths up to date.
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef VERILATOR_V3ORDERMTASKGRAPH_H_
|
||||
|
|
@ -37,43 +41,13 @@
|
|||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
class LogicMTask;
|
||||
class OrderMTaskGraph;
|
||||
template <GraphWay::en N_Way>
|
||||
class PropagateCp;
|
||||
|
||||
//=============================================================================
|
||||
// OrderMTaskGraph
|
||||
|
||||
// The graph of LogicMTask vertices and MTaskEdge edges, used during multi-threaded scheduling.
|
||||
class OrderMTaskGraph final : public V3Graph {
|
||||
OrderMoveGraph& m_moveGraph; // The OrderMoveGraph this graph is built from
|
||||
LogicMTask* const m_entryp; // The singular entry point vertex
|
||||
LogicMTask* const m_exitp; // The singular exit point vertex
|
||||
|
||||
// CONSTRUCTOR
|
||||
explicit OrderMTaskGraph(OrderMoveGraph& moveGraph); // Used by build(), hence private
|
||||
VL_UNCOPYABLE(OrderMTaskGraph);
|
||||
VL_UNMOVABLE(OrderMTaskGraph);
|
||||
|
||||
public:
|
||||
// ACCESSORS
|
||||
OrderMoveGraph& moveGraph() const { return m_moveGraph; }
|
||||
LogicMTask* entryp() const { return m_entryp; }
|
||||
LogicMTask* exitp() const { return m_exitp; }
|
||||
|
||||
// METHODS
|
||||
uint64_t totalCost() const; // O(V), called once
|
||||
|
||||
// STATIC METHODS
|
||||
// Build an MTask graph from 'moveGraph'
|
||||
static std::unique_ptr<OrderMTaskGraph> build(OrderMoveGraph& moveGraph) VL_MT_DISABLED;
|
||||
// Fix data hazards in the MTask graph
|
||||
static void fixDataHazards(OrderMTaskGraph& mtaskGraph) VL_MT_DISABLED;
|
||||
// Coarsen the MTask graph by merging MTasks until the given critical-path limit is reached
|
||||
static void contract(OrderMTaskGraph& mtaskGraph, uint64_t scoreLimit) VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// We keep MTaskEdge graph edges in a PairingHeap, sorted by score and id
|
||||
|
||||
|
|
@ -327,6 +301,225 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// PropagateCp
|
||||
|
||||
template <GraphWay::en N_Way>
|
||||
class PropagateCp final {
|
||||
// Propagate increasing critical path (CP) costs through a graph.
|
||||
//
|
||||
// Usage:
|
||||
// * Client increases the cost and/or CP at a node or small set of nodes
|
||||
// (often a pair in practice, eg. edge contraction.)
|
||||
// * Client calls PropagateCp::cpHasIncreased() one or more times.
|
||||
// Each call indicates that the inclusive CP of some "seed" vertex
|
||||
// has increased to a given value.
|
||||
// * NOTE: PropagateCp will neither read nor modify the cost
|
||||
// or CPs at the seed vertices, it only accesses and modifies
|
||||
// vertices wayward from the seeds.
|
||||
// * Client calls PropagateCp::go(). Internally, this iteratively
|
||||
// propagates the new CPs wayward through the graph.
|
||||
//
|
||||
|
||||
// TYPES
|
||||
|
||||
// We keep pending vertices in a heap during critical path propagation
|
||||
struct PendingKey final {
|
||||
LogicMTask* m_mtaskp; // The vertex in the heap
|
||||
uint64_t m_score; // The score of this entry
|
||||
void increase(uint64_t score) {
|
||||
UDEBUGONLY(UASSERT(score >= m_score, "Must increase"););
|
||||
m_score = score;
|
||||
}
|
||||
bool operator<(const PendingKey& other) const {
|
||||
if (m_score != other.m_score) return m_score < other.m_score;
|
||||
return *m_mtaskp < *other.m_mtaskp;
|
||||
}
|
||||
};
|
||||
|
||||
using PendingHeap = PairingHeap<PendingKey>;
|
||||
using PendingHeapNode = typename PendingHeap::Node;
|
||||
|
||||
// MEMBERS
|
||||
PendingHeap m_pendingHeap; // Heap of pending rescores
|
||||
|
||||
// We allocate this many heap nodes at once
|
||||
static constexpr size_t ALLOC_CHUNK_SIZE = 128;
|
||||
PendingHeapNode* m_freep = nullptr; // List of free heap nodes
|
||||
std::vector<std::unique_ptr<PendingHeapNode[]>> m_allocated; // Allocated heap nodes
|
||||
|
||||
const bool m_slowAsserts; // Enable nontrivial asserts
|
||||
// Used only with slow asserts to check MTasks visited only once
|
||||
std::unordered_set<LogicMTask*> m_seen;
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit PropagateCp(bool slowAsserts)
|
||||
: m_slowAsserts{slowAsserts} {}
|
||||
|
||||
// METHODS
|
||||
private:
|
||||
// Allocate a HeapNode for the given element
|
||||
PendingHeapNode* allocNode() {
|
||||
// If no free nodes available, then make some
|
||||
if (!m_freep) {
|
||||
// Allocate in chunks for efficiency
|
||||
m_allocated.emplace_back(new PendingHeapNode[ALLOC_CHUNK_SIZE]);
|
||||
// Set up free list pointer
|
||||
m_freep = m_allocated.back().get();
|
||||
// Set up free list chain
|
||||
for (size_t i = 1; i < ALLOC_CHUNK_SIZE; ++i) {
|
||||
m_freep[i - 1].m_next.m_ptr = &m_freep[i];
|
||||
}
|
||||
// Clear the next pointer of the last entry
|
||||
m_freep[ALLOC_CHUNK_SIZE - 1].m_next.m_ptr = nullptr;
|
||||
}
|
||||
// Free nodes are available, pick up the first one
|
||||
PendingHeapNode* const resultp = m_freep;
|
||||
m_freep = resultp->m_next.m_ptr;
|
||||
resultp->m_next.m_ptr = nullptr;
|
||||
return resultp;
|
||||
}
|
||||
|
||||
// Release a heap node (make it available for future allocation)
|
||||
void freeNode(PendingHeapNode* nodep) {
|
||||
// Re-use the existing link pointers and simply prepend it to the free list
|
||||
nodep->m_next.m_ptr = m_freep;
|
||||
m_freep = nodep;
|
||||
}
|
||||
|
||||
public:
|
||||
void cpHasIncreased(LogicMTask* vxp, uint64_t newInclusiveCp) {
|
||||
constexpr GraphWay way{N_Way};
|
||||
constexpr GraphWay inv{way.invert()};
|
||||
|
||||
// For *vxp, whose CP-inclusive has just increased to
|
||||
// newInclusiveCp, iterate to all wayward nodes, update the edges
|
||||
// of each, and add each to m_pending if its overall CP has grown.
|
||||
for (V3GraphEdge& graphEdge : vxp->edges<way>()) {
|
||||
MTaskEdge& edge = static_cast<MTaskEdge&>(graphEdge);
|
||||
|
||||
LogicMTask* const relativep = edge.furtherMTaskp<N_Way>();
|
||||
EdgeHeap::Node& edgeHeapNode = edge.m_edgeHeapNode[inv];
|
||||
if (newInclusiveCp > edgeHeapNode.key().m_score) {
|
||||
relativep->m_edgeHeap[inv].increaseKey(&edgeHeapNode, newInclusiveCp);
|
||||
}
|
||||
|
||||
const uint64_t critPathCost = relativep->critPathCost(way);
|
||||
|
||||
if (critPathCost >= newInclusiveCp) continue;
|
||||
|
||||
// relativep's critPathCost() is out of step with its longest !wayward edge.
|
||||
// Schedule that to be resolved.
|
||||
const uint64_t newVal = newInclusiveCp - critPathCost;
|
||||
|
||||
void*& pendingNodepRef = relativep->m_propagateHeapNodep;
|
||||
if (PendingHeapNode* const nodep = static_cast<PendingHeapNode*>(pendingNodepRef)) {
|
||||
// Already in heap. Increase score if needed.
|
||||
if (newVal > nodep->key().m_score) m_pendingHeap.increaseKey(nodep, newVal);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add to heap
|
||||
PendingHeapNode* const nodep = allocNode();
|
||||
pendingNodepRef = nodep;
|
||||
m_pendingHeap.insert(nodep, {relativep, newVal});
|
||||
}
|
||||
}
|
||||
|
||||
void go() {
|
||||
constexpr GraphWay way{N_Way};
|
||||
constexpr GraphWay inv{way.invert()};
|
||||
|
||||
// m_pending maps each pending vertex to the amount that it wayward
|
||||
// CP will grow.
|
||||
//
|
||||
// We can iterate over the pending set in reverse order, always
|
||||
// choosing the nodes with the largest pending CP-growth.
|
||||
//
|
||||
// The intuition is: if the original seed node had its CP grow by
|
||||
// 50, the most any wayward node can possibly grow is also 50. So
|
||||
// for anything pending to grow by 50, we know we can process it
|
||||
// once and we won't have to grow its CP again on the current pass.
|
||||
// After we're done with all the grow-by-50s, nothing else will
|
||||
// grow by 50 again on the current pass, and we can process the
|
||||
// grow-by-49s and we know we'll only have to process each one
|
||||
// once. And so on.
|
||||
//
|
||||
// This generalizes to multiple seed nodes also.
|
||||
while (!m_pendingHeap.empty()) {
|
||||
// Pop max element from heap
|
||||
PendingHeapNode* const maxp = m_pendingHeap.max();
|
||||
m_pendingHeap.remove(maxp);
|
||||
// Pick up values
|
||||
LogicMTask* const mtaskp = maxp->key().m_mtaskp;
|
||||
const uint64_t cpGrowBy = maxp->key().m_score;
|
||||
// Free the heap node, we are done with it
|
||||
freeNode(maxp);
|
||||
mtaskp->m_propagateHeapNodep = nullptr;
|
||||
// Update the critPathCost of mtaskp, that was out-of-date with respect to its edges
|
||||
const uint64_t startCp = mtaskp->critPathCost(way);
|
||||
const uint64_t newCp = startCp + cpGrowBy;
|
||||
if (VL_UNLIKELY(m_slowAsserts)) {
|
||||
// Check that CP matches that of the longest edge wayward of vxp.
|
||||
const uint64_t edgeCp = mtaskp->m_edgeHeap[inv].max()->key().m_score;
|
||||
UASSERT_OBJ(edgeCp == newCp, mtaskp, "CP doesn't match longest wayward edge");
|
||||
// Confirm that we only set each node's CP once. That's an
|
||||
// important property of PropagateCp which allows it to be far
|
||||
// faster than a recursive algorithm on some graphs.
|
||||
const bool first = m_seen.insert(mtaskp).second;
|
||||
UASSERT_OBJ(first, mtaskp, "Set CP on node twice");
|
||||
}
|
||||
mtaskp->setCritPathCost(way, newCp);
|
||||
cpHasIncreased(mtaskp, newCp + mtaskp->cost());
|
||||
}
|
||||
|
||||
if (VL_UNLIKELY(m_slowAsserts)) m_seen.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
VL_UNCOPYABLE(PropagateCp);
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// OrderMTaskGraph
|
||||
|
||||
// The graph of LogicMTask vertices and MTaskEdge edges, used during multi-threaded scheduling.
|
||||
class OrderMTaskGraph final : public V3Graph {
|
||||
OrderMoveGraph& m_moveGraph; // The OrderMoveGraph this graph is built from
|
||||
LogicMTask* const m_entryp; // The singular entry point vertex
|
||||
LogicMTask* const m_exitp; // The singular exit point vertex
|
||||
|
||||
// The critical path propagators, one for each direction. Owned here so the algorithms
|
||||
// operating on this graph (contraction, hazard fixing) share them.
|
||||
PropagateCp<GraphWay::FORWARD> m_forwardPropagator; // Forward propagator
|
||||
PropagateCp<GraphWay::REVERSE> m_reversePropagator; // Reverse propagator
|
||||
|
||||
// CONSTRUCTOR
|
||||
explicit OrderMTaskGraph(OrderMoveGraph& moveGraph); // Used by build(), hence private
|
||||
VL_UNCOPYABLE(OrderMTaskGraph);
|
||||
VL_UNMOVABLE(OrderMTaskGraph);
|
||||
|
||||
public:
|
||||
// ACCESSORS
|
||||
OrderMoveGraph& moveGraph() const { return m_moveGraph; }
|
||||
LogicMTask* entryp() const { return m_entryp; }
|
||||
LogicMTask* exitp() const { return m_exitp; }
|
||||
PropagateCp<GraphWay::FORWARD>& forwardPropagator() { return m_forwardPropagator; }
|
||||
PropagateCp<GraphWay::REVERSE>& reversePropagator() { return m_reversePropagator; }
|
||||
|
||||
// METHODS
|
||||
uint64_t totalCost() const; // O(V), called once
|
||||
|
||||
// STATIC METHODS
|
||||
// Build an MTask graph from 'moveGraph'
|
||||
static std::unique_ptr<OrderMTaskGraph> build(OrderMoveGraph& moveGraph) VL_MT_DISABLED;
|
||||
// Fix data hazards in the MTask graph
|
||||
static void fixDataHazards(OrderMTaskGraph& mtaskGraph) VL_MT_DISABLED;
|
||||
// Coarsen the MTask graph by merging MTasks until the given critical-path limit is reached
|
||||
static void contract(OrderMTaskGraph& mtaskGraph, uint64_t scoreLimit) VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// MTaskEdge method definitions (need the full definition of LogicMTask)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue