Improve memory usage of V3Partition. Only performance change intended. (#3192)

This commit is contained in:
Geza Lore 2021-11-06 02:08:54 +00:00 committed by GitHub
parent 61612582e6
commit e69a8e838d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -702,30 +702,53 @@ public:
} }
}; };
class SiblingMC;
class MTaskEdge;
// Information associated with scoreboarding an MTask // Information associated with scoreboarding an MTask
class MergeCandidate VL_NOT_FINAL { class MergeCandidate VL_NOT_FINAL {
private: private:
// Only the known subclasses can create or delete one of these
friend class SiblingMC;
friend class MTaskEdge;
// This structure is extremely hot. To save 8 bytes we pack // This structure is extremely hot. To save 8 bytes we pack
// one bit indicating removedFromSb with the id. // one bit indicating removedFromSb with the id. To save another
// By using bit zero, we can still use < to compare IDs without masking. // 8 bytes by not having a virtual function table, we implement the
vluint64_t m_id; // <63> removed, <62:0> Serial number for ordering // few polymorphic methods over the two known subclasses explicitly,
static constexpr vluint64_t REMOVED_MASK = 1ULL; // using another bit of the id to denote the actual subtype.
// By using the bottom bits for flags, we can still use < to compare IDs without masking.
vluint64_t m_id; // <63:2> Serial number for ordering, <1> subtype (SiblingMC), <0> removed
static constexpr vluint64_t REMOVED_MASK = 1ULL << 0;
static constexpr vluint64_t IS_SIBLING_MASK = 1ULL << 1;
static constexpr vluint64_t ID_INCREMENT = 1ULL << 2;
bool isSiblingMC() const { return m_id & IS_SIBLING_MASK; }
// CONSTRUCTORS
explicit MergeCandidate(bool isSiblingMC) {
static vluint64_t serial = 0;
serial += ID_INCREMENT; // +ID_INCREMENT so doesn't set the special bottom bits
m_id = serial | (isSiblingMC * IS_SIBLING_MASK);
}
~MergeCandidate() = default;
public: public:
// CONSTRUCTORS
MergeCandidate() {
static vluint64_t serial = 0;
serial += 2; // +2 so doesn't set REMOVED_MASK bit
m_id = serial;
}
virtual ~MergeCandidate() = default;
virtual bool mergeWouldCreateCycle() const = 0;
// METHODS // METHODS
SiblingMC* toSiblingMC(); // Instead of dynamic_cast
const SiblingMC* toSiblingMC() const; // Instead of dynamic_cast
MTaskEdge* toMTaskEdge(); // Instead of dynamic_cast
const MTaskEdge* toMTaskEdge() const; // Instead of dynamic_cast
bool mergeWouldCreateCycle() const; // Instead of virtual method
bool removedFromSb() const { return (m_id & REMOVED_MASK) != 0; } bool removedFromSb() const { return (m_id & REMOVED_MASK) != 0; }
void removedFromSb(bool removed) { m_id |= REMOVED_MASK; } void removedFromSb(bool removed) { m_id |= REMOVED_MASK; }
bool operator<(const MergeCandidate& other) const { return m_id < other.m_id; } bool operator<(const MergeCandidate& other) const { return m_id < other.m_id; }
}; };
static_assert(sizeof(MergeCandidate) == sizeof(vluint64_t), "Should not have a vtable");
// A pair of associated LogicMTask's that are merge candidates for sibling // A pair of associated LogicMTask's that are merge candidates for sibling
// contraction // contraction
class SiblingMC final : public MergeCandidate { class SiblingMC final : public MergeCandidate {
@ -736,7 +759,8 @@ private:
public: public:
// CONSTRUCTORS // CONSTRUCTORS
SiblingMC() = delete; SiblingMC() = delete;
SiblingMC(LogicMTask* ap, LogicMTask* bp) { SiblingMC(LogicMTask* ap, LogicMTask* bp)
: MergeCandidate{/* isSiblingMC: */ true} {
// Assign 'ap' and 'bp' in a canonical order, so we can more easily // Assign 'ap' and 'bp' in a canonical order, so we can more easily
// compare pairs of SiblingMCs // compare pairs of SiblingMCs
if (ap->id() > bp->id()) { if (ap->id() > bp->id()) {
@ -747,11 +771,11 @@ public:
m_bp = ap; m_bp = ap;
} }
} }
virtual ~SiblingMC() = default; ~SiblingMC() = default;
// METHODS // METHODS
LogicMTask* ap() const { return m_ap; } LogicMTask* ap() const { return m_ap; }
LogicMTask* bp() const { return m_bp; } LogicMTask* bp() const { return m_bp; }
bool mergeWouldCreateCycle() const override { bool mergeWouldCreateCycle() const {
return (LogicMTask::pathExistsFrom(m_ap, m_bp, nullptr) return (LogicMTask::pathExistsFrom(m_ap, m_bp, nullptr)
|| LogicMTask::pathExistsFrom(m_bp, m_ap, nullptr)); || LogicMTask::pathExistsFrom(m_bp, m_ap, nullptr));
} }
@ -762,12 +786,16 @@ public:
} }
}; };
static_assert(sizeof(SiblingMC) == sizeof(MergeCandidate) + 2 * sizeof(LogicMTask*),
"Should not have a vtable");
// GraphEdge for the MTask graph // GraphEdge for the MTask graph
class MTaskEdge final : public V3GraphEdge, public MergeCandidate { class MTaskEdge final : public V3GraphEdge, public MergeCandidate {
public: public:
// CONSTRUCTORS // CONSTRUCTORS
MTaskEdge(V3Graph* graphp, LogicMTask* fromp, LogicMTask* top, int weight) MTaskEdge(V3Graph* graphp, LogicMTask* fromp, LogicMTask* top, int weight)
: V3GraphEdge{graphp, fromp, top, weight} { : V3GraphEdge{graphp, fromp, top, weight}
, MergeCandidate{/* isSiblingMC: */ false} {
fromp->addRelative(GraphWay::FORWARD, top); fromp->addRelative(GraphWay::FORWARD, top);
top->addRelative(GraphWay::REVERSE, fromp); top->addRelative(GraphWay::REVERSE, fromp);
} }
@ -781,7 +809,7 @@ public:
} }
LogicMTask* fromMTaskp() const { return dynamic_cast<LogicMTask*>(fromp()); } LogicMTask* fromMTaskp() const { return dynamic_cast<LogicMTask*>(fromp()); }
LogicMTask* toMTaskp() const { return dynamic_cast<LogicMTask*>(top()); } LogicMTask* toMTaskp() const { return dynamic_cast<LogicMTask*>(top()); }
virtual bool mergeWouldCreateCycle() const override { bool mergeWouldCreateCycle() const {
return LogicMTask::pathExistsFrom(fromMTaskp(), toMTaskp(), this); return LogicMTask::pathExistsFrom(fromMTaskp(), toMTaskp(), this);
} }
static MTaskEdge* cast(V3GraphEdge* edgep) { static MTaskEdge* cast(V3GraphEdge* edgep) {
@ -806,6 +834,30 @@ private:
VL_UNCOPYABLE(MTaskEdge); VL_UNCOPYABLE(MTaskEdge);
}; };
// Instead of dynamic cast
SiblingMC* MergeCandidate::toSiblingMC() {
return isSiblingMC() ? static_cast<SiblingMC*>(this) : nullptr;
}
MTaskEdge* MergeCandidate::toMTaskEdge() {
return isSiblingMC() ? nullptr : static_cast<MTaskEdge*>(this);
}
const SiblingMC* MergeCandidate::toSiblingMC() const {
return isSiblingMC() ? static_cast<const SiblingMC*>(this) : nullptr;
}
const MTaskEdge* MergeCandidate::toMTaskEdge() const {
return isSiblingMC() ? nullptr : static_cast<const MTaskEdge*>(this);
}
// Normally this would be a virtual function, but we save space by not having a vtable,
// and we know we only have 2 possible subclasses.
bool MergeCandidate::mergeWouldCreateCycle() const {
return isSiblingMC() ? static_cast<const SiblingMC*>(this)->mergeWouldCreateCycle()
: static_cast<const MTaskEdge*>(this)->mergeWouldCreateCycle();
}
//###################################################################### //######################################################################
// Vertex utility classes // Vertex utility classes
@ -1271,13 +1323,13 @@ private:
void contract(MergeCandidate* mergeCanp) { void contract(MergeCandidate* mergeCanp) {
LogicMTask* top = nullptr; LogicMTask* top = nullptr;
LogicMTask* fromp = nullptr; LogicMTask* fromp = nullptr;
MTaskEdge* mergeEdgep = dynamic_cast<MTaskEdge*>(mergeCanp); MTaskEdge* mergeEdgep = mergeCanp->toMTaskEdge();
SiblingMC* mergeSibsp = nullptr; SiblingMC* mergeSibsp = nullptr;
if (mergeEdgep) { if (mergeEdgep) {
top = dynamic_cast<LogicMTask*>(mergeEdgep->top()); top = dynamic_cast<LogicMTask*>(mergeEdgep->top());
fromp = dynamic_cast<LogicMTask*>(mergeEdgep->fromp()); fromp = dynamic_cast<LogicMTask*>(mergeEdgep->fromp());
} else { } else {
mergeSibsp = dynamic_cast<SiblingMC*>(mergeCanp); mergeSibsp = mergeCanp->toSiblingMC();
UASSERT(mergeSibsp, "Failed to cast mergeCanp to either MTaskEdge or SiblingMC"); UASSERT(mergeSibsp, "Failed to cast mergeCanp to either MTaskEdge or SiblingMC");
top = mergeSibsp->ap(); top = mergeSibsp->ap();
fromp = mergeSibsp->bp(); fromp = mergeSibsp->bp();
@ -1413,14 +1465,13 @@ private:
} }
static uint32_t mergeCandidateScore(const MergeCandidate* pairp) { static uint32_t mergeCandidateScore(const MergeCandidate* pairp) {
if (const MTaskEdge* const edgep = dynamic_cast<const MTaskEdge*>(pairp)) { if (const MTaskEdge* const edgep = pairp->toMTaskEdge()) {
// The '1 +' favors merging a SiblingMC over an otherwise- // The '1 +' favors merging a SiblingMC over an otherwise-
// equal-scoring MTaskEdge. The comment on selfTest() talks // equal-scoring MTaskEdge. The comment on selfTest() talks
// about why. // about why.
return 1 + edgeScore(edgep); return 1 + edgeScore(edgep);
} } else {
if (const SiblingMC* const sibsp = dynamic_cast<const SiblingMC*>(pairp)) { return siblingScore(pairp->toSiblingMC());
return siblingScore(sibsp);
} }
v3fatalSrc("Failed to cast pairp to either MTaskEdge or SiblingMC in mergeCandidateScore"); v3fatalSrc("Failed to cast pairp to either MTaskEdge or SiblingMC in mergeCandidateScore");
return 0; return 0;