news 2026/10/3 15:59:44

AI性能工程实战:从指标构建到推理训练优化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AI性能工程实战:从指标构建到推理训练优化

很多人一提到 AI 性能工程,第一反应是“调 GPU 参数”“改推理框架配置”。干过几年之后我越来越觉得,这个领域真正难的其实不是单个点的调优,而是你能不能建立起一整套从指标体系、压测方法到排障流程的工作方式。之前写过第一篇的基础内容,今天这篇继续往深了讲,把训练和推理两条链路里那些最常踩的坑、最有效的做法,以及我自己的实操经验,一次性梳理清楚。

这篇文章适合正在做 AI 应用落地、大模型推理服务、训练平台优化的人看。不管你是算法工程师、后端开发还是 SRE,只要你的工作跟“让 AI 系统跑得更快更省”有关,这里面的方法论和具体手法都能直接用。内容会偏工程实践,不聊太虚的概念,重点放在“为什么这么做”和“怎么做才不翻车”。

1. AI 系统的性能问题为什么和传统软件不一样

1.1 传统性能优化的惯性思维在哪里失效

做过后端性能优化的人通常有一套成熟打法:先压测,再看监控,QPS 上不去就扩副本,接口慢就用 profiling 找到热点函数,缓存、连接池、异步化三板斧下去基本能解决大部分问题。这套思路放到 AI 系统里,不能说完全没用,但如果你照搬过来,很容易撞墙。

最核心的差异在于,传统软件的性能瓶颈是相对分散的,而 AI 系统的性能是高度耦合的。举个具体例子,一个推荐系统里嵌了一个 CTR 模型,你单独优化模型的推理延迟到 10ms,但上游特征工程在高峰期耗时突然飙到 80ms,整个推荐接口的 P99 依然惨不忍睹。这时候你再快 10 倍模型也没用,瓶颈已经转移了。换句话说,AI 系统本身是一整个流水线,数据接入、特征处理、模型推理、后处理、业务逻辑调度,哪个环节弱,整条链路就烂在那里。

还有一个容易被忽视的点:传统性能优化追求的是“峰值能力”,比如双十一压测支持多少万 QPS;AI 系统要的是“稳定的低延迟 + 高吞吐”,而且两者往往是冲突的。你为了追求吞吐把 batch size 加大,单次推理的延迟会明显上升,用户侧的体感变差。你为了追求低延迟用很小的 batch,GPU 利用率又上不去,成本翻倍。这就是 AI 性能工程里最经典的“吞吐 vs 延迟”矛盾,所有方案设计本质上都在找这个平衡点。

1.2 AI 链路里的新瓶颈:数据搬移和资源焦虑

传统软件性能工程师盯的最多的是 CPU、内存、磁盘 IO。AI 系统里多了一个“显眼包”——GPU,但 GPU 并不是唯一需要注意的资源。我自己的体会是,AI 系统里最大的隐性瓶颈其实有两个:一个是数据搬移,一个是显存墙。

先说话数据搬移。很多训练任务看起来 GPU 利用率在 90% 以上,以为已经拉满了,但仔细拼开看计算和通信的 overlap 情况,发现大量时间花在节点间同步梯度、CPU 到 GPU 的拷贝上。数据搬移不像计算那样容易量化,而且问题往往藏得很深,比如 GPU Direct 没开、网络拓扑不对、存储带宽不达标,表现出来却是莫名其妙的“GPU 吃不满”或“训练速度忽快忽慢”。

再说显存墙。大模型的权重、优化器状态、中间激活、KV Cache 每一项都是显存大户。显存一爆,第一反应都是“调小 batch”,但这就等于让算力打折。真正可做的方向比想象中多:激活重算、梯度累积、混合精度、显存碎片整理、KV Cache 量化,每一个都值得单独去试。这也回答了我经常被问的一个问题:“显存不够怎么办?”标准答案不是无脑调小 batch,而是把显存消耗拆开,看看哪一项最大,再针对性地做优化。

