性能优化让 AI 参与到哪一步:它能读火焰图吗,什么必须你自己测

2026-07-29

数据截至 2026-07,各产品的额度与报错口径以官方最新说明为准。

多数人在性能优化上把 AI 用错了位置:把它当成”诊断者”,让它看几段代码就说哪里慢,然后照着改。但性能问题的本质是资源在真实负载下的分布,这个分布只存在于运行时,不存在于源码里。模型读代码只能给出”哪些写法通常会慢”的先验,那是猜测不是诊断。真正的分工是:测量必须你来,解释可以一起做,改法它能给一堆,验收必须回到你的测量上。跳过测量直接采纳建议,最常见的结局是改了三处、系统还是那么慢,而且你已经说不清哪一处改动是白改的。

这也是本篇和站内两篇相邻文章的分工:改完功能接口变慢讲的是代码评审阶段怎么把退化拦在合并之前,内存持续增长讲的是长驻进程内存不回落这一类具体故障;而本篇不针对某一种故障,只按工程环节切——性能优化从测量到验收有五个环节,逐个说清哪一环可以交给 AI、哪一环交出去就会出事。

一、把性能优化拆成五个环节,先定清楚交接面

不拆环节就没法谈”AI 能干到哪”。一次完整的性能优化,动作顺序基本是固定的:

  1. 立项:确定要优化什么指标、优化到什么程度算完。
  2. 测量:在可复现的环境里跑出基线数据,拿到剖析结果。
  3. 定位:从数据里找出真正吃掉资源的那一段。
  4. 改动:设计并实现优化方案。
  5. 验收:用同一套测量方法确认改动生效,且没有引入正确性问题。

这五环里,AI 的能力分布极不均匀。第 4 环它最强,第 3 环它能当很好的副手,第 1、2、5 环它基本帮不上,而且越装作能帮越危险。

原因不复杂。第 4 环模式化强——批量化改写、加缓存层、同步改并发、把 O(n²) 的匹配换成哈希表,都是有标准解法的题,模型写得又快又稳。第 1、2、5 环依赖你的系统上下文和真实流量特征:什么指标对业务重要、压测的并发模型怎么设、看均值还是分位数、多大波动算噪声——这些没有通用答案,模型只能给你一段听着完整的套话。

所以交接面这样画:你负责把”事实”生产出来并保管,AI 负责在事实之上做解释和改写。 事实包括基线数据、剖析产物、验收结果。事实是你产的,模型说错了你能立刻发现;一旦事实也让它编(“你估计一下这个函数大概占多少时间”),后面全是空中楼阁。

二、火焰图能不能给 AI 读:能,但得换个形态

这是被问得最多的一个问题,答案分三层。

第一层:直接甩截图,效果最差。 火焰图是 SVG,截成图给模型,它能认出几个宽帧上的函数名,但容易读错三件事——把纵轴当成时间轴(纵轴是调用栈深度,横轴才是采样占比,且横轴顺序通常按字母排,不代表执行先后);窄帧上的文字在截图里根本渲染不出来,模型会顺着上下文把函数名”补”出来,那就是幻觉;它读不出具体占比,只能目测宽窄,给你一个你没法核对的模糊判断。

第二层:给折叠栈文本,效果好得多。 火焰图的原始输入本来就是文本——每行一条调用栈加一个采样计数。这个形态模型能真正处理:排序、聚合、按模块归类、找出反复出现的调用路径。以 Linux 上的 perf 为例:

# 采集:-g 打开调用栈,采样目标进程
perf record -g -p <pid> -- sleep 30
# 展开成文本
perf script > out.perf

拿到 out.perf 之后再用栈折叠工具转成”每行一栈 + 计数”的形态,最后才渲染成 SVG。给模型的应该是折叠后的那份文本,而不是最后那张图。 Python 侧同理,cProfile 的产物用 pstats 排序输出就是纯文本:

import pstats
p = pstats.Stats("out.prof")
p.sort_stats("cumulative").print_stats(40)

把这段输出贴给模型,让它按调用链归因,比给图靠谱一个量级。

