资讯动态

AI系统性能工程实战:延迟、吞吐、显存与算力利用率优化指南

发布时间:2026/10/5 9:10:07 来源:尧图企业网站定制
1. 为什么“性能”在 AI 系统里变成了一个全新的命题过去做后端性能优化脑子里装的都是 QPS、P99 延迟、连接池大小、GC 停顿这些词。一套服务压测跑下来瓶颈基本落在数据库、网络 IO 或者锁竞争上优化路径清晰工具链成熟。但把这套经验直接搬到 AI 系统上你会发现很多地方对不上号——不是工具不好用而是问题的性质变了。AI 系统的性能工程核心矛盾从“请求-响应”的确定性链路变成了“算力-显存-吞吐”之间的动态博弈。一个推理服务你盯着它的接口延迟看可能一切正常但 GPU 利用率只有 30%显存却快爆了batch size 稍微调大一点就 OOM。这种“接口指标好看、底层资源浪费”的状态是传统性能工程很少遇到的。更麻烦的是AI 系统的性能表现高度依赖输入——同样一个模型处理 10 个 token 的短请求和处理 4000 个 token 的长请求延迟能差出两个数量级而这两种请求在真实流量里往往是混在一起的。所以我在这个系列的第一篇里想先把“AI 系统性能工程”这件事的边界划清楚。它不是一个单纯的调参问题也不是装个监控面板就能解决的事。它要求你同时理解模型的计算特性、推理框架的调度逻辑、硬件的资源模型以及业务流量的分布规律。这四样东西缺一个你的优化就会变成盲人摸象。这篇文章适合两类人看一类是已经有一定后端或运维基础正在往 AI 基础设施方向转的工程师另一类是已经在做 AI 应用但发现“能跑”和“跑得好”之间差距巨大的开发者。我会尽量把原理讲透同时给出可以直接上手验证的操作路径不堆砌术语也不回避那些实际踩过的坑。2. 拆解 AI 系统性能的四个核心维度2.1 延迟不只是“快慢”而是分层的在 AI 系统里谈延迟第一件事是分清你谈的是哪一层。最粗的划分是端到端延迟和模型推理延迟。端到端延迟包含网络传输、排队、预处理、推理、后处理、返回而模型推理延迟只是其中一段。很多人优化了半天模型结果发现瓶颈在预处理的数据拷贝上这种事太常见了。再往细里分推理延迟本身又可以分为首 token 延迟和后续 token 延迟。这两个指标在流式输出场景下意义完全不同。首 token 延迟决定了用户“感觉等了多久才开始有反应”后续 token 延迟决定了“输出得顺不顺”。一个对话应用如果首 token 延迟 2 秒、后续每 token 50 毫秒用户会觉得“开始慢但后面还行”反过来首 token 200 毫秒、后续每 token 300 毫秒用户会觉得“一开始挺快怎么越写越卡”。实测中我习惯用这样一个表格来定位延迟问题的归属指标典型关注点常见瓶颈端到端延迟用户体验网络、排队、预处理首 token 延迟交互感知调度排队、KV Cache 初始化后续 token 延迟输出流畅度显存带宽、batch 竞争批处理延迟离线任务算力利用率、IO这张表的价值在于当你拿到一个“慢”的反馈时能快速判断该往哪个方向查而不是一上来就怀疑模型本身。2.2 吞吐batch 不是越大越好吞吐量的直觉是“一次多处理一些总吞吐就上去了”。这个直觉在 CPU 时代基本成立但在 GPU 上有个前提你的显存够用而且计算单元没有被打满。实际的情况是batch size 增大到某个点之后吞吐增长会明显放缓而延迟会快速上升。这个拐点取决于模型大小、序列长度分布和硬件的显存带宽。更关键的是AI 系统的吞吐往往不是单一数字。你要区分token 吞吐和请求吞吐。一个系统可能每秒处理 100 个请求但每个请求只有 20 个 token另一个系统每秒只处理 10 个请求但每个请求 2000 个 token。前者的请求吞吐高后者的 token 吞吐高。如果你的业务是长文本生成盯着请求吞吐看就会严重误判系统能力。我在实际项目里会同时记录这两个指标并且按序列长度分桶统计。因为混合流量下平均值会骗人——短请求把请求吞吐拉高了长请求把 token 吞吐拉高了两个数字都好看但用户体验可能一塌糊涂。2.3 显存最容易被低估的约束显存是 AI 系统性能工程里最硬的约束。算力不够可以等显存不够直接 OOM服务就挂了。显存的占用主要分三块模型权重、KV Cache、中间激活值。模型权重是固定的KV Cache 和激活值则随 batch size 和序列长度动态变化。这里有个很多人踩过的坑用短序列压测调出来的 batch size上线后遇到长序列请求直接爆显存。原因是 KV Cache 的大小和序列长度成正比序列翻倍KV Cache 翻倍而你可能只留了 20% 的余量。所以显存规划必须按最长序列来算而不是按平均序列。一个粗略的 KV Cache 估算公式是KV Cache 大小 ≈ 2 × 层数 × 注意力头数 × 头维度 × 序列长度 × batch size × 精度字节数这个公式不用背但你要知道它和哪些变量相关。当你发现显存吃紧时能动的杠杆无非是减小 batch size、缩短最大序列长度、用量化降低精度、或者用 PagedAttention 这类技术减少碎片。2.4 算力利用率GPU 到底在忙什么GPU 利用率是个容易被误读的指标。nvidia-smi里显示的利用率很多时候反映的是“有 kernel 在跑”而不是“计算单元被高效利用”。一个显存带宽受限的操作GPU 利用率可能显示 90%但实际算力只用了 20%。这就是所谓的内存墙问题。判断算力是否真的被用好要看更细的指标比如 SM 占用率、Tensor Core 活跃度、显存带宽利用率。在推理场景下decode 阶段通常是显存带宽受限prefill 阶段更偏计算受限。这两个阶段的优化策略完全不同decode 阶段要减少显存访问prefill 阶段要提升计算并行度。理解这四个维度的相互关系是后面所有优化工作的基础。它们不是独立的调 batch size 会影响延迟和显存调量化会影响显存和算力利用率牵一发动全身。3. 推理框架选型别只看 benchmark 排名3.1 选型的真正标准是什么网上有很多推理框架的 benchmark 对比吞吐量数字一个比一个漂亮。但我在实际选型时第一件事不是看这些数字而是问三个问题我的模型结构是什么、我的流量分布是什么、我的团队能维护什么。模型结构决定了框架的适配程度。Transformer 类模型在主流框架上都有深度优化但如果你用的是 MoE、多模态或者自定义算子较多的模型很多优化就用不上了。流量分布决定了你该优化 prefill 还是 decode该不该开连续批处理。团队能力决定了你选一个开箱即用的方案还是选一个需要深度定制的方案。我见过不少团队跟风选了某个“性能最强”的框架结果因为不熟悉其调度机制配置没调对实际性能还不如默认配置的简单方案。框架的性能上限和你能达到的性能是两回事。3.2 连续批处理吞吐提升的关键机制连续批处理是这几年推理框架里最重要的工程进步之一。传统批处理要等一个 batch 凑齐了才开始算算完才能接下一批。连续批处理则是在每个 decode step 都重新组 batch已经完成的请求退出新来的请求插入。这样 GPU 几乎不会空转吞吐提升非常明显。但连续批处理不是免费的。它增加了调度的复杂度对显存管理要求更高而且在请求长度差异很大时调度开销会上升。实测中如果请求长度比较均匀连续批处理的收益最大如果长短请求混杂严重收益会打折扣甚至因为频繁的显存分配释放导致碎片化。提示开启连续批处理前先确认你的框架是否支持 PagedAttention 或类似的分页显存管理。没有分页管理的话连续批处理很容易把显存搞碎。3.3 量化精度换性能的边界在哪量化是降低显存占用、提升吞吐的常用手段。从 FP16 到 INT8显存直接减半理论上吞吐也能提升。但量化的坑在于精度损失不是均匀分布的。有些模型对量化很敏感INT8 之后输出质量明显下降有些模型则几乎无感。我的经验是量化一定要做任务级评估不能只看 perplexity 这种指标。因为 perplexity 变化很小不代表你的具体任务表现没退化。比如一个代码生成模型perplexity 只涨了 0.1但生成的代码通过率可能掉了 5 个点。这种退化在通用指标上看不出来只有跑真实任务才能发现。另外量化对延迟的影响要看具体实现。weight-only 量化主要省显存对计算速度提升有限weightactivation 量化才能真正加速计算但精度风险也更大。选哪种取决于你的瓶颈是显存还是算力。4. 从压测到上线一套可复现的性能验证流程4.1 压测数据要模拟真实分布很多团队的压测用的是固定长度的请求比如全部 512 token。这样压出来的数字很好看但上线后完全不是那么回事。真实流量的序列长度分布通常是长尾的大部分请求短少数请求很长。这个长尾部分才是压垮系统的元凶。我的做法是从生产日志里采样真实的输入长度分布然后按这个分布生成压测数据。如果还没有生产数据就按业务场景预估一个合理的分布比如 70% 短请求200 token、20% 中等200-1000、10% 长请求1000。压测时重点观察长请求对短请求延迟的影响这个影响在混合流量下非常显著。4.2 分阶段压测先单请求再并发压测不要一上来就拉满并发。我习惯分三步走单请求基线测出单个请求在无竞争情况下的延迟作为基准。逐步加压从低并发开始每次增加并发数记录延迟和吞吐的变化曲线。找到拐点当延迟开始非线性上升时那个并发数就是系统的实际容量上限。这个过程能帮你画出系统的性能曲线而不是只知道一个最大吞吐数字。性能曲线告诉你系统在不同负载下的表现这对容量规划更有价值。比如你知道在 70% 负载下延迟还很稳就可以把告警阈值设在那里而不是等到 100% 才告警。4.3 上线后的持续观测上线不是终点。AI 系统的性能会随着流量模式变化、模型更新、框架升级而漂移。我建议至少监控这几类指标延迟分位数P50、P95、P99不要只看平均值。吞吐趋势按小时或天看 token 吞吐的变化。显存水位峰值显存和平均显存留足余量。GPU 利用率区分计算利用率和显存带宽利用率。错误率特别是 OOM 和超时错误。这些指标要能按序列长度、请求类型等维度下钻。不然出了问题你只知道“慢了”不知道“哪类请求慢了”。5. 那些文档里不会写的踩坑经验5.1 显存碎片比显存不足更隐蔽显存不足会直接 OOM报错清晰。显存碎片则很隐蔽——总显存明明够但就是分配不出连续的大块导致请求失败。这个问题在长时间运行、请求长度变化大的服务里特别常见。解决办法一是用分页显存管理二是定期重启服务释放碎片。前者是治本后者是兜底。我见过一个服务跑 12 小时之后失败率开始上升重启就恢复查了很久才发现是碎片问题。如果你的服务有类似“跑一段时间就变慢/变差”的现象优先怀疑碎片。5.2 批处理里的“慢请求拖累”连续批处理下一个超长请求会占着 batch 位置很久导致后面的短请求排队。这就是所谓的队头阻塞。用户感知就是“偶尔特别慢”但平均延迟看起来正常。缓解办法有几种设置单请求的最大 token 数超长请求走单独的低优先级队列或者用抢占式调度。具体选哪种取决于你的业务能不能接受截断或降级。如果业务上不能截断那就得在容量规划时给长请求留出专门的资源。5.3 预热不是可选项模型加载、CUDA kernel 编译、显存池初始化这些操作在服务刚启动时很慢。如果不预热就接流量前几十个请求的延迟会高得离谱甚至超时。我习惯在服务启动后先跑一批预热请求覆盖不同的序列长度让各种 kernel 都编译好、显存池都分配好再接入负载均衡。预热的请求要覆盖你的典型场景不能只用短请求预热。因为长序列会触发不同的 kernel 和显存分配路径不预热的话第一个长请求还是会慢。5.4 监控指标要能对得上业务技术指标和业务指标之间要有映射。GPU 利用率 80% 意味着什么对业务来说它意味着“还能不能扛住下一次流量高峰”。所以我会把技术指标翻译成业务语言比如“当前配置下系统能支撑每秒 X 个请求延迟 P99 在 Y 毫秒以内”。这样当流量增长时能快速判断要不要扩容。6. 一个最小可用的性能基线模板如果你刚开始做 AI 系统性能工程不知道从哪下手可以先用这个模板建立基线。它不完美但能让你快速看清系统的状态。第一步确定关键指标首 token 延迟 P50/P95后续 token 延迟 P50/P95token 吞吐按序列长度分桶峰值显存占用GPU 计算利用率第二步设计压测场景短请求为主模拟交互长请求为主模拟生成混合分布模拟真实流量第三步记录基线数据在默认配置下跑一遍把所有指标记下来。这就是你的起点。第四步逐项调优每次只改一个变量记录变化。比如先调 batch size再调量化再调调度策略。不要一次改多个否则不知道哪个起了作用。第五步固化配置并监控把调好的配置固化下来上线后持续监控。当指标偏离基线时及时排查。这个模板的价值在于它强迫你把“感觉慢”变成“哪个指标慢”把“优化了”变成“优化了多少”。性能工程最怕的就是凭感觉有了基线一切讨论才有依据。我在实际项目里最大的体会是AI 系统的性能问题八成不是模型本身的问题而是工程配置和流量理解的问题。把这两块吃透很多所谓的“性能瓶颈”其实都能缓解。这个系列后面会继续拆解具体的优化手段包括 KV Cache 管理、调度策略、多卡推理这些话题感兴趣可以接着看。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