2. 别只盯 GPU 利用率:AI 系统的指标体系到底怎么建

2.1 训练阶段:从“看多少卡”到“看 MFU”

训练侧最容易犯的错误是拿 GPU 利用率作为唯一指标。这个数字高不代表你的计算效率高,它只能说明 GPU 没有完全闲着。同样的模型训练,有人能用 16 张卡跑出很高的 MFU,有人用 32 张卡反而更慢,原因就在于并行策略、通信同步和数据管线这些“看不见的部分”。

我更推荐用这几个指标组合起来看训练健康度:

指标含义关注点
GPU 利用率SM 繁忙程度是否被小算子拖垮
MFU / HFU模型算力利用率真实计算效率
samples/s 或 tokens/s训练吞吐端到端效果
通信占比同步时间占总时长比例并行策略是否合理
显存占用权重+激活+梯度等是否存在浪费和 OOM 风险

训练吞吐反映了整个数据流水线到计算再到通信的最终效果,它的升降比单看 GPU 利用率更有说服力。举个我自己踩过的例子,有一次我们训练一个多模态模型,GPU 利用率看起来很正常,但 samples/s 一直不达标。后来用 PyTorch Profiler 详细排查,才发现问题出在 DataLoader 的 prefetch 不够,GPU 每个 step 要空等几百毫秒。GPU 利用率高是因为它一旦开算就全力以赴,但“算”和“等”交替进行,整体吞吐就被拉下来了。所以训练侧一定要结合“吞吐 + 利用率 + 通信时间”一起看,单看哪一个都会被误导。

2.2 推理阶段:延迟拆开看,才知道疼在哪

推理侧的指标比训练侧复杂,因为用户的真实体验是“端到端延迟”,但模型服务内部的耗时结构完全是另一回事。一个大模型推理服务如果只看接口平均耗时,基本等于什么都没看,你需要把延迟拆成几个关键段:

  • TTFT(Time To First Token):从请求进来,到把第一个 token 返回给用户的时间。聊天场景里 TTFT 直接决定“用户会觉得你卡吗”。
  • TPOT(Time Per Output Token):生成阶段每个 token 的平均耗时。它决定了后续内容的流畅度。
  • ITL(Inter-Token Latency):相邻 token 之间的实际间隔,用户打字速度的类比就是这句话。
  • 整体吞吐:一个实例每秒能生成的 token 数,或者并发输入和完成的请求数。

互联网后端常见的“P99 延迟”在这里依然适用,但需要特别小心生成式场景的分布。大模型的 decode 阶段受批大小和内存带宽影响很大,尾延迟往往比均值高出几倍,所以只看平均值容易产生“还挺快”的错觉,P95/P99 才有真正的参考意义。同时还要注意“排队时间”作为独立指标,因为高并发下大量请求在等 prefill 或等显存,用户明明模型推理很快,整体却卡顿,这种情况和模型本身关系不大,更可能是调度或 GC 策略没做好。

2.3 成本效率指标:每千 token 成本才是王道

AI 性能工程做了几年,我最大的一个感触是:绕来绕去,最终都会回到“单位成本”和“单位算力产出”这两个词上。模型效果好是一回事,能不能用可控成本稳定对外提供服务是另一回事。这就是为什么现在很多团队在推“每百万 token 的推理成本”这个指标。

你可以自己做一张简单的成本账:把一台推理机器的租用成本除以它一个小时内产出的 token 总量,得到每 token 成本。同一型号的模型,A 团队配置出来的每 token 成本可能是 B 团队的三分之一,但服务质量差别不大。差距从哪来?就是 batch 策略、KV Cache 命中率、量化方案、在线离线部署规划这些细节叠出来的。性能工程做到最后,本质是一种“用更少资源办更多事”的工程能力,指标上的体现就是成本效率。

3. 推理侧性能优化:框架、参数、量化的实操取舍

3.1 框架选型定上限,别在错误的地基上搬砖

