diag: 统计回写的实体类,定位到 AecDbEwPipeBase 基类实例 #102

Merged
gjm merged 1 commits from company-desktop/company-desktop-computer/diag-writeback-class AGit into main 2026-10-28 21:25:47 +08:00
4 changed files with 60 additions and 0 deletions

No files matched your search

@@ -1289,6 +1289,7 @@ Acad::ErrorStatus AecDbEwPipeBase::RegionAcisoutDwgOutFields(AcDbDwgFiler* pFile
SectionPerf::Scope perfScope("EwPipe::RegionAcisout");
SectionPerf::NoteWriteBack("EwPipe::RegionAcisout");
SectionPerf::NoteWriteBackClass(CStringA(isA()->name()));
if (m_pRegion == NULL)
return Acad::eInvalidInput;
@@ -65,6 +65,9 @@ namespace
// 属性 key:class 名 -> 唯一/命中统计
std::map<std::string, KeyStat> attrStats;
// 回写次数:实体类 -> 次数
std::map<std::string, unsigned long long> writeBackByClass;
};
// 故意用函数内 static:AGENTS.md 禁止文件级 static。
@@ -262,6 +265,15 @@ namespace SectionPerf
}
}
void NoteWriteBackClass(const char* pszClass)
{
Stats& s = GetStats();
if (!s.bEnabled)
return;
std::string key(pszClass != NULL ? pszClass : "?");
++s.writeBackByClass[key];
}
void Dump()
{
Stats& s = GetStats();
@@ -288,6 +300,18 @@ namespace SectionPerf
s.nTempFiles, s.nTempFileBytes, s.nWriteBacks);
out += szLine;
if (!s.writeBackByClass.empty())
{
out += "---- write-backs by entity class ----\n";
out += " count class\n";
for (std::map<std::string, unsigned long long>::const_iterator it = s.writeBackByClass.begin();
it != s.writeBackByClass.end(); ++it)
{
sprintf_s(szLine, sizeof(szLine), " %-8llu %s\n", it->second, it->first.c_str());
out += szLine;
}
}
if (!s.attrStats.empty())
{
out += "---- attr key (by class, would-be cache hit rate) ----\n";
@@ -417,5 +441,6 @@ namespace SectionPerf
s.nTempFileBytes = 0;
s.nWriteBacks = 0;
s.attrStats.clear();
s.writeBackByClass.clear();
}
}
@@ -73,6 +73,9 @@ namespace SectionPerf
// 记录一次回写(Region -> SAT 文本)
void NoteWriteBack(const char* pszTag);
// 记录一次回写,并按实体类分开统计(谁在开图期回写)
void NoteWriteBackClass(const char* pszClass);
// 打印 + 写文件
void Dump();
+31
View File
@@ -322,6 +322,37 @@ pClone->dwgIn(&filer); // → dwgInFields → acisIn 读回来
(`AecDbX3dPartEntity.cpp:757` 直接 `clone()`),但**尚未证实它就是本次的调用者**,
需要用 PDB 解析上面三个 RVA 才能钉死。
**哪个类在回写**(按实体类统计):
```
---- write-backs by entity class ----
1140 AecDbEwPipeBase
```
全部 1140 次都是 **`AecDbEwPipeBase` 基类实例**(不是 `AecDbEwPipe`,也不是任何具名子类)。
它与属性 key 统计里 `AecDbEwPipeBase` 1172 次几乎一一对应 →
图纸里有约 1172 个基类实例,绘制期每个被深拷贝一次。
(这些实例由 `AecDbAssembleWell.cpp` 的装配井代码创建,多为矩形截面段。)
**完整调用链**(文件:行):
| 层次 | 位置 |
| --- | --- |
| 视口绘制 | `AecDbEntityCommon::base_ViewportDraw` → `AecDbCurveBase::subViewportDraw` |
| 取变换副本 | `AecDbCurveBase::subGetTransformedCopy`(`AecRxBase/AecDbCurveBase.cpp:307`) |
| ↑ 其中的 `clone()` | 触发深拷贝 |
| 深拷贝实现 | `AecDbEntityCommon::base_DeepClone`(`AecRxBase/AecDbEntityCommon.cpp:1232`,`dwgOut` 在 :1265) |
| 写字段 | `AecDbEwPipeBase::dwgOutFields`(`CoreLibrary/AecDbEwPipeBase.cpp:1440`) |
| **写截面** | **`AecDbEwPipeBase::RegionAcisoutDwgOutFields`(`CoreLibrary/AecDbEwPipeBase.cpp:1275`)** |
即:`getTransformedCopy` → `clone()` → 全实体序列化 → 截面走 `acisOut` 写临时 SAT 文件。
注意 `AecDbEwPipeBase::copyFrom`(:1605)里复制截面用的是
`m_pRegion->copyFrom(...)`(内存级 ACIS 拷贝),比"写文件再读回"便宜得多。
所以最局部的修法可能是:给管系实体加一个 `subGetTransformedCopy` 重载,
用 `isA()->create()` + `copyFrom()` 代替 `clone()`,绕开整条序列化链路。
**但 clone 与 copyFrom 的语义并不完全等价,需要先核对**(扩展字典、XData、数据库驻留属性等)。
可选修法(都需要动 `base_DeepClone` 的契约,属于结构性改动,建议单独立项):
| 方案 | 做法 | 风险 |