fix: AecDbX3dPart 记住"已尝试建过",避免 Hide 构件被反复重建

现象:开图时 X3dPart 的 EnsureCreated 命中率只有 10.1%,1914 次重建里
721 次产出 NULL(38%),单个对象最多被重建 544 次。

根因:EnsureCreated 的命中条件是 !m_bNeedCreate && m_pSolid != NULL,
而 Rebuild() 里对 Hide 构件是"故意不建几何、直接 return true"——
m_pSolid 按设计就是 NULL,于是每次访问都重新走一遍 Rebuild,永远不可能命中。
子项全为空的构件同理。

修复:
- 新增 m_bCreated 标记"已经尝试建过"(无论是否产出实体)
- EnsureCreated 命中条件改为 !m_bNeedCreate && (m_bCreated || m_pSolid != NULL)
- Rebuild 入口置 m_bCreated = true;所有设 m_bNeedCreate = true 的 setter
  同时置 m_bCreated = false,保证参数变化后仍会重建
- 重建后仍无实体时打一条日志(限前 5 条,带 m_sName),方便定位是哪些构件

实测(big1.dwg / R230 Debug):
  X3dPart 命中率      10.1% -> 41.4%
  重建次数            1914  -> 1248(-666)
  rebuild_null         721  -> 55
  单对象最多重建       544  -> <=3
  开图墙钟             93 s -> 93 s(无变化)

注意:这 666 次空转没有带来可测的提速,因为 Rebuild 里 Hide 的早退分支在
布尔运算之前,每次只有几微秒。所以这是正确性/整洁度修复,不是性能修复;
但消除了"个别构件被重试几百次"这种病态行为,也暴露了"哪些构件建不出实体"。

顺带新增诊断(Debug + AEC_PERF_COUNTER=1 才生效,Release 无开销):
- SectionPerf::NoteEnsure():按 tag 统计"延迟建几何"的命中/重建,
  并给出对象维度直方图与重建次数最高的对象
- AecDbX3dPart::Rebuild / AcDbX3dSolid::EnsureCreated / AecDbGuardBar::Draw3d 打点
- tools/run_acad_perf.ps1 增加 -CpuTrace:用 xperf 采 CPU 采样并 merge 成 etl,
  可直接喂 tools/perf_diagsession.py(VS 的 VSDiagnostics 在这台机器上会卡住,
  wpr -stop 有 COM 线程模式 bug,故用 xperf)

验证:R230 Debug 编译通过并部署,自动开图取数;截面缓存与回写仍为 0/正常。
This commit is contained in:
company-desktop committed 2026-10-28 22:51:01 +08:00
1 parent 56c89c5fa4
commit facf14699b
7 files changed
+154 -3

No files matched your search

@@ -1,5 +1,6 @@
#include "stdafx.h"
#include "AecDbGuardBar.h"
#include "SectionPerfCounter.h"
#include "KTSetting.h"
#include "KTCADUtilityEx.h"
#include "dbgrip.h"
@@ -188,6 +189,7 @@ Acad::ErrorStatus AecDbGuardBar::SubAddDimPoints(AcDbGripDataPtrArray& grips,con
AcGeVector3d vtDirX;
AcGePoint3d ptGe[4],ptMid;
SectionPerf::NoteEnsure("GuardBar::Draw3d", this, true);
GuardBar bar;
GetGuardBarData(bar);
@@ -786,6 +788,7 @@ AcDbEntity* AecDbGuardBar::DrawPipe(AcGiDraw &dc,AcDbCurve* pBaseCln1,AcDbRegion
Acad::ErrorStatus AecDbGuardBar::Draw3d(AcGiDraw &dc) const
{
SectionPerf::NoteEnsure("GuardBar::Draw3d.entry", this, false);
//护栏的样子参照 PDMS 的样子
assertReadEnabled();
@@ -823,6 +826,7 @@ Acad::ErrorStatus AecDbGuardBar::Draw3d(AcGiDraw &dc) const
if (Has3DBuffer())
{
SectionPerf::NoteEnsure("GuardBar::Draw3d", this, false);
//Clean3DBuffer();
AcDbEntity* pEnt = NULL;
const AcDbVoidPtrArray &ar3D = Get3DBuffer();