// PerfCounterLite.h —— 给 AecRxBase 这类"不链接 CoreLibrary"的模块用的轻量计数器 // // 为什么不复用 SectionPerf:CoreLibrary 是静态库且只被 CoreCommand 链接, // AecRxBase.arx 里拿不到它的符号;所以这里做成 header-only。 // // 行为: // - 仅在 Debug(NDEBUG 未定义)且 AEC_PERF_COUNTER=1 时开启,Release 零开销 // - 每个 tag 统计:调用次数、累计耗时、平均/最长 // - 每 200 次调用重写一次 %TEMP%\aec_prim_perf.txt(快照) // —— 故意不写 aec_section_perf.txt:那个文件是自动化脚本判断"开图完成"的信号, // 提前写会让脚本误判并提前关闭 CAD #ifndef PERF_COUNTER_LITE_H #define PERF_COUNTER_LITE_H #include #include #include #include #include #include namespace perf_lite { struct Entry { Entry() : n(0), us(0), maxus(0) {} long long n; long long us; long long maxus; }; inline bool Enabled() { #ifndef NDEBUG static int s_nOn = -1; if (s_nOn < 0) { const char* psz = ::getenv("AEC_PERF_COUNTER"); s_nOn = (psz != NULL && psz[0] != '\0' && psz[0] != '0') ? 1 : 0; } return s_nOn == 1; #else return false; #endif } inline std::map& Stats() { static std::map s_map; return s_map; } inline long long QpcNow() { LARGE_INTEGER li; ::QueryPerformanceCounter(&li); return (long long)li.QuadPart; } inline long long QpcFreq() { static long long s_nFreq = 0; if (s_nFreq == 0) { LARGE_INTEGER li; if (!::QueryPerformanceFrequency(&li) || li.QuadPart == 0) s_nFreq = 1; else s_nFreq = (long long)li.QuadPart; } return s_nFreq; } inline void Dump() { char szTmp[MAX_PATH + 1]; ::memset(szTmp, 0, sizeof(szTmp)); if (::GetTempPathA(MAX_PATH, szTmp) == 0) return; std::string sPath(szTmp); sPath += "aec_prim_perf.txt"; FILE* fp = NULL; if (fopen_s(&fp, sPath.c_str(), "w") != 0 || fp == NULL) return; fprintf(fp, "---- primitive builders (per tag) ----\n"); fprintf(fp, " count total_ms avg_us max_us tag\n"); const std::map& m = Stats(); for (std::map::const_iterator it = m.begin(); it != m.end(); ++it) { const Entry& e = it->second; const double dAvg = e.n ? ((double)e.us / (double)e.n) : 0.0; fprintf(fp, " %-9lld %10.1f %10.1f %10lld %s\n", e.n, (double)e.us / 1000.0, dAvg, e.maxus, it->first.c_str()); } fclose(fp); } inline void Add(const char* pszTag, long long nStart) { const long long nUs = (QpcNow() - nStart) * 1000000LL / QpcFreq(); Entry& e = Stats()[std::string(pszTag != NULL ? pszTag : "?")]; ++e.n; e.us += nUs; if (nUs > e.maxus) e.maxus = nUs; // 每 200 次落一次快照,开图结束后文件里就是全量统计 static long long s_nTotal = 0; if (++s_nTotal % 200 == 0) Dump(); } // RAII:函数入口构造即可 class Scope { public: explicit Scope(const char* pszTag) : m_pszTag(pszTag), m_nStart(0) { if (Enabled()) m_nStart = QpcNow(); } ~Scope() { if (m_nStart != 0) Add(m_pszTag, m_nStart); } private: Scope(const Scope&); Scope& operator=(const Scope&); const char* m_pszTag; long long m_nStart; }; // 在卸载时再补一次,确保最后的零头也落盘 inline void Flush() { if (Enabled()) Dump(); } } #endif // PERF_COUNTER_LITE_H