Fix performance of V3Trace when many activity blocks (#5372).

This commit is contained in:
Wilson Snyder 2024-08-28 20:45:51 -04:00
parent f4fe89a8c4
commit fa32d10f39
3 changed files with 51 additions and 21 deletions

View File

@ -16,6 +16,7 @@ Verilator 5.029 devel
* Support unconstrained randomization for unions (#5395) (#5396). [Yilou Wang]
* Add method to check if there are VPI callbacks of the given type (#5399). [Kaleb Barrett]
* Improve Verilation thread pool (#5161). [Bartłomiej Chmiel, Antmicro Ltd.]
* Fix performance of V3Trace when many activity blocks (#5372). [Deniz Güzel]
* Fix REALCVT warning on integral timescale conversions (#5378). [Liam Braun]
* Fix dot fallback finding wrong symbols (#5394). [Arkadiusz Kozdra, Antmicro Ltd.]
* Fix infinite recursion due to recursive functions/tasks (#5398). [Krzysztof Bieganski, Antmicro Ltd.]

View File

@ -221,7 +221,7 @@ void V3Stats::statsStage(const string& name) {
void V3Stats::infoHeader(std::ofstream& os, const string& prefix) {
os << prefix << "Information:\n";
os << prefix << " " << V3Options::version() << '\n';
os << prefix << " Version: " << V3Options::version() << '\n';
os << prefix << " Arguments: " << v3Global.opt.allArgsString() << '\n';
os << prefix << " Build jobs: " << v3Global.opt.buildJobs() << '\n';
os << prefix << " Verilate jobs: " << v3Global.opt.verilateJobs() << '\n';

View File

@ -175,6 +175,7 @@ class TraceVisitor final : public VNVisitor {
AstScope* const m_topScopep = v3Global.rootp()->topScopep()->scopep(); // The top AstScope
AstCFunc* m_cfuncp = nullptr; // C function adding to graph
AstCFunc* m_regFuncp = nullptr; // Trace registration function
AstCFunc* m_actAllFuncp = nullptr; // Set all activity function
AstTraceDecl* m_tracep = nullptr; // Trace function adding to graph
AstVarScope* m_activityVscp = nullptr; // Activity variable
uint32_t m_activityNumber = 0; // Count of fields in activity variable
@ -187,8 +188,10 @@ class TraceVisitor final : public VNVisitor {
const uint32_t m_parallelism
= v3Global.opt.useTraceParallel() ? static_cast<uint32_t>(v3Global.opt.threads()) : 1;
VDouble0 m_statUniqSigs; // Statistic tracking
VDouble0 m_statSetters; // Statistic tracking
VDouble0 m_statSettersSlow; // Statistic tracking
VDouble0 m_statUniqCodes; // Statistic tracking
VDouble0 m_statUniqSigs; // Statistic tracking
// All activity numbers applying to a given trace
using ActCodeSet = std::set<uint32_t>;
@ -395,25 +398,32 @@ class TraceVisitor final : public VNVisitor {
return new AstArraySel(flp, new AstVarRef{flp, m_activityVscp, access}, acode);
}
void addActivitySetter(AstNode* insertp, uint32_t code) {
AstNode* newActivitySetter(AstNode* insertp, uint32_t code) {
++m_statSetters;
FileLine* const fl = insertp->fileline();
AstAssign* const setterp = new AstAssign{fl, selectActivity(fl, code, VAccess::WRITE),
new AstConst{fl, AstConst::BitTrue{}}};
if (AstStmtExpr* const stmtp = VN_CAST(insertp, StmtExpr)) {
stmtp->addNextHere(setterp);
} else if (AstCFunc* const funcp = VN_CAST(insertp, CFunc)) {
// If there are awaits, insert the setter after each await
if (funcp->isCoroutine() && funcp->stmtsp()) {
funcp->stmtsp()->foreachAndNext([&](AstCAwait* awaitp) {
AstNode* stmtp = awaitp->backp();
while (VN_IS(stmtp, NodeExpr)) stmtp = stmtp->backp();
stmtp->addNextHere(setterp->cloneTree(false));
});
return setterp;
}
AstNode* newActivityAll(AstNode* insertp) {
++m_statSettersSlow;
if (!m_actAllFuncp) {
FileLine* const flp = m_topScopep->fileline();
AstCFunc* const funcp = new AstCFunc{flp, "__Vm_traceActivitySetAll", m_topScopep};
funcp->slow(true);
funcp->isStatic(false);
funcp->isLoose(true);
m_topScopep->addBlocksp(funcp);
for (uint32_t code = 0; code < m_activityNumber; ++code) {
AstNode* const setterp = newActivitySetter(insertp, code);
funcp->addStmtsp(setterp);
}
funcp->addStmtsp(setterp);
} else {
insertp->v3fatalSrc("Bad trace activity vertex");
m_actAllFuncp = funcp;
}
AstCCall* const callp = new AstCCall{insertp->fileline(), m_actAllFuncp};
callp->dtypeSetVoid();
return callp->makeStmt();
}
void createActivityFlags() {
@ -441,14 +451,31 @@ class TraceVisitor final : public VNVisitor {
// Insert activity setters
for (const V3GraphVertex& vtx : m_graph.vertices()) {
if (const TraceActivityVertex* const vtxp = vtx.cast<const TraceActivityVertex>()) {
AstNode* setterp = nullptr;
if (vtxp->activitySlow()) {
setterp = newActivityAll(vtxp->insertp());
// Just set all flags in slow code as it should be rare.
// This will be rolled up into a loop by V3Reloop.
for (uint32_t code = 0; code < m_activityNumber; ++code) {
addActivitySetter(vtxp->insertp(), code);
}
} else if (!vtxp->activityAlways()) {
addActivitySetter(vtxp->insertp(), vtxp->activityCode());
setterp = newActivitySetter(vtxp->insertp(), vtxp->activityCode());
}
if (setterp) {
AstNode* const insertp = vtxp->insertp();
if (AstStmtExpr* const stmtp = VN_CAST(insertp, StmtExpr)) {
stmtp->addNextHere(setterp);
} else if (AstCFunc* const funcp = VN_CAST(insertp, CFunc)) {
// If there are awaits, insert the setter after each await
if (funcp->isCoroutine() && funcp->stmtsp()) {
funcp->stmtsp()->foreachAndNext([&](AstCAwait* awaitp) {
AstNode* stmtp = awaitp->backp();
while (VN_IS(stmtp, NodeExpr)) stmtp = stmtp->backp();
stmtp->addNextHere(setterp->cloneTree(false));
});
}
funcp->addStmtsp(setterp);
} else {
insertp->v3fatalSrc("Bad trace activity vertex");
}
}
}
}
@ -901,8 +928,10 @@ public:
iterate(nodep);
}
~TraceVisitor() override {
V3Stats::addStat("Tracing, Unique traced signals", m_statUniqSigs);
V3Stats::addStat("Tracing, Activity setters", m_statSetters);
V3Stats::addStat("Tracing, Activity slow blocks", m_statSettersSlow);
V3Stats::addStat("Tracing, Unique trace codes", m_statUniqCodes);
V3Stats::addStat("Tracing, Unique traced signals", m_statUniqSigs);
}
};