perf: 增加截面恢复路径的 Phase 0 计数器(Debug 探针) #97
No files matched your search
@@ -20,6 +20,7 @@
|
||||
#include "SelectBlkDlg.h"
|
||||
#include "gpc.h"
|
||||
#include "ImpMD5.h"
|
||||
#include "SectionPerfCounter.h"
|
||||
#include "breptools.h"
|
||||
#include "DimScaleZoneDlg.h"
|
||||
#include "simpleProperty.h"
|
||||
@@ -3113,6 +3114,10 @@ namespace
|
||||
|
||||
Adesk::UInt64 CoreTools::RegionToString(const AcDbRegion *pReg, char *& pszTxt)
|
||||
{
|
||||
// Phase 0 计数:回写(Region -> SAT 文本)次数
|
||||
SectionPerf::Scope perfScope("CoreTools::RegionToString");
|
||||
SectionPerf::NoteWriteBack("CoreTools::RegionToString");
|
||||
|
||||
Adesk::UInt64 dwFileLength = 0;
|
||||
pszTxt = NULL;
|
||||
|
||||
@@ -3518,6 +3523,9 @@ bool CoreTools::CurveToString(const AcDbCurve * pCurve, CString & sScr2)
|
||||
|
||||
AcDbRegion* CoreTools::StringToRegion(const CString & sRegionScrp)
|
||||
{
|
||||
// Phase 0 计数:截面恢复耗时/次数/文本唯一性,见 pipe-section-region-performance-plan.md
|
||||
SectionPerf::Scope perfScope("CoreTools::StringToRegion");
|
||||
|
||||
std::string sUtf8 = CoreTools::unicode2Ansii((LPCTSTR)sRegionScrp, CP_UTF8);
|
||||
std::string sNewSrc;
|
||||
if (!fixACISFormat(sUtf8.c_str(), sUtf8.size(), sNewSrc))
|
||||
@@ -3538,10 +3546,13 @@ AcDbRegion* CoreTools::StringToRegion(const CString & sRegionScrp)
|
||||
CString sFullPath = MakeTempSatPath();
|
||||
if (sFullPath.IsEmpty()) return NULL;
|
||||
|
||||
SectionPerf::NoteRegionText(sNewSrc.c_str(), sNewSrc.size());
|
||||
|
||||
if (file.Open(sFullPath, CFile::modeCreate | CFile::modeWrite))
|
||||
{
|
||||
file.Write(sNewSrc.c_str(), sizeof(char) * (UINT)sNewSrc.size());
|
||||
file.Close();
|
||||
SectionPerf::NoteTempFile(sNewSrc.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3580,6 +3591,8 @@ AcDbRegion* CoreTools::StringToRegion(const CString & sRegionScrp)
|
||||
|
||||
AcDbRegion *CoreTools::StringToRegion(Adesk::UInt64 nLen, char *pszReg)
|
||||
{
|
||||
SectionPerf::Scope perfScope("CoreTools::StringToRegion(buf)");
|
||||
|
||||
std::string sNewSrc;
|
||||
if (!fixACISFormat(pszReg, (size_t)nLen, sNewSrc))
|
||||
{
|
||||
@@ -3597,11 +3610,14 @@ AcDbRegion *CoreTools::StringToRegion(Adesk::UInt64 nLen, char *pszReg)
|
||||
CString sFullPath = MakeTempSatPath();
|
||||
if (sFullPath.IsEmpty()) return NULL;
|
||||
|
||||
SectionPerf::NoteRegionText(sNewSrc.c_str(), sNewSrc.size());
|
||||
|
||||
CFile file;
|
||||
if (file.Open(sFullPath, CFile::modeCreate | CFile::modeWrite))
|
||||
{
|
||||
file.Write(sNewSrc.c_str(), sizeof(char) * (UINT)sNewSrc.size());
|
||||
file.Close();
|
||||
SectionPerf::NoteTempFile(sNewSrc.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "AecDbPingFaDim.h"
|
||||
#include "AecDbDimScaleZone.h"
|
||||
#include "AecDbJoggedCamera.h"
|
||||
#include "SectionPerfCounter.h"
|
||||
|
||||
#include "breptools.h"
|
||||
#include "HlrCutOption.h"
|
||||
@@ -543,6 +544,20 @@ KTDocReactor *g_pDocReactor(NULL);
|
||||
#define ADD_CMD(cmd, func) CoreTools::AddCommand(_T("KTEARTHWORMCORE"), _T(#cmd), _T(#cmd), ACRX_CMD_TRANSPARENT | ACRX_CMD_USEPICKSET, func)
|
||||
#define ADD_CMD_EX(cmd, func, flag) CoreTools::AddCommand(_T("KTEARTHWORMCORE"), _T(#cmd), _T(#cmd), flag, func)
|
||||
|
||||
#if defined(_DEBUG)
|
||||
// Phase 0:截面恢复计数器。实现见 CoreLibrary/SectionPerfCounter.cpp
|
||||
void envierpf_dump()
|
||||
{
|
||||
SectionPerf::Dump();
|
||||
}
|
||||
|
||||
void envierpf_reset()
|
||||
{
|
||||
SectionPerf::Reset();
|
||||
acutPrintf(_T("\n[SectionPerf] 已清零。"));
|
||||
}
|
||||
#endif
|
||||
|
||||
void InitApplication()
|
||||
{
|
||||
#ifdef ZWCAD
|
||||
@@ -1066,6 +1081,12 @@ void InitApplication()
|
||||
void pzjcs();
|
||||
ADD_CMD(co_pzjcs, pzjcs);
|
||||
|
||||
#if defined(_DEBUG)
|
||||
// Phase 0:截面恢复计数器(仅 Debug,且需启动前设 AEC_PERF_COUNTER=1)
|
||||
ADD_CMD(ENVIPERFDUMP, envierpf_dump);
|
||||
ADD_CMD(ENVIPERFRESET, envierpf_reset);
|
||||
#endif
|
||||
|
||||
#pragma endregion
|
||||
|
||||
if (CKTCadUtility::SetSystemVar(_T("STATUSBARAUTOWRAP"), 0))
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "GiDrawUtility.h"
|
||||
#include "AcGiViewDraw.h"
|
||||
#include "AcDbX3dSolid.h"
|
||||
#include "SectionPerfCounter.h"
|
||||
|
||||
#ifndef EW_EARTHWORMINIT
|
||||
#define EW_EARTHWORMINIT _T("EW_EARTHWORMINIT")
|
||||
@@ -793,6 +794,9 @@ AcDbRegion *AecDbEwPipeBase::RegionFromCurvesDwgInFields(AcDbDwgFiler *pFiler, A
|
||||
|
||||
AcDbRegion* AecDbEwPipeBase::RegionAcisInDwgInFields(AcDbDwgFiler* pFiler)
|
||||
{
|
||||
// Phase 0 计数:截面恢复耗时/次数,见 pipe-section-region-performance-plan.md
|
||||
SectionPerf::Scope perfScope("EwPipe::RegionAcisIn");
|
||||
|
||||
//高版本acis方式
|
||||
CString sFileName;
|
||||
TCHAR* pszFileName = NULL;
|
||||
@@ -816,6 +820,9 @@ AcDbRegion* AecDbEwPipeBase::RegionAcisInDwgInFields(AcDbDwgFiler* pFiler)
|
||||
::memset(pszTxt, 0, nLen + 1);
|
||||
pFiler->readBytes(pszTxt,nLen);
|
||||
|
||||
// 同一份 SAT 文本被恢复多少次:唯一 hash 数 / 模拟命中率
|
||||
SectionPerf::NoteRegionText(pszTxt, (size_t)nLen);
|
||||
|
||||
if (sFileName.CompareNoCase(_T("errorfile")) == 0)
|
||||
{
|
||||
//原生写法
|
||||
@@ -854,6 +861,9 @@ AcDbRegion* AecDbEwPipeBase::RegionAcisInDwgInFields(AcDbDwgFiler* pFiler)
|
||||
file.Close();
|
||||
delete[] pszTxt;
|
||||
|
||||
// 读侧落盘的临时文件次数/字节(杀软扫描影响面)
|
||||
SectionPerf::NoteTempFile((size_t)nLen);
|
||||
|
||||
AcDbVoidPtrArray arBodyPtrs;
|
||||
Acad::ErrorStatus es = AcDbBody::acisIn(sFullPath,arBodyPtrs);
|
||||
if (es != Acad::eOk)
|
||||
@@ -1196,6 +1206,10 @@ Acad::ErrorStatus AecDbEwPipeBase::BaseCurveDwgOutFields(AcDbCurve* pBaseCurve,A
|
||||
|
||||
Acad::ErrorStatus AecDbEwPipeBase::RegionAcisoutDwgOutFields(AcDbDwgFiler* pFiler) const
|
||||
{
|
||||
// Phase 0 计数:回写(Region -> SAT 文本)的次数与耗时
|
||||
SectionPerf::Scope perfScope("EwPipe::RegionAcisout");
|
||||
SectionPerf::NoteWriteBack("EwPipe::RegionAcisout");
|
||||
|
||||
if (m_pRegion == NULL)
|
||||
return Acad::eInvalidInput;
|
||||
|
||||
@@ -1251,6 +1265,9 @@ Acad::ErrorStatus AecDbEwPipeBase::RegionAcisoutDwgOutFields(AcDbDwgFiler* pFile
|
||||
ULONGLONG dwFileLength = file.GetLength();
|
||||
Adesk::UInt32 nLen = (Adesk::UInt32)dwFileLength;
|
||||
|
||||
// acisOut 写出来的临时文件(回写侧的落盘量)
|
||||
SectionPerf::NoteTempFile((size_t)nLen);
|
||||
|
||||
char* pszTxt = new char[nLen + 1];
|
||||
::memset(pszTxt, 0, nLen + 1);
|
||||
|
||||
|
||||
@@ -0,0 +1,281 @@
|
||||
#include "stdafx.h"
|
||||
#include "SectionPerfCounter.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
namespace
|
||||
{
|
||||
// 单个计数器:次数 + 累计耗时(µs)
|
||||
struct Entry
|
||||
{
|
||||
Entry() : nCount(0), nTotalUs(0), nMaxUs(0) {}
|
||||
|
||||
unsigned long long nCount;
|
||||
unsigned long long nTotalUs;
|
||||
unsigned long long nMaxUs;
|
||||
};
|
||||
|
||||
// 唯一 hash 表的上限:正常图纸几十到几百个唯一截面,
|
||||
// 万一文本各不相同(说明缓存没意义)也不让内存无限涨
|
||||
const size_t kMaxUniqueHash = 50000;
|
||||
|
||||
struct Stats
|
||||
{
|
||||
Stats()
|
||||
: bInited(false), bEnabled(false)
|
||||
, nRegionTexts(0), nUniqueHashes(0), nSimulatedHits(0), bHashTableOverflow(false)
|
||||
, nTempFiles(0), nTempFileBytes(0)
|
||||
, nWriteBacks(0)
|
||||
{
|
||||
}
|
||||
|
||||
bool bInited;
|
||||
bool bEnabled;
|
||||
|
||||
std::map<std::string, Entry> entries; // tag -> 次数/耗时
|
||||
|
||||
unsigned long long nRegionTexts; // 一共恢复过多少份 SAT 文本
|
||||
size_t nUniqueHashes; // 其中不同内容有多少
|
||||
unsigned long long nSimulatedHits; // 若做内容缓存,能命中多少次
|
||||
bool bHashTableOverflow;
|
||||
|
||||
std::set<unsigned long long> hashSeen;
|
||||
|
||||
unsigned long long nTempFiles; // 临时文件写入次数
|
||||
unsigned long long nTempFileBytes; // 临时文件累计字节
|
||||
|
||||
unsigned long long nWriteBacks; // Region -> SAT 文本 次数
|
||||
};
|
||||
|
||||
// 故意用函数内 static:AGENTS.md 禁止文件级 static。
|
||||
// CoreLibrary 是静态库且只被 CoreCommand 链接,
|
||||
// 所以这里的实例在 CoreCommand.arx 里是唯一的一份。
|
||||
Stats& GetStats()
|
||||
{
|
||||
static Stats s_stats;
|
||||
if (!s_stats.bInited)
|
||||
{
|
||||
s_stats.bInited = true;
|
||||
#if defined(_DEBUG)
|
||||
const char* psz = ::getenv("AEC_PERF_COUNTER");
|
||||
s_stats.bEnabled = (psz != NULL && psz[0] != '\0' && psz[0] != '0');
|
||||
#else
|
||||
s_stats.bEnabled = false;
|
||||
#endif
|
||||
}
|
||||
return s_stats;
|
||||
}
|
||||
|
||||
long long NowTicks()
|
||||
{
|
||||
LARGE_INTEGER li;
|
||||
::QueryPerformanceCounter(&li);
|
||||
return (long long)li.QuadPart;
|
||||
}
|
||||
|
||||
unsigned long long TicksToUs(long long nTicks)
|
||||
{
|
||||
static long long nFreq = 0;
|
||||
if (nFreq == 0)
|
||||
{
|
||||
LARGE_INTEGER li;
|
||||
if (!::QueryPerformanceFrequency(&li) || li.QuadPart == 0)
|
||||
nFreq = 1;
|
||||
else
|
||||
nFreq = (long long)li.QuadPart;
|
||||
}
|
||||
if (nTicks <= 0)
|
||||
return 0;
|
||||
return (unsigned long long)((nTicks * 1000000LL) / nFreq);
|
||||
}
|
||||
|
||||
// FNV-1a 64
|
||||
unsigned long long Hash64(const char* pszTxt, size_t nLen)
|
||||
{
|
||||
unsigned long long h = 1469598103934665603ULL;
|
||||
for (size_t i = 0; i < nLen; ++i)
|
||||
{
|
||||
h ^= (unsigned char)pszTxt[i];
|
||||
h *= 1099511628211ULL;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
Entry& GetEntry(const char* pszTag)
|
||||
{
|
||||
Stats& s = GetStats();
|
||||
std::string key(pszTag ? pszTag : "?");
|
||||
return s.entries[key];
|
||||
}
|
||||
|
||||
std::string TempReportPath()
|
||||
{
|
||||
char szTmp[MAX_PATH + 1];
|
||||
::memset(szTmp, 0, sizeof(szTmp));
|
||||
DWORD n = ::GetTempPathA(MAX_PATH, szTmp);
|
||||
if (n == 0 || n >= MAX_PATH)
|
||||
return std::string("aec_section_perf.txt");
|
||||
std::string s(szTmp);
|
||||
s += "aec_section_perf.txt";
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
namespace SectionPerf
|
||||
{
|
||||
bool Enabled()
|
||||
{
|
||||
return GetStats().bEnabled;
|
||||
}
|
||||
|
||||
Scope::Scope(const char* pszTag)
|
||||
: m_pszTag(pszTag), m_nStart(0), m_bActive(false)
|
||||
{
|
||||
if (!GetStats().bEnabled)
|
||||
return;
|
||||
m_bActive = true;
|
||||
m_nStart = NowTicks();
|
||||
}
|
||||
|
||||
Scope::~Scope()
|
||||
{
|
||||
if (!m_bActive)
|
||||
return;
|
||||
|
||||
const unsigned long long nUs = TicksToUs(NowTicks() - m_nStart);
|
||||
Entry& e = GetEntry(m_pszTag);
|
||||
++e.nCount;
|
||||
e.nTotalUs += nUs;
|
||||
if (nUs > e.nMaxUs)
|
||||
e.nMaxUs = nUs;
|
||||
}
|
||||
|
||||
void NoteRegionText(const char* pszTxt, size_t nLen)
|
||||
{
|
||||
Stats& s = GetStats();
|
||||
if (!s.bEnabled)
|
||||
return;
|
||||
if (pszTxt == NULL || nLen == 0)
|
||||
return;
|
||||
|
||||
++s.nRegionTexts;
|
||||
|
||||
const unsigned long long h = Hash64(pszTxt, nLen);
|
||||
if (s.hashSeen.find(h) != s.hashSeen.end())
|
||||
{
|
||||
++s.nSimulatedHits;
|
||||
return;
|
||||
}
|
||||
if (s.hashSeen.size() < kMaxUniqueHash)
|
||||
{
|
||||
s.hashSeen.insert(h);
|
||||
++s.nUniqueHashes;
|
||||
}
|
||||
else
|
||||
{
|
||||
s.bHashTableOverflow = true;
|
||||
}
|
||||
}
|
||||
|
||||
void NoteTempFile(size_t nBytes)
|
||||
{
|
||||
Stats& s = GetStats();
|
||||
if (!s.bEnabled)
|
||||
return;
|
||||
++s.nTempFiles;
|
||||
s.nTempFileBytes += (unsigned long long)nBytes;
|
||||
}
|
||||
|
||||
void NoteWriteBack(const char* pszTag)
|
||||
{
|
||||
Stats& s = GetStats();
|
||||
if (!s.bEnabled)
|
||||
return;
|
||||
++s.nWriteBacks;
|
||||
(void)pszTag;
|
||||
}
|
||||
|
||||
void Dump()
|
||||
{
|
||||
Stats& s = GetStats();
|
||||
if (!s.bEnabled)
|
||||
{
|
||||
acutPrintf(_T("\n[SectionPerf] not enabled: set AEC_PERF_COUNTER=1 before starting CAD (Debug build only)."));
|
||||
return;
|
||||
}
|
||||
|
||||
// 报告正文全 ASCII:控制台用 %hs 直出,落文件也直接可读
|
||||
std::string out;
|
||||
char szLine[1024];
|
||||
|
||||
out += "\n================ SectionPerf (Phase 0) ================\n";
|
||||
|
||||
sprintf_s(szLine, sizeof(szLine), "RegionTexts=%llu UniqueHashes=%llu SimulatedHits=%llu HitRate=%.1f%%\n",
|
||||
s.nRegionTexts, (unsigned long long)s.nUniqueHashes, s.nSimulatedHits,
|
||||
s.nRegionTexts ? (100.0 * (double)s.nSimulatedHits / (double)s.nRegionTexts) : 0.0);
|
||||
out += szLine;
|
||||
if (s.bHashTableOverflow)
|
||||
out += " (hash table capped: UniqueHashes is under-counted, HitRate over-estimated)\n";
|
||||
|
||||
sprintf_s(szLine, sizeof(szLine), "TempFiles=%llu TempBytes=%llu WriteBacks=%llu\n",
|
||||
s.nTempFiles, s.nTempFileBytes, s.nWriteBacks);
|
||||
out += szLine;
|
||||
|
||||
out += "---- timing (by tag) ----\n";
|
||||
out += " count total_ms avg_us max_us tag\n";
|
||||
for (std::map<std::string, Entry>::const_iterator it = s.entries.begin();
|
||||
it != s.entries.end(); ++it)
|
||||
{
|
||||
const Entry& e = it->second;
|
||||
const double avgUs = e.nCount ? ((double)e.nTotalUs / (double)e.nCount) : 0.0;
|
||||
sprintf_s(szLine, sizeof(szLine), " %-9llu %10.1f %10.1f %10llu %s\n",
|
||||
e.nCount, (double)e.nTotalUs / 1000.0, avgUs, e.nMaxUs, it->first.c_str());
|
||||
out += szLine;
|
||||
}
|
||||
out += "=======================================================\n";
|
||||
|
||||
// 逐行输出:%hs 把窄字符串塞进 TCHAR 格式串,不涉及宽字符转换
|
||||
const char* psz = out.c_str();
|
||||
while (*psz != '\0')
|
||||
{
|
||||
const char* pszEnd = strchr(psz, '\n');
|
||||
const size_t nSeg = (pszEnd != NULL) ? (size_t)(pszEnd - psz) : strlen(psz);
|
||||
if (nSeg > 0 && nSeg < sizeof(szLine))
|
||||
{
|
||||
memcpy(szLine, psz, nSeg);
|
||||
szLine[nSeg] = '\0';
|
||||
acutPrintf(_T("\n%hs"), szLine);
|
||||
}
|
||||
if (pszEnd == NULL)
|
||||
break;
|
||||
psz = pszEnd + 1;
|
||||
}
|
||||
|
||||
// 同时落文件,方便事后拿去做分析
|
||||
const std::string sPath = TempReportPath();
|
||||
FILE* fp = NULL;
|
||||
if (fopen_s(&fp, sPath.c_str(), "a") == 0 && fp != NULL)
|
||||
{
|
||||
fprintf(fp, "%s", out.c_str());
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
Stats& s = GetStats();
|
||||
s.entries.clear();
|
||||
s.hashSeen.clear();
|
||||
s.nRegionTexts = 0;
|
||||
s.nUniqueHashes = 0;
|
||||
s.nSimulatedHits = 0;
|
||||
s.bHashTableOverflow = false;
|
||||
s.nTempFiles = 0;
|
||||
s.nTempFileBytes = 0;
|
||||
s.nWriteBacks = 0;
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ set(SOURCES
|
||||
"AECAngleWatch.cpp"
|
||||
"AecComplexCurveDraw.cpp"
|
||||
"AecCoverBooleanOper.cpp"
|
||||
"SectionPerfCounter.cpp"
|
||||
"AecDb3dRoad.cpp"
|
||||
"AecDb3dRoadAxisLine.cpp"
|
||||
"AecDb3dRoadFitting.cpp"
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
// SectionPerfCounter.h —— Phase 0 计数器:截面(Region)恢复路径的实测探针
|
||||
//
|
||||
// 目的(见 pipe-section-region-performance-plan.md 的 Phase 0):
|
||||
// 1. 每次截面恢复到底多贵(µs),一次开图总共多少次、多少时间;
|
||||
// 2. 同一份 SAT 文本被重复恢复多少次(唯一 hash 数、模拟命中率)——
|
||||
// 这个数字直接决定 Phase 1 截面缓存能省多少;
|
||||
// 3. 临时文件写入次数与累计字节(杀软扫描影响面);
|
||||
// 4. 放回写(RegionToString / RegionAcisoutDwgOutFields)的次数。
|
||||
//
|
||||
// 使用方式:
|
||||
// set AEC_PERF_COUNTER=1 必须在启动 CAD 前设好(进程内只读一次)
|
||||
// ENVIPERFDUMP 在 CAD 命令行打印汇总,同时追加到 %TEMP%\aec_section_perf.txt
|
||||
// ENVIPERFRESET 清零
|
||||
//
|
||||
// 只在 _DEBUG 下生效:Release 里 Enabled() 恒为 false,调用点只剩一次 bool 判断。
|
||||
// 不改变任何几何逻辑与存盘格式。
|
||||
|
||||
#ifndef SECTION_PERF_COUNTER_H
|
||||
#define SECTION_PERF_COUNTER_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace SectionPerf
|
||||
{
|
||||
// 环境变量 AEC_PERF_COUNTER 是否开启,且当前是 Debug 构建
|
||||
bool Enabled();
|
||||
|
||||
// 计时作用域:函数入口构造,离开时累加耗时与次数
|
||||
class Scope
|
||||
{
|
||||
public:
|
||||
explicit Scope(const char* pszTag);
|
||||
~Scope();
|
||||
|
||||
private:
|
||||
Scope(const Scope&);
|
||||
Scope& operator=(const Scope&);
|
||||
|
||||
const char* m_pszTag;
|
||||
long long m_nStart;
|
||||
bool m_bActive;
|
||||
};
|
||||
|
||||
// 记录一次"待恢复的 SAT 文本":内部按内容算 64 位 hash,
|
||||
// 统计唯一 hash 数与被重复命中的次数(模拟命中率)
|
||||
void NoteRegionText(const char* pszTxt, size_t nLen);
|
||||
|
||||
// 记录一次临时文件写入
|
||||
void NoteTempFile(size_t nBytes);
|
||||
|
||||
// 记录一次回写(Region -> SAT 文本)
|
||||
void NoteWriteBack(const char* pszTag);
|
||||
|
||||
// 打印 + 写文件
|
||||
void Dump();
|
||||
|
||||
// 清零
|
||||
void Reset();
|
||||
}
|
||||
|
||||
#endif // SECTION_PERF_COUNTER_H
|
||||
@@ -88,16 +88,29 @@ pSolidO->booleanOper(kBoolSubtract, pSolidI) // 布尔减
|
||||
|
||||
### Phase 0:先测量(不改行为)
|
||||
|
||||
在 `RegionAcisInDwgInFields` 与 `StringToRegion` 两个入口 + 临时文件写/删处加计数,
|
||||
`_DEBUG` 编译进、环境变量 `AEC_PERF_COUNTER=1` 开启、命令 `ENVIPERFDUMP` 打印汇总:
|
||||
已在 `AecDbEwPipeBase::RegionAcisInDwgInFields` / `RegionAcisoutDwgOutFields` 与
|
||||
`CoreTools::StringToRegion`(两个重载)/ `RegionToString` 上接入探针,
|
||||
实现见 `inc/SectionPerfCounter.h` + `CoreLibrary/SectionPerfCounter.cpp`。
|
||||
|
||||
**用法**(必须是 Debug 构建):
|
||||
|
||||
```bat
|
||||
set AEC_PERF_COUNTER=1 :: 必须在启动 CAD 之前设好,进程内只读一次
|
||||
:: 打开图纸
|
||||
ENVIPERFDUMP :: 命令行打印汇总,并追加到 %TEMP%\aec_section_perf.txt
|
||||
ENVIPERFRESET :: 清零,方便多次对比
|
||||
```
|
||||
|
||||
Release 里 `SectionPerf::Enabled()` 恒为 false,调用点只剩一次 bool 判断,无发布风险。
|
||||
|
||||
采集项:
|
||||
|
||||
| 计数项 | 用途 |
|
||||
| --- | --- |
|
||||
| 调用次数、单次/累计 µs(QPC 包住整段) | 单次成本(预估 3~15 ms) |
|
||||
| 输入文本 hash 的**唯一值数** | 复用率 = 1 − 唯一/总数 → Phase 1 收益上限 |
|
||||
| **模拟命中率**(只统计、不改行为) | 同上,更直观 |
|
||||
| 临时文件写入次数 + 累计字节 | 杀软影响面 |
|
||||
| `RegionToString` / `RegionAcisoutDwgOutFields` 次数 | 查清打开时为何在回写(3.07 %) |
|
||||
| `RegionTexts` / `UniqueHashes` / `SimulatedHits` / `HitRate` | 复用率 = 1 − 唯一/总数 → Phase 1 收益上限 |
|
||||
| 每个 tag 的 `count` / `total_ms` / `avg_us` / `max_us` | 单次成本,判断值不值得做 |
|
||||
| `TempFiles` / `TempBytes` | 杀软扫描影响面 |
|
||||
| `WriteBacks` | 打开图纸时为何在回写(3.07 %) |
|
||||
|
||||
**决策点 D0**:唯一数是否远小于总次数(例如 30 种 × 1500 次)。
|
||||
- 是 → Phase 1 直接做
|
||||
|
||||
Reference in new issue
Block a user