Appearance
性能与长稳诊断(V10)
性能问题最容易把 AI 带进“看哪里都能优化”的状态:换集合、换线程、上缓存、改异步、重构 UI……最后代码变了很多,真正瓶颈却没被证明。
V10 的原则是:
先定义可测症状,再找到最窄热点;一次只优化一个有证据的瓶颈,并用同一场景做前后对比。
1. 先把“慢”改写成可测现象
不要只说:
text
软件有点卡
内存越来越高
通信好像慢尽量变成:
text
采集 20 分钟后 UI 点击延迟从 <100 ms 变成约 1.5 s
每秒 500 帧时 CPU 从 12% 上升到 75%
运行 8 小时 Working Set 从 220 MB 增长到 1.4 GB 且不回落
RX 到 UI 展示延迟 P95 约 800 ms
点击 Stop 后窗口 6–10 s 无响应没有量化基线时,可以先建立一个最小基线,不急着改代码。
2. 性能任务先分成 Read-Only / Regression / Optimization
| 情况 | 推荐入口 |
|---|---|
| “为什么这里会卡?”只想查当前路径 | Read-Only Trace |
| “昨天修改以后 CPU 才变高” | RegressionFix,known-good first |
| 长期存在且已有量化热点 | Performance Optimization |
| 用户只是感觉慢,没有数据 | 先建立基线和反馈信号 |
近期性能回归不能因为发现“某方法很重”就直接判它是本次根因;仍需对比 known-good 的调用频率、数据量或实现差异。
3. 先判断瓶颈属于哪一段
常见链路:
text
Device / Input
→ Receive
→ Parse / Compute
→ Queue / State
→ UI Dispatch / Render
→ Storage / Log先找“延迟从哪一段开始明显增加”。
常见瓶颈类别
- CPU 热点:计算密集或调用次数失控;
- UI:Dispatcher 堆积、频繁 Binding、图表重绘;
- 内存:事件未解绑、集合无限增长、缓存无上限;
- GC:高频分配、大对象、频繁 Gen2;
- IO:同步写文件/数据库、Flush 太频繁、大锁;
- 通信:接收回调太重、等待 UI、队列堆积;
- 生命周期:Stop/Dispose 等待旧任务或队列排空。
4. Tight Feedback Signal:先选一个能快速复测的指标
例如:
text
UI 卡顿
→ 一段固定 5 分钟 Replay + UI update latency
通信延迟
→ 固定报文序列 + RX→Parsed→UI 时间戳
内存增长
→ 固定 30 分钟场景 + object/Working Set 曲线
停止假死
→ 固定启动/运行/停止脚本 + Stop elapsed优先使用 Fake/Replay,使每次修改都能在同一输入下比较。
真机长稳可以作为最终证据,但不应成为每个小优化的唯一反馈环。
5. WPF 卡顿先查 Dispatcher 压力
采集界面常见问题:
text
每个包
→ Dispatcher.Invoke/BeginInvoke
→ 多个 PropertyChanged
→ 图表逐点更新
→ 日志追加在高频采集下,UI 队列会越来越长。
重点检查:
- 是否每个采样包都刷新 UI;
- 是否可以 100–200 ms 合并一次展示;
- 非活动 Tab 是否仍在刷新图表;
- 是否逐点修改大
ObservableCollection; - 图表点数是否无限增长;
- 日志文本是否无限追加;
Dispatcher.Invoke是否反向阻塞接收线程。
常见顺序是:
text
先降低 UI 更新频率/粒度
→ 再处理图表构建
→ 再考虑更大架构变化不要一开始就换 UI 框架。
6. 接收线程/回调要尽快返回
通信接收路径不适合同时做:
- 文件写入;
- 数据库事务;
- 大量 JSON/CSV 格式化;
- 图表点构建;
- 同步等待 UI;
- 大量日志输出。
推荐:
text
Receive
→ copy/normalize minimal data
→ queue/channel
→ return
Parser / Worker
→ business object
UI / Storage
→ 各自消费但不要为了这个模式无条件重构现有项目。先证明当前回调确实是热点或阻塞点。
7. 存储和日志:先查“频率”和“锁”
常见性能问题:
- 每个采样都
Flush; - 多个文件写入共享一个大锁;
- UI/receive thread 同步写磁盘;
- 异常时每包打一条 Error;
- 日志内容包含大量重复堆栈或 HEX;
- CSV 每次都重新拼大量字符串。
优化前先确认数据完整性约束:
text
哪些数据绝不能丢?
允许缓存多少?
异常退出允许损失多少尾数据?
Stop 时必须排空还是可以丢弃?“更快”不能通过静默丢数据换来。
8. 队列必须有容量和背压语义
无限队列可以暂时消除接收阻塞,却把问题变成内存增长。
至少知道:
text
Capacity
ProducerRate
ConsumerRate
FullPolicy
StopPolicyFullPolicy 可能是:
- 阻塞生产;
- 丢弃旧数据;
- 丢弃新数据;
- 降采样;
- 报警;
- 根据数据类型分级。
由业务需求决定,不能由性能优化自行猜。
9. 内存增长先区分“缓存增长”与“泄漏”
Working Set 上升不自动等于内存泄漏。
先看:
- GC 后对象是否仍持续增加;
- 哪类对象增长;
- 是否有无限集合;
- 页面关闭后 ViewModel/View 是否仍被引用;
- Event/Timer 是否解绑;
- 静态集合/缓存是否有清理策略;
- 图表历史点和日志是否有限制;
- 大对象是否重复分配。
如果内存最终稳定在合理平台,可能只是缓存/运行时行为;不要看到曲线上升就先大改对象模型。
10. Stop/关闭阶段也是性能场景
“运行不卡,但点停止卡 10 秒”很常见。
追踪:
text
StopRequested
→ stop producer
→ cancel pending IO
→ complete queue
→ drain/drop according to policy
→ flush storage
→ await worker exit
→ dispose session/resource
→ UI final state记录每一步耗时,就能知道卡在:
- 设备读写取消;
- 队列排空;
- 文件 Flush;
- 锁等待;
- UI thread;
- Task 没响应 Cancellation。
详见 线程与资源生命周期。
11. 优化项按“收益 / 成本 / 风险”排序
推荐输出:
| Candidate | Evidence | ExpectedGain | Cost | Risk | Priority |
|---|---|---|---|---|---|
| UI 合并刷新 | Dispatcher queue backlog | 高 | 低 | 低 | P0 |
| 日志节流 | 45% IO time in log formatting | 中 | 低 | 低 | P0 |
| 更换图表库 | 当前库 render hotspot | 可能高 | 高 | 高 | P2 |
| 重写通信架构 | 目前无直接证据 | 未知 | 很高 | 高 | 不做 |
没有证据的“大重构能更快”只能是假设。
12. 一次只改一个主要瓶颈
推荐:
text
Baseline
→ change A
→ same scenario replay
→ compare
→ keep/revert
→ next candidate如果同时改线程模型、图表库、日志、队列和协议解析,最后很难知道哪一项真正有效,也增加回归风险。
13. 性能优化不能破坏业务语义
必须保护:
- 数据完整性;
- 顺序;
- 时间戳;
- Raw/工程值换算;
- 报警/联锁时序;
- Stop/Cancel;
- 历史格式兼容;
- 操作员可见状态。
例如把 UI 刷新从每帧改成 200 ms 一次通常可以,但如果某个安全联锁原本错误地依赖 UI 刷新触发,就必须先修正 owner,而不是只做节流。
14. 性能验证也按最小有效证据
普通性能优化通常不是“跑全量测试”解决的。
应该保留两类证据:
Correctness
- focused tests;
- Replay;
- WorkflowSmoke;
- 数据完整性检查。
Performance
- 同一输入;
- 同一时间窗;
- 同一机器/环境;
- 优化前后 CPU/内存/延迟/帧率对比。
例如:
text
Scenario: 500 frames/s replay, 10 min
Before: UI P95 latency 820 ms, CPU 68%
After: UI P95 latency 95 ms, CPU 31%
Correctness: parser replay 12000/12000, data loss 015. 不要过早宣布“性能已解决”
如果只做了 3 分钟 Replay,却问题原来是运行 8 小时后出现,正确结论是:
text
ShortScenario: Improved
LongRun: Unverified如果真机条件没到位:
text
HardwareLongRun: HardwarePending证据覆盖到哪里,结论就写到哪里。
16. 最短性能分析 Prompt
text
$host-computer-dev
这是一个性能/长稳问题。
现象:
【填写】
可量化指标:
【CPU / 内存 / UI 延迟 / RX→UI 延迟 / Stop 耗时】
复现场景:
【填写】
请先:
1. 建立最小基线;
2. 找第一个有证据的热点路径;
3. 区分 Evidence / Inference / Unknown;
4. 按收益/成本/风险排序候选;
5. 先给最小优化,不做无证据的大重构;
6. 用同一场景给出前后对比方法。17. 相关页面
一句话原则
先测再改,先解决最窄且有证据的热点;性能优化必须同时证明“更快了”和“原来的业务没有被破坏”。