Files
envi-code/SourceCode/Code2026/inc/SectionPerfCounter.h
T
company-desktop 6d6f9ddaff diag: 统计回写的实体类,定位到 AecDbEwPipeBase 基类实例
上一版只抓到"绘制期深拷贝"的调用栈,还没回答"是哪个类的实体在回写"。
现在 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 编译通过并部署,脚本自动开图一次得到上述分类统计。
2026-10-28 21:06:22 +08:00

93 lines
3.0 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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>
#include <string>
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);
// 属性 key:用"实体数据"而不是 SAT 文本做 key 时的模拟命中统计。
// 调用方把参与 key 的字段逐个 Add 进来,析构时统一记一次。
// 用途:比较"内容 hash"与"属性 key"两种缓存键的命中率,再决定 Phase 1 用哪个。
class AttrKey
{
public:
// pszClass 一般是 isA()->name(),用于隔离不同实体类
explicit AttrKey(const TCHAR* pszClass);
~AttrKey();
void Add(const void* pData, size_t nLen);
private:
AttrKey(const AttrKey&);
AttrKey& operator=(const AttrKey&);
unsigned long long m_nHash;
std::string m_sClass;
bool m_bActive;
};
// 记录一次临时文件写入
void NoteTempFile(size_t nBytes);
// 记录一次回写(Region -> SAT 文本)
void NoteWriteBack(const char* pszTag);
// 记录一次回写,并按实体类分开统计(谁在开图期回写)
void NoteWriteBackClass(const char* pszClass);
// 打印 + 写文件
void Dump();
// 只追加文本到 %TEMP%\aec_section_perf.txt(供其它模块复用同一份报告)
void AppendReport(const char* pszText);
// 抓一次调用栈写进报告,用于定位"谁在开图期回写实体"这类问题
void TraceCallStack(const char* pszTag, int nSkip = 1);
// 清零
void Reset();
}
#endif // SECTION_PERF_COUNTER_H