From 978d900e3636ebc1760588c51ff3eb4cce6894d2 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Sat, 21 Oct 2023 14:45:30 +0100 Subject: [PATCH] Simplify and fix code stats V3Stats for "fast" code have bit-rotted a little and is causing some problems with tests that rely on stats outputs. The problem is that not all code is necessarily reachable from eval() any more (due to the complexity of some the features added over the past few years), so it might miss some things, as for measuring the "fast" code, it is trying to trace the execution paths via calls, starting from eval(). It also appears the fast code can also contain calls to slow code in some circumstances. To avoid all that, removed trying to trace dynamic execution, and simply report the static node counts, which is enough for testing. Similarly, the variable counts are somewhat dubious, as they don't include all data types, or all instances of a module in some stages. Removing these as they are not widely used nor dependable. More specific stats can be added if required and can be well defined. --- src/V3Stats.cpp | 296 ++++++++++++++---------------------------------- src/V3Stats.h | 2 +- 2 files changed, 88 insertions(+), 210 deletions(-) diff --git a/src/V3Stats.cpp b/src/V3Stats.cpp index f79c8145a..03d823d8b 100644 --- a/src/V3Stats.cpp +++ b/src/V3Stats.cpp @@ -27,246 +27,124 @@ VL_DEFINE_DEBUG_FUNCTIONS; // Stats class functions class StatsVisitor final : public VNVisitorConst { -private: - // NODE STATE/TYPES - - using NameMap = std::map; // Number of times a name appears + struct Counters final { + // Nodes of given type + uint64_t m_statTypeCount[VNType::_ENUM_END]; + // Nodes of given type with given type immediate child + uint64_t m_statAbove[VNType::_ENUM_END][VNType::_ENUM_END]; + // Prediction of given type + uint64_t m_statPred[VBranchPred::_ENUM_END]; + }; // STATE - const string m_stage; // Name of the stage we are scanning - /// m_fast = true: Counting only critical branch of fastpath - /// m_fast = false: Counting every node, ignoring structure of program - const bool m_fast; - - const AstCFunc* m_cfuncp; // Current CFUNC - bool m_counting; // Currently counting - double m_instrs; // Current instr count (for determining branch direction) - bool m_tracingCall; // Iterating into a CCall to a CFunc - - std::vector m_statTypeCount; // Nodes of given type - VDouble0 m_statAbove[VNType::_ENUM_END][VNType::_ENUM_END]; // Nodes of given type - std::array m_statPred; // Nodes of given type - VDouble0 m_statInstr; // Instruction count - VDouble0 m_statInstrFast; // Instruction count, non-slow() eval functions only - std::vector m_statVarWidths; // Variables of given width - std::vector m_statVarWidthNames; // Var names of given width - VDouble0 m_statVarArray; // Statistic tracking - VDouble0 m_statVarBytes; // Statistic tracking - VDouble0 m_statVarClock; // Statistic tracking - VDouble0 m_statVarScpBytes; // Statistic tracking + const bool m_fastOnly; // When true, consider only fast functions + const AstNodeExpr* m_parentExprp = nullptr; // Parent expression + Counters m_counters; // The actual counts we will display + Counters m_dumpster; // Alternate buffer to make discarding parts of the tree easier + Counters* m_accump; // The currently active accumulator + std::vector m_statVarWidths; // Variables of given width + std::vector> + m_statVarWidthNames; // Var names of given width // METHODS - - void allNodes(AstNode* nodep) { - m_instrs += nodep->instrCount(); - if (m_counting) { - ++m_statTypeCount[nodep->type()]; - if (nodep->firstAbovep()) { // Grab only those above, not those "back" - ++m_statAbove[nodep->firstAbovep()->type()][nodep->type()]; - } - m_statInstr += nodep->instrCount(); - if (m_cfuncp && !m_cfuncp->slow()) m_statInstrFast += nodep->instrCount(); - } + void countThenIterateChildren(AstNode* nodep) { + ++m_accump->m_statTypeCount[nodep->type()]; + iterateChildrenConst(nodep); } // VISITORS - void visit(AstNodeModule* nodep) override { - allNodes(nodep); - if (!m_fast) { - // Count all CFuncs below this module - iterateChildrenConst(nodep); - } - // Else we recursively trace fast CFuncs from the top _eval - // func, see visit(AstNetlist*) - } void visit(AstVar* nodep) override { - allNodes(nodep); - iterateChildrenConst(nodep); - if (m_counting && nodep->dtypep()) { - if (nodep->isUsedClock()) ++m_statVarClock; - if (VN_IS(nodep->dtypeSkipRefp(), UnpackArrayDType)) { - ++m_statVarArray; - } else { - m_statVarBytes += nodep->dtypeSkipRefp()->widthTotalBytes(); - } - if (int(m_statVarWidths.size()) <= nodep->width()) { + if (nodep->dtypep()) { + if (m_statVarWidths.size() <= static_cast(nodep->width())) { m_statVarWidths.resize(nodep->width() + 5); - if (v3Global.opt.statsVars()) m_statVarWidthNames.resize(nodep->width() + 5); + if (v3Global.opt.statsVars()) { // + m_statVarWidthNames.resize(nodep->width() + 5); + } } ++m_statVarWidths.at(nodep->width()); - const string pn = nodep->prettyName(); if (v3Global.opt.statsVars()) { - NameMap& nameMapr = m_statVarWidthNames.at(nodep->width()); - if (nameMapr.find(pn) != nameMapr.end()) { - nameMapr[pn]++; - } else { - nameMapr[pn] = 1; - } + ++m_statVarWidthNames.at(nodep->width())[nodep->prettyName()]; } } - } - void visit(AstVarScope* nodep) override { - allNodes(nodep); - iterateChildrenConst(nodep); - if (m_counting) { - if (VN_IS(nodep->varp()->dtypeSkipRefp(), BasicDType)) { - m_statVarScpBytes += nodep->varp()->dtypeSkipRefp()->widthTotalBytes(); - } - } - } - void visit(AstNodeIf* nodep) override { - UINFO(4, " IF i=" << m_instrs << " " << nodep << endl); - allNodes(nodep); - // Condition is part of cost allocated to PREVIOUS block - iterateAndNextConstNull(nodep->condp()); - // Track prediction - if (m_counting) ++m_statPred[nodep->branchPred()]; - if (!m_fast) { - // Count everything - iterateChildrenConst(nodep); - } else { - // See which path we want to take - // Need to do even if !m_counting because maybe determining upstream if/else - double ifInstrs = 0.0; - double elseInstrs = 0.0; - if (!nodep->branchPred().unlikely()) { // Check if - VL_RESTORER(m_instrs); - VL_RESTORER(m_counting); - { - m_counting = false; - m_instrs = 0.0; - iterateAndNextConstNull(nodep->thensp()); - ifInstrs = m_instrs; - } - } - if (!nodep->branchPred().likely()) { // Check else - VL_RESTORER(m_instrs); - VL_RESTORER(m_counting); - { - m_counting = false; - m_instrs = 0.0; - iterateAndNextConstNull(nodep->elsesp()); - elseInstrs = m_instrs; - } - } - // Now collect the stats - if (m_counting) { - if (ifInstrs >= elseInstrs) { - iterateAndNextConstNull(nodep->thensp()); - } else { - iterateAndNextConstNull(nodep->elsesp()); - } - } - } - } - // While's we assume evaluate once. - // void visit(AstWhile* nodep) override { - void visit(AstNodeCCall* nodep) override { - allNodes(nodep); - iterateChildrenConst(nodep); - if (m_fast && !nodep->funcp()->entryPoint()) { - // Enter the function and trace it - m_tracingCall = true; - iterateConst(nodep->funcp()); - } + countThenIterateChildren(nodep); } + + void visit(AstNodeExpr* nodep) override { + // Count expression combinations + if (m_parentExprp) ++m_accump->m_statAbove[m_parentExprp->type()][nodep->type()]; + VL_RESTORER(m_parentExprp); + m_parentExprp = nodep; + countThenIterateChildren(nodep); + } + + void visit(AstNodeIf* nodep) override { + // Track prediction + ++m_accump->m_statPred[nodep->branchPred()]; + countThenIterateChildren(nodep); + } + void visit(AstCFunc* nodep) override { - if (m_fast) { - if (!m_tracingCall && !nodep->entryPoint()) return; - m_tracingCall = false; - } - VL_RESTORER(m_cfuncp); - { - m_cfuncp = nodep; - allNodes(nodep); - iterateChildrenConst(nodep); - } - } - void visit(AstNode* nodep) override { - allNodes(nodep); - iterateChildrenConst(nodep); - } - void visit(AstNetlist* nodep) override { - if (m_fast && nodep->evalp()) { - m_instrs = 0; - m_counting = true; - iterateChildrenConst(nodep->evalp()); - m_counting = false; - } - allNodes(nodep); - iterateChildrenConst(nodep); + VL_RESTORER(m_accump); + if (m_fastOnly && !nodep->slow()) m_accump = &m_counters; + countThenIterateChildren(nodep); } + void visit(AstNode* nodep) override { countThenIterateChildren(nodep); } + public: // CONSTRUCTORS - StatsVisitor(AstNetlist* nodep, const string& stage, bool fast) - : m_stage{stage} - , m_fast{fast} { - UINFO(9, "Starting stats, fast=" << fast << endl); - m_cfuncp = nullptr; - m_counting = !m_fast; - m_instrs = 0; - m_tracingCall = false; - // Initialize arrays - m_statTypeCount.resize(VNType::_ENUM_END); - // Process + StatsVisitor(AstNetlist* nodep, const std::string& stage, bool fastOnly) + : m_fastOnly{fastOnly} + , m_accump{fastOnly ? &m_dumpster : &m_counters} { + UINFO(9, "Starting stats, fastOnly=" << fastOnly << endl); + memset(&m_counters, 0, sizeof(m_counters)); + memset(&m_dumpster, 0, sizeof(m_dumpster)); + iterateConst(nodep); - } - ~StatsVisitor() override { - // Done. Publish statistics - V3Stats::addStat(m_stage, "Instruction count, TOTAL", m_statInstr); - V3Stats::addStat(m_stage, "Instruction count, fast critical", m_statInstrFast); - // Vars - V3Stats::addStat(m_stage, "Vars, unpacked arrayed", m_statVarArray); - V3Stats::addStat(m_stage, "Vars, clock attribute", m_statVarClock); - V3Stats::addStat(m_stage, "Var space, non-arrays, bytes", m_statVarBytes); - if (m_statVarScpBytes != 0.0) { - V3Stats::addStat(m_stage, "Var space, scoped, bytes", m_statVarScpBytes); - } + + // Shorthand + const auto addStat = [&](const std::string& name, double count) { // + V3Stats::addStat(stage, name, count); + }; + + // Variable widths for (unsigned i = 0; i < m_statVarWidths.size(); i++) { - const double count{m_statVarWidths.at(i)}; - if (count != 0.0) { + if (const uint64_t count = m_statVarWidths.at(i)) { + std::stringstream ss; + ss << "Vars, width " << std::setw(5) << std::dec << i; + const std::string prefix = ss.str(); if (v3Global.opt.statsVars()) { - const NameMap& nameMapr = m_statVarWidthNames.at(i); - for (const auto& itr : nameMapr) { - std::ostringstream os; - os << "Vars, width " << std::setw(5) << std::dec << i << " " << itr.first; - V3Stats::addStat(m_stage, os.str(), itr.second); + for (const auto& it : m_statVarWidthNames.at(i)) { + addStat(prefix + " " + it.first, it.second); } } else { - std::ostringstream os; - os << "Vars, width " << std::setw(5) << std::dec << i; - V3Stats::addStat(m_stage, os.str(), count); + addStat(prefix, count); } } } + // Node types - for (int type = 0; type < VNType::_ENUM_END; type++) { - const double count{m_statTypeCount.at(type)}; - if (count != 0.0) { - V3Stats::addStat(m_stage, std::string{"Node count, "} + VNType{type}.ascii(), - count); + const auto typeName = [](int type) { return std::string{VNType{type}.ascii()}; }; + for (int t = 0; t < VNType::_ENUM_END; ++t) { + if (const uint64_t count = m_counters.m_statTypeCount[t]) { + addStat("Node count, " + typeName(t), count); } } - for (int type = 0; type < VNType::_ENUM_END; type++) { - for (int type2 = 0; type2 < VNType::_ENUM_END; type2++) { - const double count{m_statAbove[type][type2]}; - if (count != 0.0) { - V3Stats::addStat(m_stage, - (std::string{"Node pairs, "} + VNType{type}.ascii() + "_" - + VNType{type2}.ascii()), - count); + + // Expression combinations + for (int t1 = 0; t1 < VNType::_ENUM_END; ++t1) { + for (int t2 = 0; t2 < VNType::_ENUM_END; ++t2) { + if (const uint64_t c = m_counters.m_statAbove[t1][t2]) { + addStat("Expr combination, " + typeName(t1) + " over " + typeName(t2), c); } } } - // Branch pred - for (int type = 0; type < VBranchPred::_ENUM_END; type++) { - const double count{m_statPred[type]}; - if (count != 0.0) { - V3Stats::addStat(m_stage, - (std::string{"Branch prediction, "} + VBranchPred{type}.ascii()), - count); + + // Branch predictions + for (int t = 0; t < VBranchPred::_ENUM_END; ++t) { + if (const uint64_t c = m_counters.m_statPred[t]) { + addStat(std::string{"Branch prediction, "} + VBranchPred{t}.ascii(), c); } } } @@ -275,11 +153,11 @@ public: //###################################################################### // Top Stats class -void V3Stats::statsStageAll(AstNetlist* nodep, const string& stage, bool fast) { - { StatsVisitor{nodep, stage, fast}; } +void V3Stats::statsStageAll(AstNetlist* nodep, const std::string& stage, bool fastOnly) { + StatsVisitor{nodep, stage, fastOnly}; } void V3Stats::statsFinalAll(AstNetlist* nodep) { - statsStageAll(nodep, "Final"); - statsStageAll(nodep, "Final_Fast", true); + statsStageAll(nodep, "Final all"); + statsStageAll(nodep, "Final fast", true); } diff --git a/src/V3Stats.h b/src/V3Stats.h index 97c61050b..8fd207d28 100644 --- a/src/V3Stats.h +++ b/src/V3Stats.h @@ -117,7 +117,7 @@ public: /// Called each stage static void statsStage(const string& name); /// Called by the top level to collect statistics - static void statsStageAll(AstNetlist* nodep, const string& stage, bool fast = false); + static void statsStageAll(AstNetlist* nodep, const string& stage, bool fastOnly = false); static void statsFinalAll(AstNetlist* nodep); /// Called by the top level to dump the statistics static void statsReport();