第三层:它能读出什么,读不出什么。 能读出的是结构性线索:哪个模块的栈反复出现、哪条路径的入口在你的业务代码里、哪些帧属于框架和运行时不该动。读不出的是因果——采样占比高不代表那里是瓶颈。三个典型反例:等待型开销(线程阻塞在 IO 上)在 on-CPU 火焰图里几乎不显示,得另做 off-CPU 分析;被内联的调用在栈里归属会漂移;采样窗口本身没覆盖到问题时段,图上一片祥和,问题出在你没采到的那一段。

这三条是模型系统性的盲区,它只能看你给的那份数据,没法反问”这份数据是怎么采的”。所以每次把剖析结果交出去之前,你自己先答一遍:采样窗口是什么时候?当时负载什么样?包不包含等待?答不上来就先重采。

三、现象到成因的判别表

下面这张表覆盖工程里最常撞见的几类性能现象。用法是先对现象,再照”怎么验证”那一列动手,验证通过了再做处置——不要跳过验证直接照处置动作改。

现象大概率成因怎么验证处置动作
CPU 打满,火焰图里业务函数占大头算法复杂度或重复计算把输入规模减半再跑,看耗时怎么变:大致也减半说明是每条数据的固定开销偏大(重复计算、无谓的对象创建),降到远不足一半说明复杂度超线性,是算法问题交给 AI 做算法改写,你负责补等价性测试
CPU 不高但响应慢,栈大多停在等待下游 IO 或锁竞争做 off-CPU 分析;或在关键段两侧打时间戳,看时间去哪了先量下游延迟分布,再决定并发化还是加缓存
单次快、并发上来就崩连接池、线程池或锁的容量限制逐级抬并发画出吞吐曲线,看拐点在哪调整池化参数并复测;不要先改业务逻辑
冷启动或首次请求特别慢懒加载、JIT 预热、连接首次建立连续请求同一接口,比较第一次与后续的差加预热;把首次开销移出请求路径
平均值正常,尾部延迟很高GC 停顿、周期任务抢占、批量作业干扰看分位数而非均值;把延迟按时间画出来找周期性错峰或限流;GC 相关的先看内存曲线
越跑越慢,重启就好资源累积未释放观察内存与句柄数是否单调上升内存持续增长那条线单独排查
本地测着很快,线上就慢环境、数据量或网络拓扑不一致对齐数据规模与配置再复测本地好线上挂的环境对齐清单

这张表可以整段贴给模型当判别框架,让它对着你的现象描述帮你缩范围。但”怎么验证”那一列的动作必须你自己执行,它执行不了,也不该替你想当然地跳过。

四、必须你自己定的三件事

有三个决定,交给模型就等于放弃了这次优化的可信度。

第一件:基线怎么测。 跑几轮、丢不丢首轮、用什么机器、测时有没有别的负载——这些决定了你的数字有没有意义。最简单的接口延迟基线不需要复杂工具,curl 自带的耗时字段就够用:

curl -o /dev/null -s -w "dns=%{time_namelookup} connect=%{time_connect} ttfb=%{time_starttransfer} total=%{time_total}\n" https://example.com/api/x

跑十几轮看分布,比跑一轮看一个数强得多。关键是同一套命令、同一台机器,改动前后各跑一遍——你要的是可比,不是精确。

第二件:负载模型长什么样。 真实流量的请求分布、参数取值分布、缓存命中情况,和你随手造的压测数据往往差很远。模型不知道你的用户长什么样,你不说清楚,它给的压测脚本多半是参数均匀随机取值,结论可能和线上相反——你的热点 key 集中度高,均匀随机会把缓存命中率压到不真实的低位。这一步只能你来。

第三件:验收口径。 优化到什么程度算完,是业务决定不是技术决定。你不定,模型会替你定一个听着漂亮的目标,然后你为了够到它做过度优化,把代码搞复杂。定口径时把回滚条件一起定了——达不到就退回去,这是止损的前提。

还有改动范围:性能优化最容易失控的地方,是模型顺手把周边代码一起”优化”了。这类越界的控制方法见改动范围失控,性能场景下尤其要守——一次只改一处,改完就测。

五、什么情况下别再折腾了

优化是有边际的,下面几条是我用的止损线,撞上任何一条就停。

