.NET 性能回归排查应从可重复的工作负载开始,再用分配、CPU、锁等待和数据库耗时把问题缩到具体路径。升级运行时后变快或变慢都不能只靠一次压测下结论。机器型号、运行模式、预热状态、数据量和并发度不同,结果可能完全不同。
先确认慢的是哪一种时间
请求变慢可能来自 CPU 计算、垃圾回收、线程池排队、锁竞争、磁盘、网络或下游服务。先观察端到端延迟和吞吐,再拆分应用内部阶段。若只测一个方法,可能看不到序列化和连接池;若只看接口总耗时,又很难知道代码该改哪里。
如何建立可信基准
基准需要固定输入规模和运行环境。JIT 编译与缓存会影响前几次执行,测试时要区分预热后结果与冷启动结果。不要把调试构建、笔记本省电模式和线上容器的数据混在一起比较。
- 记录运行时版本、提交版本和机器配置。
- 用代表性输入覆盖常见与极端路径。
- 多次运行并保留分布,而非只保存最低值。
- 同时记录耗时、吞吐、分配字节数和 GC 次数。
当改动只减少了几微秒,却增加大量复杂代码时,也要评估它是否值得长期维护。
分配为何常常隐藏问题
短命对象大量创建时,单次请求看起来很快,持续负载下却会提高 GC 压力。字符串拼接、装箱、闭包捕获和临时集合都可能形成额外分配。性能分析器能显示分配热点,但修复前要确认对象确实位于高频路径。把所有对象都改成池化,可能引入生命周期和并发错误。
线程池与异步路径
异步代码不会自动提高吞吐。同步阻塞、长时间 CPU 任务和错误的锁使用会让线程池排队。排查时观察队列长度、活跃线程和等待位置。对于 CPU 密集任务,限制并发或分配专用执行策略通常比无限增加请求并发更可控。
升级运行时后的验证
运行时新版本可能改变 JIT、集合实现和垃圾回收行为。应用应在接近生产的配置下跑回归套件,并比较关键接口的尾延迟。出现差异时,用采样和跟踪确认是代码生成、分配还是外部依赖导致,避免凭版本号给原因下结论。
FAQ
只看平均响应时间够吗
不够。平均值会掩盖少量很慢的请求,P95 与 P99 更能反映用户遇到的卡顿。
性能分析应在线上直接跑吗
优先在可复现环境进行。线上采样需要控制开销、脱敏数据并设置明确的时间窗口。
修复后怎样避免再次回归
把关键基准放进持续集成的趋势记录中,设置合理阈值而非对单次波动报警。发布后继续观察真实请求的延迟分位、错误率和资源使用。基准变慢时先复跑确认环境,再查看变更是否改变了输入规模或业务逻辑。性能门禁要服务用户体验,不能逼迫开发者为了数字牺牲可读性。