diff --git a/Changes b/Changes index 157188658..42944dd49 100644 --- a/Changes +++ b/Changes @@ -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.] diff --git a/src/V3StatsReport.cpp b/src/V3StatsReport.cpp index 8b83c501f..5aa2aa159 100644 --- a/src/V3StatsReport.cpp +++ b/src/V3StatsReport.cpp @@ -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'; diff --git a/src/V3Trace.cpp b/src/V3Trace.cpp index 9bd2e21ad..59a1e191b 100644 --- a/src/V3Trace.cpp +++ b/src/V3Trace.cpp @@ -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(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; @@ -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()) { + 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); } };