改了三轮、指标没有可复现的改善。 注意是”可复现”——单次跑赢不算,改动前后各跑十几轮、分布上没分开就是没效果。三轮还在原地,说明成因假设错了,该回到测量重采一份剖析数据,而不是再问模型一遍。

改动开始伤害可读性和正确性边界。 当方案需要引入手写缓存失效逻辑、跨模块共享可变状态、绕过既有事务边界时,先停。这类改动的长期成本经常高于省下的那点延迟,而且引入的 bug 不会立刻出现——测试全绿上线炸,机制见测试绿了线上还是报错

瓶颈不在你能改的范围内。 剖析结果指向第三方服务、基础设施或运行时本身时,代码层的优化空间通常很小。该换路:加缓存挡掉调用、改异步不阻塞主链路,或者把它升级成容量与架构的讨论。

回滚点要在开工前就留好。 性能优化的分支尽量小、尽量独立,别和功能改动混在一个提交里。判断的锚点是:出问题的时候,你能不能只回退性能改动而不影响功能。

# 性能改动单独成支,改动前打一个可回退的锚点
git switch -c perf/query-batch
git tag perf-baseline-before

真要回退时,git revert <commit>reset 安全,因为它保留历史、也不会让协作者的分支错乱。

六、避坑清单

坑一:拿模型的”估算”当数据。 会踩是因为它答得太顺——你问”这个函数大概占多少耗时”,它会给你一个具体百分比,语气笃定。怎么避:定一条硬规矩,凡是数字必须有产出它的命令。模型给的任何量化结论,你都要能指着某条命令的输出说”这来自这里”。

坑二:只改不测,或者测的时候环境变了。 会踩是因为改完那一刻你已经相信自己改对了,测量变成走过场。怎么避:把测量命令固化成一个脚本,改动前后各跑一次,输出存成两个文件放一起比。人为的心理偏差挡不住,流程能挡住。

坑三:一次改多处。 会踩是因为模型一口气给了五条建议,条条有理,你顺手全采纳了。结果指标动了,但你不知道是哪条起的作用,也不知道有没有哪条其实是负优化被别的抵消了。怎么避:一次一条,测完再下一条。慢,但你最后知道自己在改什么。

坑四:优化了非热点路径。 会踩是因为热点路径往往不好改,而边角代码改起来很爽,模型也乐意配合你改。怎么避:动手前先看剖析结果里的占比排序,占比小的直接不碰——把一段占比很小的代码优化掉一半,整体几乎没变化。

坑五:把剖析开销算进结果里。 会踩是因为剖析工具本身有成本,插桩式剖析器会让被测代码显著变慢,此时看到的耗时分布是被扭曲的。怎么避:定位阶段用采样式剖析看分布,验收阶段关掉所有剖析、用干净环境测真实耗时——这两件事用的不是同一套数据。

坑六:用测试环境的数据量下结论。 会踩是因为测试库数据少,全表扫描和索引查询在小表上耗时几乎一样,慢查询根本暴露不出来。怎么避:涉及数据访问的优化,验证必须在接近真实规模的数据集上做,哪怕造一批脱敏数据。

坑七:改完只测性能不测正确性。 会踩是因为注意力全在指标上。缓存、并发、批量化这三类改法都会改变执行语义——缓存带来一致性窗口,并发带来顺序不确定,批量化会改变错误处理边界。怎么避:每次性能改动都补一条针对语义变化的测试,而不只是看接口还通不通。

收束

把这件事压成一句话:AI 在性能优化里的位置是”数据之后、验收之前”。 数据你产,验收你判,中间的解释和改写它可以大量承担,效率提升是实打实的。顺序反过来——先听建议再补数据——就是在给自己制造一堆无法归因的改动。

开工前过一遍这份自检:

  • 要优化的指标和”多少算完”写下来了吗?
  • 基线跑了几轮,命令固化了吗?改动前后能用同一条命令比吗?
  • 交给模型的是折叠栈或剖析文本,而不是截图吗?
  • 你能说清这份采样是什么时候、什么负载、包不包含等待时间吗?
  • 这一轮只改了一处吗?
  • 性能改动和功能改动分开在不同提交里了吗?
  • 止损线和回滚条件是开工前定的,还是现在临时想的?

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。