推理框架的选型几乎是所有性能优化工作的前提。我经常跟团队说一句话:“同样的模型,换框架可能直接带来 2-5 倍吞吐提升,这是任何后续参数调优都比不了的。”现在主流的自研或开源框架里,性能表现比较好的基本集中在 vLLM、TensorRT-LLM,另外还有不少团队基于 FasterTransformer 风格的组件自研调度层。选型时除了看 benchmark 数据,更重要的是看它们和你的部署环境、模型结构、生态成熟度能不能匹配。

vLLM 的核心优势是 PagedAttention 和连续批处理(Continuous Batching),它把显存利用率提升了非常多,动态 batch 时也不容易因为显存碎片被迫降低并发。TensorRT-LLM 的优势是在 NVIDIA 硬件上做了极致的 kernel 融合和低精度优化,吞吐比 vLLM 高一点,但它对模型支持的灵活性差一些,很多自定义算子需要自己写 plugin。小模型或者需要频繁迭代的模型,用高灵活性的框架更稳妥;定制化极高的生产环境,用 TensorRT 系做深度优化更容易出效果。

我的建议是,不要把框架当作一个固定配置去用,而是要理解它的调度语义。比如 vLLM 里一个模型实例的 max_num_seqs 和 gpu_memory_utilization 两个参数,直接决定了单实例能承接的并发量和显存预留比例。调高了这俩,吞吐可能上去,但显存风险跟着来;调低了,延迟很稳但成本不划算。类似这种参数的调整,背后需要有一套压测数据来支撑,而不是拍脑袋。

3.2 KV Cache 和调度策略:被低估的两个大头

推理侧优化里,KV Cache 往往是压榨性能最大的突破口。大模型生成 token 时要把历史 token 的 key/value 缓存下来,缓存越大,显存占用越高,但命中率越高速度越快。这里就有一个“缓存分配和预留给新请求空间”的平衡问题。

连续批处理是另一个容易被低估的优化。传统 batch 是“一批请求来齐了才开始推理”,如果请求到达时间很分散,GPU 大量时间在空等。连续批处理则允许新到的请求插入当前批次,只要显存和算力允许,随时放进推理流里。这个机制对吞吐的提升非常可观,但副作用是可能引入额外的调度延迟,尤其是 prefill 阶段长序列和 decode 短序列混在一起时,必须做优先级控制。

生产环境里我还推荐用chunked prefill处理长短不一的请求。把很长的 prefill 切成小块,和 decode 阶段插在一起执行,避免一个长序列请求把 GPU 全占了导致其他请求全部卡死。这套方案对“P99 高但不清楚为什么高”的推理服务往往立竿见影。

3.3 量化和投机解码的“性价比账”

量化几乎是推理部署的必选项。从 FP16 到 INT8,推理速度通常提高 40%-80%,显存减半,效果在多数场景下可接受;到 INT4 则要看模型敏感度,有些模型精度损失小,有些则崩得厉害。AWQ、GPTQ 这些方法在 7B、13B 模型上的表现都有公开数据,但实际用下来一定要拿你自己的业务数据做离线评测,别只看 benchmark 上那个把模型烤糊才测出来的数字。

投机解码(Speculative Decoding)的思路是让一个小的草稿模型先快速生成一批候选 token,再由大模型一次性验证。这个方案在小模型和超大模型搭配的场景下效果非常好,实测里生成长文本的吞吐能提升 2-3 倍。但它也挑场景,如果业务本身的输出很短,收益会打折;如果草稿模型和大模型分布太接近,正确率太高,验证步骤反而成为拖累。所以做优化时一定要把“场景特征”纳入考量,不要拿一个方案到处套。

4. 训练侧性能工程:从静态调参走向数据驱动

4.1 训练画像:先把 GPU 吃饱这件事拆开看

