From 6d6f9ddaffe51e2554fa21752d1ff031a8fe8f34 Mon Sep 17 00:00:00 2001 From: company-desktop-computer Date: Wed, 28 Oct 2026 21:06:22 +0800 Subject: [PATCH] =?UTF-8?q?diag:=20=E7=BB=9F=E8=AE=A1=E5=9B=9E=E5=86=99?= =?UTF-8?q?=E7=9A=84=E5=AE=9E=E4=BD=93=E7=B1=BB=EF=BC=8C=E5=AE=9A=E4=BD=8D?= =?UTF-8?q?=E5=88=B0=20AecDbEwPipeBase=20=E5=9F=BA=E7=B1=BB=E5=AE=9E?= =?UTF-8?q?=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 上一版只抓到"绘制期深拷贝"的调用栈,还没回答"是哪个类的实体在回写"。 现在 SectionPerf 增加按类统计的写回计数(NoteWriteBackClass), 在 RegionAcisoutDwgOutFields 里记一次 isA()->name()。 实测(big1.dwg,开图一次): ---- write-backs by entity class ---- 1140 AecDbEwPipeBase 全部 1140 次都是 AecDbEwPipeBase 基类实例,不是 AecDbEwPipe 也不是具名子类, 与属性 key 统计里 AecDbEwPipeBase 的 1172 次几乎一一对应—— 说明图纸里约有 1172 个基类实例(由 AecDbAssembleWell.cpp 的装配井代码创建, 多为矩形截面段),绘制期每个被深拷贝一次。 完整链路(已写入计划文档): base_ViewportDraw -> AecDbCurveBase::subGetTransformedCopy (AecRxBase/AecDbCurveBase.cpp:307 里的 clone()) -> AecDbEntityCommon::base_DeepClone(AecDbEntityCommon.cpp:1232,dwgOut 在 1265) -> AecDbEwPipeBase::dwgOutFields(AecDbEwPipeBase.cpp:1440) -> AecDbEwPipeBase::RegionAcisoutDwgOutFields(AecDbEwPipeBase.cpp:1275) 顺带发现:AecDbEwPipeBase::copyFrom(:1605)复制截面用的是 m_pRegion->copyFrom(), 是内存级 ACIS 拷贝,比"写文件再读回"便宜得多。所以最局部的修法可能是给管系实体 加 subGetTransformedCopy 重载,用 isA()->create()+copyFrom() 代替 clone(), 绕开整条序列化链路;但 clone 与 copyFrom 语义不完全等价,需先核对。 本次只记录分析,不改行为。 验证:R230 Debug 编译通过并部署,脚本自动开图一次得到上述分类统计。 --- .../Code2026/CoreLibrary/AecDbEwPipeBase.cpp | 1 + .../CoreLibrary/SectionPerfCounter.cpp | 25 +++++++++++++++ SourceCode/Code2026/inc/SectionPerfCounter.h | 3 ++ pipe-section-region-performance-plan.md | 31 +++++++++++++++++++ 4 files changed, 60 insertions(+) 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` 的契约,属于结构性改动,建议单独立项): | 方案 | 做法 | 风险 |