company-desktop
|
facf14699b
|
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/正常。
|
2026-10-28 22:51:01 +08:00 |
|
company-desktop
|
7038a98441
|
diag: 用 PDB 解析栈帧,定位到 AecDbEwPipeBase::copyFrom 里的多余序列化
上一版只给了"模块+RVA"的栈,我据此先后推断成
AecDbCurveBase::subGetTransformedCopy 和 AecDbEntityCommon::base_DeepClone,两者都是错的
(用户在那里下断点不命中;新加的 PerfTraceMark 打点也没有触发)。本次用符号解析定死。
本次改动:
- 新增 inc/PerfTraceMark.h:inline 的"某函数被调用过"打点,Debug 且 AEC_WRITEBACK_TRACE=1
才生效。AecRxBase.arx 不链接 CoreLibrary(静态库只被 CoreCommand 链接),拿不到 SectionPerf,
所以做成 header-only。
- 在 5 个候选入口打点:AecDbCurveBase::subGetTransformedCopy / subDeepClone、
AecDbEntityBase::subDeepClone、AecDbEntityCommon::base_DeepClone、CKTCadUtility::CloneObject。
跑一次开图,5 个都没触发 —— 这些候选被排除。
- 新增 tools/resolve_rva.ps1:用 dbghelp 的 SymLoadModuleEx / SymFromAddr 配本地 PDB,
把"模块+RVA"解析成函数名。Windows 没有自带 symbolizer,这个脚本很轻,可反复用。
解析结果(CoreCommand.arx):
0x58f051 AecDbEwPipeBase::copyFrom+0x41
0x7e1300 AecDbZLineStair::DrawGuarBar3d+0x2c0
0x7e09d0 AecDbZLineStair::DrawSteep3d+0xbe0
0x7ddcef AecDbZLineStair::Draw3d+0x34f
真实成因:Z 型楼梯画护栏(DrawGuarBar3d)时 copyFrom 一份管道实体,
而 AecDbEwPipeBase::copyFrom 用通用的 CopyObject(inc/AecDbEntityCommon.h:331)
做"整实体 dwgOutFields/dwgInFields 往返"来拷字段,于是截面被写成临时 SAT 文件再读回来。
而且这次序列化是多余的:copyFrom 随后把所有字段又用内存级方式拷了一遍
(曲线 clone/copyFrom、截面 clone/copyFrom、XData CopyFrom、m_arUserInfs append、
标志位/图层/线型/Breaks 逐项赋值),WriteUserInterface 写的也只是 m_arUserInfs。
所以最小修改是把那句 CopyObject 去掉,并补上源为 NULL 时清空目标的分支
(目前这个"清空"是 CopyObject 顺带完成的)。
本次只做定位与记录(分析写进 pipe-section-region-performance-plan.md 并纠正了上一版的误判),
不改 copyFrom 的行为——它影响所有管系实体的复制粘贴/块内拷贝,需要单独验证。
|
2026-10-28 21:28:00 +08:00 |
|
company-desktop
|
f8d10d5a3b
|
perf: 增加截面 Region 缓存,开图恢复耗时降 56%
一次开图要做 5641 次截面恢复(22.4 s),其中大量内容重复,
而每次恢复都是"写临时 SAT 文件 + 从盘 acisIn + 删文件",单次 2.7~5.5 ms,
还要过一次杀软。本提交加一级母体缓存,命中直接 clone 返回。
新增 CoreLibrary/SectionRegionCache.{h,cpp},三个通道按 key 的正确性强度分开:
- kParam:纯参数 hash,用于 AecDbEwFitting::GetSection 的两个重载
(key = dD/dt/HLR 标志 或 W/H/dt/HLR 标志,同参数必同几何)
- kAttr:类名 + spec XData(排除编号,并跳过 Id/点/向量/矩阵/二进制类型),
只给 AecDbEwPipe 用。AecDbEwPipeBase 是很多类的基类,其余派生类实例间的
区别就是截面,不满足"同数据必同截面",所以一律走内容 hash
- kText:SAT 文本内容 hash,用于 RegionAcisInDwgInFields 的其它类
与 CoreTools::StringToRegion 的两个重载
约定:母体由缓存持有、永不外泄,Acquire/Put 都返回 clone,调用方拥有返回值;
母体不入库;每条通道 4096 条上限(AEC_SECTION_CACHE_MAX 可调),超出按 FIFO 淘汰;
AEC_SECTION_CACHE=0 可整体关闭做 A/B;卸载时 ClearAll() 释放,避免 DLL 析构顺序问题。
关键发现:SAT 文本第 2 行带存盘时间戳
16 Autodesk AutoCAD 19 ASM 219.0.0.5600 NT 24 Thu Oct 22 10:19:20 2026
同一几何存盘时间不同,整份文本就不同(采样里两份 2596 字节的文本只差
10:19:20 与 10:19:16),这正是内容 hash 命中率只有 54% 的原因,与截面无关。
因此新增 HashSatText():算 hash 时跳过第 2 行,仅当该行含 "Autodesk" 时才跳过。
计数器也改用同一口径,避免两套统计互相矛盾。
实测(big1.dwg / AutoCAD 2020 / R230 Debug,AEC_SECTION_CACHE 关 vs 开):
开图墙钟 108~113 s -> 98 s
截面恢复总耗时 22.4 s -> 9.87 s(-56%)
StringToRegion 14.05 s -> 7.73 s
RegionAcisIn 8.38 s -> 2.14 s
临时文件 6773 次/9.34MB -> 2875 次/3.61MB
命中率 text 65.5% / attr 84.1% / param 88.8%
clone 开销 命中 58us、未命中 73us(合计 0.37 s,可忽略)
顺带:
- tools/run_acad_perf.ps1 增加 -CacheOff 开关,用于同一构建上的 A/B 对比
- SectionPerfCounter 增加 AppendReport(),让缓存统计也写进同一份报告文件
- 诊断口子 AEC_SAT_SAMPLE=n 可导出未命中的 SAT 原文,用于定位这类差异
验证:R230 Debug 编译通过并部署;用脚本自动开图各跑一次(缓存关/开),
数据已写入 pipe-section-region-performance-plan.md。
尚未做:R190 编译(本机缺 v100 工具集)、Release 构建的收益复测、
开图期间 1140 次 RegionAcisout 回写(与本缓存无关,需单独立项)。
|
2026-10-28 20:41:05 +08:00 |
|
company-desktop
|
6559cdf6f5
|
docs: 记录 Phase 0 实测结果、AutoCAD 自动化实测环境与属性 key 的取舍
Phase 0 在本机跑通(big1.dwg / AutoCAD 2020 / R230 Debug,开图 108 s):
RegionTexts=5641 UniqueHashes=2597 SimulatedHits=3044 HitRate=54.0%
TempFiles=6773 TempBytes=9,341,125 WriteBacks=1140
CoreTools::StringToRegion 2559 次 14.6 s 平均 5.7 ms 最长 321 ms
EwPipe::RegionAcisIn 3082 次 8.3 s 平均 2.7 ms
EwPipe::RegionAcisout 1140 次 2.3 s 平均 2.0 ms
结论:一次开图做 5641 次 SAT 恢复,共 22.9 s(约占 108 s 的 21%)。
此前基于 CPU 采样的估算只有 550~1000 次,低估了 5~10 倍——说明采样只能算占比,
算不出调用次数,这类结论必须靠计数器。
按 54% 命中率折算,Phase 1 缓存净省约 12 s(约 11%),并少约 4000 次临时文件写盘。
同时记录三个待追查项:命中率为何只有 54%、StringToRegion 为何比 RegionAcisIn 贵一倍、
开图期间为何回写 1140 次 SAT。
文档里补充了"用管子属性做 key"的思路与风险:属性相同则截面相同,业务上成立,
但不能单独当 key——DWG 里的 SAT 文本可能来自旧版本/旧路径/手工编辑的异形截面,
属性漏一项就会静默复用错误几何。若要做,采用 L1 内容 hash + L2 属性 key 的两级结构,
L2 命中后仍比对 textHash,不一致按 miss 处理;落地前先加一路属性 key 命中率计数比较。
另外新增 tools/run_acad_perf.ps1 并在 AGENTS.md 记录本机 CAD 实测环境:
acad.exe 路径、/p SHS-ENVI2026 启动参数、测试图纸、部署目录,
以及"PowerShell 5.1 把无 BOM 的 UTF-8 当 ANSI 读"这个坑(脚本保持 ASCII)。
验证:文档与脚本;脚本语法检查通过,本次实测就是用它的等价命令跑出来的。
|
2026-10-28 19:52:18 +08:00 |
|
company-desktop
|
18aa1d1260
|
feat: 通知脚本支持 Telegram 通道
用户没有企业微信,改用 Telegram Bot。同一台机器的 api.telegram.org 需要走本地代理
(实测 127.0.0.1:7897),所以发送逻辑要显式支持代理。
- notify.json 新增 telegram 段:token / chatId / proxy
(也认环境变量 PI_NOTIFY_TELEGRAM_TOKEN、PI_NOTIFY_TELEGRAM_CHAT)
- 未配置 proxy 时回落 HTTPS_PROXY 环境变量,再回落系统(IE)代理
- 文本超过 3900 字符截断(Telegram 上限 4096)
- 自检 -Probe 同时报告 webhook 与 telegram 两个通道的结果,token 只显示前 10 位
- SKILL.md 更新通道配置说明,补上 notify.json 的完整示例与代理注意事项
实测:假 token 时返回 "(401) 未经授权",说明请求确实穿过了代理到达 api.telegram.org,
只是凭据无效;未配置时正确报告 not configured。真实推送待用户提供 bot token。
|
2026-10-28 15:55:13 +08:00 |
|
company-desktop
|
46d76317c7
|
feat: 通知脚本支持本地 webhook 配置与连通性自检
接微信/钉钉时 URL 是密钥,不能进仓库,也不适合每次都写在命令行里。
- webhook 取值顺序:-Url 参数 > 环境变量 PI_NOTIFY_WEBHOOK >
%USERPROFILE%\.pi\agent\notify.json 的 webhook 字段 > notify_webhook.txt 纯文本
(两个备用文件都在仓库之外,不会被提交)
- 新增 -Probe:只做一次连通性自检并打印结果(URL 打码,只显示前 32 位与后 8 位)
- 修正一个误判:企业微信/钉钉出错时仍返回 HTTP 200 + errcode,
原来只判断 HTTP 成功会把"密钥无效"当成发送成功。
现在解析响应体,errcode/code 非 0 一律视为失败并把原因带出来
(实测假 key 返回 errcode=93000 invalid webhook url)
验证:-Probe 在"未配置"和"配置了假 key"两种情况下都给出正确结论,
未做真实推送(还缺用户的企业微信机器人地址)。
|
2026-10-28 15:51:00 +08:00 |
|
company-desktop
|
dff08b17a3
|
feat: 需要用户决策时主动通知(桌面 toast / 决策日志 / IM 机器人)
背景:用户可能不在终端前面(远程连过来、或者走开了),agent 把问题打在终端里就停住,
人根本不知道,等于空等。
新增:
- tools/notify_decision.ps1
- Windows toast(WinRT),失败则回退到模态弹窗(120 秒自动关),并带提示音
- 追加写入 %USERPROFILE%\.pi\agent\decisions\pending.md,漏看 toast 也不丢问题
- 可选 webhook:-Url 或环境变量 PI_NOTIFY_WEBHOOK;
企业微信/钉钉用 {"msgtype":"text",...},URL 含 feishu/lark 时自动换飞书格式
- 中文必须走 -MessageFile(UTF-8 读取),避免命令行参数被控制台代码页弄乱
- 任何失败都 exit 0,通知出问题不能反过来中断 agent
- .pi/skills/notify-decision/SKILL.md:什么时候该通知、消息里必须写清"要决定什么 /
选项与代价 / 我的建议与默认动作"三件事、通道配置方式
- AGENTS.md:加一小节指向该 skill,并在仓库内 skill 表格里登记
验证:本机跑通三种调用(ASCII -Message、中文 -MessageFile、加 -NoBeep),
中文在决策日志里显示正常;toast 通过 PowerShell appId 注册,WinRT 接口检测为可用。
仅新增工具与文档,未触碰任何编译产物与源码。
|
2026-10-28 15:48:23 +08:00 |
|
company-desktop
|
e0c9e84acc
|
perf: 增加 diagsession/ETL 的 CPU 采样一键分析脚本
配合 diagsession-cpu-analysis.md 使用。之前分析图纸打开慢时,是临时在
%TEMP% 里拼了四个脚本按主题分别聚合;这次合并成一个,可以反复跑。
用法:
:: 先花 10 秒确认符号能对上(关键)
python tools\perf_diagsession.py C:\temp\open.etl --verify-only
:: 出报告
python tools\perf_diagsession.py C:\temp\open.etl --out report-cpu-hotspots.md
做的事:
1. .diagsession 是 zip,自动解出内部最大的 .etl;
2. 符号校验:拿 trace 里记录的 TimeDateStamp + SizeOfImage 去
<repo>/bin/Code2026/** 找同一份二进制,再找它旁边的 .pdb,
符号路径只加这些目录 + C:\Symbols 本地缓存,默认不联网;
这一步很关键——采集用的二进制和 bin 下的不是同一份时,
我们的函数只能显示成「模块 + RVA」,脚本会明确告警;
3. 跑一趟 xperf -a dumper,一次遍历同时算出:模块独占/包含、
函数独占、我们模块自身的耗时、出现在栈里的骨架函数、
驱动 Parasolid 的我们的函数、线程分布、主干调用链、
SQLite/Parasolid 的直接调用点、每秒时间线;
4. 输出 markdown 报告 + 原始聚合 JSON。
几个踩过的坑,已在实现里处理:
- trace 里的 EndAddr-BaseAddr 是 SizeOfImage(虚拟大小,0x1000 对齐),
不能拿去和文件大小比,否则所有模块都判成「版本不符」;
- 部分目标不写 PE 时间戳(为 0),此时退化为只比 SizeOfImage;
- 模块基址未知时不能换算 RVA,否则会算出负数;
- STL 容器名(std::_Tree<std::_Tmap_traits<...>>)一行几百字符,
报告里折叠模板参数后再输出。
被 .gitignore 的 *.py 规则排除,这里显式加了例外;
同时补上 __pycache__/ 忽略。
验证:在 report.diagsession 上跑通(PDB 匹配 28/28,22 个是我们的模块),
生成的结论已写入 diagsession-cpu-analysis.md。纯离线分析工具,不影响任何 target 的编译。
|
2026-10-28 14:47:30 +08:00 |
|