diff --git a/SourceCode/Code2026/CoreLibrary/AecDbEwPipeBase.cpp b/SourceCode/Code2026/CoreLibrary/AecDbEwPipeBase.cpp index 166c0d2..659bf52 100644 --- a/SourceCode/Code2026/CoreLibrary/AecDbEwPipeBase.cpp +++ b/SourceCode/Code2026/CoreLibrary/AecDbEwPipeBase.cpp @@ -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; diff --git a/SourceCode/Code2026/CoreLibrary/SectionPerfCounter.cpp b/SourceCode/Code2026/CoreLibrary/SectionPerfCounter.cpp index f8c7ffd..c52f11a 100644 --- a/SourceCode/Code2026/CoreLibrary/SectionPerfCounter.cpp +++ b/SourceCode/Code2026/CoreLibrary/SectionPerfCounter.cpp @@ -65,6 +65,9 @@ namespace // 属性 key:class 名 -> 唯一/命中统计 std::map attrStats; + + // 回写次数:实体类 -> 次数 + std::map 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::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(); } } diff --git a/SourceCode/Code2026/inc/SectionPerfCounter.h b/SourceCode/Code2026/inc/SectionPerfCounter.h index b8f3d43..e27603f 100644 --- a/SourceCode/Code2026/inc/SectionPerfCounter.h +++ b/SourceCode/Code2026/inc/SectionPerfCounter.h @@ -73,6 +73,9 @@ namespace SectionPerf // 记录一次回写(Region -> SAT 文本) void NoteWriteBack(const char* pszTag); + // 记录一次回写,并按实体类分开统计(谁在开图期回写) + void NoteWriteBackClass(const char* pszClass); + // 打印 + 写文件 void Dump(); diff --git a/pipe-section-region-performance-plan.md b/pipe-section-region-performance-plan.md index 9bf742b..8b0ed25 100644 --- a/pipe-section-region-performance-plan.md +++ b/pipe-section-region-performance-plan.md @@ -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` 的契约,属于结构性改动,建议单独立项): | 方案 | 做法 | 风险 |