训练侧优化的目标是让每张卡都“吃满”,注意这里说的吃满不是单纯的利用率高,而是计算、通信、数据供给三者的重叠度和效率同时达标。怎么判断当前瓶颈在哪?我会用 profiling 工具给训练任务做个“画像”,记录每个 step 里 GPU compute 时间、通信时间、数据加载等待时间、空闲时间的占比。

  • 如果GPU compute 时间正常,但 step 总时间很长,多半是同步通信或数据加载拖了后腿。
  • 如果GPU 利用率跳动很大,说明数据供给不稳定,DataLoader 或者预取逻辑有问题。
  • 如果通信时间占比持续偏高(比如超过 20%-30%),就要考虑模型并行策略是否需要调整了。
  • 如果显存占用中 activation 占比异常大,说明 batch 太大或者没有开启激活重算。

拿现实里的例子说,我们有一次训练 70B 模型,从 32 卡扩到 64 卡以后,理论上训练速度应该接近翻倍,结果只提升了 40%。用 Nsight Compute 和 PyTorch Profiler 一看,通信占比从 15% 涨到 35%,Allreduce 把时间吃掉了。后来把数据并行度降了,改为张量并行结合流水线并行,通信耗时降下来,整体吞吐明显回升。这个案例说明,训练优化要跟着数据走,不是简单加卡就能解决问题。

4.2 数据管线和数据供给:最容易被忽视的隐藏瓶颈

训练性能的问题里,数据管线的坑最隐蔽,因为它不会直接把任务打挂,只会让效率一点点漏掉。GPU 计算速度越快,数据供给的瓶颈就越突出。很多人以为 num_workers 调大就完事了,其实还要看是否开启 persistent_workers、prefetch_factor 是否匹配、数据是否落在本地 NVMe,甚至 CPU 和 GPU 之间拷贝的 pinned memory 设置。这些细节加起来,一个 step 的等待时间可能从 10ms 变成 100ms,整体训练时长拉长一倍都不稀奇。

我的做法是把数据读取链路当成一个独立服务来设计。比如训练图像模型时,先把图片做批量预处理并缓存成 TFRecord/LMDB 格式;训练文本模型时,提前把 tokenize 结果落盘,避免每个 epoch 都在重复做 tokenize。数据增强如果太重,优先放到 GPU 上做或者并行化。这样即使数据量大,供给端也能稳定跟上 GPU 的消耗。

4.3 并行策略:不只是“模型太大放不下”的选择题

很多团队选择模型并行是因为“单卡放不下”,但实际上并行策略的选择对性能的影响远不止显存这一点。数据并行最简单,但对大模型来说通信量太大;张量并行能减少通信量,但 GPU 数量需要成组约束,物理拓扑很关键;流水线并行则可以减少卡间通信,但会引入“流水线气泡”,需要靠 micro-batch 调度尽量填满。

这几个策略通常不是二选一,而是组合使用。一个 70B 模型在机间用数据并行,机内用张量并行,层间再用流水线并行,这种组合在工程上非常常见。但组合多了,通信模式就复杂了,任何一块配置不合理,都会导致算力浪费。我个人觉得最佳实践是“先跑通,再 profiling,再调整”,不要一开始就信某篇博客的推荐配置,不同硬件拓扑和网络环境下,最优组合可能完全不一样。

5. 实战排障:一次推理服务的性能问题完整复盘

5.1 排查前的准备:性能基线是唯一可信的依据

先讲一个我自己的习惯:接到任何性能问题反馈,不急着改配置,先做三件事。第一,确认监控数据是完整可信的,至少要有时间戳对齐的 QPS、延迟分布、GPU 利用率、显存、网络 IO 和日志;第二,和发布历史对照,看性能恶化是从哪个版本开始的,这个信息往往直接指向嫌疑代码;第三,用压测工具在当前环境跑出一组性能基线,作为后续改动前后的对比依据。

没跑基线就动手,是最常见的翻车方式。性能问题的表现往往是多个变量共同作用的结果,你改了一个参数,症状可能没消失,只是因为另一个瓶颈被掩盖了。有基线才有资格谈控制变量。我建议每个 AI 服务在上线前就把压测镜像和压测流量准备好,版本更新自动跑一轮,把性能变化作为发布准入的一部分。

5.2 常见性能异常速查表

