Skip to content

性能与长稳诊断(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
StopPolicy

FullPolicy 可能是:

  • 阻塞生产;
  • 丢弃旧数据;
  • 丢弃新数据;
  • 降采样;
  • 报警;
  • 根据数据类型分级。

由业务需求决定,不能由性能优化自行猜。

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. 优化项按“收益 / 成本 / 风险”排序

推荐输出:

CandidateEvidenceExpectedGainCostRiskPriority
UI 合并刷新Dispatcher queue backlogP0
日志节流45% IO time in log formattingP0
更换图表库当前库 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 0

15. 不要过早宣布“性能已解决”

如果只做了 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. 相关页面

一句话原则

先测再改,先解决最窄且有证据的热点;性能优化必须同时证明“更快了”和“原来的业务没有被破坏”。

别来无恙 · C# 上位机 AI 实战站 · 从零到交付 · QQ 群:1016188499