Files
envi-code/SourceCode/Code2026/inc/SectionPerfCounter.h
T
company-desktop 764e544db4 perf: 增加属性 key 命中率统计(Phase 0 补充)
意图:内容 hash 只看到 54% 命中率,但业务上"同数据的管子/管件截面必然相同",
所以要用实体属性做 key 再量一次,决定 Phase 1 到底用哪种缓存键。

新增 SectionPerf::AttrKey(RAII):
- 构造时按类名(isA()->name())初始化 hash 与统计桶,析构时记一次
- Add() 增量喂字节,调用方决定哪些字段参与 key
- 统计按实体类分开(total / unique / hits / hit_rate)

接入 AecDbEwPipeBase::RegionAcisInDwgInFields:key = 类名 + 全部 spec XData,
但排除编号("No.",每根管子唯一)并跳过 Id / 点 / 向量 / 矩阵 / 二进制类型
(与截面无关或随实体变化)。只在 AEC_PERF_COUNTER=1 时构建,Release 无开销。

实测(big1.dwg,AutoCAD 2020 / R230 Debug,113 s 开图):

  属性 key 合计 3082 次 / 2672 命中 = 86.7%
    AecDbEwPipe       1088 次  173 唯一  84.1%
    AecDbEwPipeBase   1172 次    3 唯一  99.7%
       (其余类 43%~99%)
  内容 hash 54.0%

结论:属性 key 显著优于内容 hash,但只覆盖 3082/5641 次恢复
(另 2559 次走 CoreTools::StringToRegion,拿不到 spec 数据,只能用内容 hash)。
混合策略预计省 14.9 s / 22.6 s 恢复开销,约占 113 s 开图的 13%。

数据已写入 pipe-section-region-performance-plan.md,并标注一个未验证点:
属性 key 命中时两份 SAT 文本是否真的相同(决定 Phase 1 是否需要 textHash 兜底校验)。

验证:R230 Debug 编译通过并部署,用 tools/run_acad_perf.ps1 自动跑图取数。
2026-10-28 20:06:06 +08:00

84 lines
2.6 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 Dump();
// 清零
void Reset();
}
#endif // SECTION_PERF_COUNTER_H