下面这张表是我在实际排查里经常对照的,不一定能覆盖所有情况,但能帮你快速定位大头问题:

现象优先怀疑方向快速验证手段
GPU 利用率低,CPU 忙DataLoader/预处理线程观察 CPU 占用和 step 等待时间
GPU 利用率低,网络忙多机通信瓶颈检查 NCCL 日志和网络吞吐
TTFT 高,prefill 慢KV Cache 分配/显存碎片看 prefill 耗时占比
TPOT 高,输出卡顿decode 未优化、量化不合适对比不同 batch 下的 TPOT
并发高时延迟整体飙升调度器排队/显存不足看 queue 长度和显存水位
显存 OOMbatch/激活/KV Cache 分配过大逐项估算显存消耗
训练 loss 震荡严重学习率/数据顺序/梯度同步问题对比相同数据下的基线

这张表的逻辑是“现象 → 方向 → 验证”,而不是“现象 → 结论”。因为同一现象可能对应多种原因,直接把结论拍死很容易误判。比如 GPU 利用率低,可能因为是数据加载,也可能是因为算子本身太小、并行度不够。所以先列方向再逐个验证,是稳定不翻车的排障姿态。

5.3 一次真实案例:P95 延迟突增背后的真相

有一回我们上线了一个大模型聊天服务,压测下来的平均延迟和 P50 都很好,唯独 P95 一直在 3-4 秒之间横跳。从监控看,GPU 利用率并不高,显存也够,看起来一切都正常。我们一开始怀疑是偶发 GC 或网络抖动,后来加了分阶段埋点才发现,prefill 和 decode 的时间分布极不均匀。

原因其实很典型:长 prompt 的请求和短 prompt 的请求混在同一个连续 batch 里,每次新请求的 prefill 都会抢占 GPU,把原来正在进行的 decode 任务打断,后续 token 生成全部往后推。表现上就是 P50 很漂亮,P95 被这些“插队”的长请求拖得很惨。最后我们开启了 chunked prefill,同时限制了单个 prefill 请求占用的算力配额,让新请求插入时的冲击变得平滑。改完后 P95 从 3.5 秒降到 1.2 秒,总吞吐还略有上涨。

这个案例特别能说明一个道理:性能问题往往不是“某段代码写慢了”,而是“调度策略和请求特征不匹配”。没有端到端的延迟分解,你根本不知道 P95 的锅到底该谁背。

6. 性能工程落地:把“个体调优”变成“团队能力”

6.1 性能平台和可观测性:别等事故再找原因

AI 性能工程做了几年,我最大的感触是“单点英雄”模式在长期维护阶段根本扛不住。今天 A 调好了,明天 B 上线一个版本把你调好的参数覆盖了,后天模型微调后延迟特征变了,你又得重新从头摸一遍。要解决这个问题,必须把性能优化沉淀成流程和平台。

至少需要两层基础设施:一层是监控和 trace,能看清每个请求在模型服务内部的耗时分解;第二层是自动化的压测和基准回归,让每次模型更新或配置变更都有性能数据。有了这两个基础,性能优化的效率会完全不一样。像 vLLM 提供/metrics接口输出 TTFT 和 TPOT 这些关键指标,很多团队还嫌不够,会自己在服务层和网关层再做 trace。要真正定位到“哪个环节拖慢了 P99”,没有这种贯穿链路的数据是做不到的。

6.2 把性能准入前置到发布流程里

我再补充一个很管用的方法论:把性能压测前置,和 CI 流程绑定。模型或者推理框架一有变化,就在一个标准的压测环境里自动跑一套基准,把 TTFT、TPOT、吞吐、显存峰值和已有的性能阈值对比,不经机械的汇报流程,自然地在发版前发现性能退化。这听起来会增加工作量,但实际维护了一套压测脚本后,纯自动化跑一次也就十几分钟,比起线上事故后半夜排查的代价小太多了。

具体操作上,性能用例要和真实流量特征匹配,不能只压一个“固定 prompt 长度”。我会建议你从线上收集一部分真实请求的 prompt 长度分布和并发模式,做成回放压测集。这样测出来的数据才不会失真。做性能准入的团队还会维护一个“历史基线库”,每轮压测都自动对比,偏移超过阈值就直接阻断发布,性能出问题的人在发布前就知道,而不是等用户来投诉。

6.3 我的实操心得:一次只改一个变量

最后分享一个我自己坚持了很久的小习惯,也算给新手的一个建议:做性能优化,一次只改一个变量。这话听起来特别简单,但我见过太多人一次性改了 batch、量化、框架版本、并行策略,结果性能有变化却根本分不清是哪个起了作用。

我会在本子上记下每次实验的改动点、期望效果、实际数据和基线对比。这种笨办法反而最快,因为每次实验的结论都是干净的,不会滋生“我明明改了 A 和 B,效果反而差了”这种说不清的事故。等实验积累多了,你手里就有了一张“什么场景对应什么优化”的决策表,后面再做类似任务就能直接抄自己的作业。

性能工程是一个没有终点的过程,模型在迭代,硬件在换代,流量在变化,你永远不可能调出“完美”的配置。唯一能依靠的,就是这套可量化、可复现、可持续迭代的方法。这套方法积累了你的团队、你的系统、你的场景专属数据,这才是性能工程真正有价值的资产。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 15:59:22

RRSI详解:用正则化约束与Harness框架驯服Agent递归自我改进

最近在追谷歌新放出来的那篇关于自我改进的论文时,看到 RRSI 这个概念——Regulated Recursive Self-Improvement,中文可以直译为“带正则化约束的递归自我改进”。论文核心是解决一个困扰很多人很久的问题:让智能体自己改自己这件事&#xf…

作者头像 李华
网站建设 2026/10/3 15:57:51

Thingsboard Gateway接入OPC-UA:从节点映射到遥测上报的完整配置

简介:面向工业物联网开发者与Thingsboard Gateway使用者的OPC-UA接入示例文档,核心解决如何将OPC-UA设备数据经网关稳定上传至云端。内容以KEPServerEX6模拟OPC-UA服务端为主线,覆盖安装配置、通道与设备创建、Tag标记设置,并结合…

作者头像 李华
网站建设 2026/10/3 15:57:00

DeepSeek Harness与Pi如何分工:从编排到执行的AI工作流实践指南

DeepSeek Harness 和 Pi 这两个名字,最近在我眼前出现的频率实在太高了。不仅是技术群里有人问,连搜索热度都一路走高,甚至已经有人在比较:装了 DeepSeek Harness,还有必要装 Pi 吗?这两个能不能二选一&…

作者头像 李华
网站建设 2026/10/3 15:52:26

Claude Code 九月更新深度解析:AGENTS.md、长任务暂停恢复与插件管理实战

1. 这次九月更新到底改了什么:从“能用”到“好用”的分水岭 九月份这波 Claude Code 的更新,我第一时间在自己的主力开发机上跑了一遍。说实话,之前我对它的定位一直是“终端里能聊两句的编码助手”,但这次更新之后,它…

作者头像 李华
网站建设 2026/10/3 15:50:47

MATLAB fdesign滤波器设计:规格与算法解耦,统一接口高效实现

做信号处理的同学应该都有过这种经历:想换个滤波器类型,得去翻半天 help 文档,因为butter、cheby1、cheby2、ellip这套经典函数的语法和参数单位各不相同,今天写.m脚本时还记得通带纹波怎么传,明天一换算法又得重新查一…

作者头像 李华
网站建设 2026/10/3 15:50:46

RK806S PMIC调试全攻略:寄存器配置、上电时序与待机功耗排查

做硬件调试这些年,我越来越认同一句话:电源管理芯片调好了,板子就成功了一半;调不好,CPU、DDR、外设全都会用各种奇怪的方式教你做人。RK806S 是 RK 平台方案里非常常见的一颗 PMIC,在 RK3588、RK3568 这类…

作者头像 李华