
上篇把AI系统性能工程的整体框架搭起来之后不少人私下问我说框架那套道理听着对落回自己的模型服务上还是不知道先动哪里。这篇我换个更直接的讲法就拿我们平时给大模型推理服务、Agent应用做性能工程踩过的坑和验证过的招儿来说事。围绕的核心就一句话AI系统的性能工程盯的不是“每秒能处理多少请求”而是“单位算力能稳定产出多少Token”以及这些Token能不能按时长成用户要的完整答案。这篇内容适合正在做AI应用落地、维护自建推理服务、或者给Agent系统做稳定性的工程师和架构师。不管你是刚把模型跑起来还是已经在线上被P99时延折磨得够呛应该都能找到能直接抄作业的部分。1. 先搞清楚AI性能工程盯的“性能”是什么1.1 别把传统压测思维直接带进AI系统传统后端性能工程的经典三件套是QPS、P99时延、错误率。这套指标对普通API服务基本够用但套在AI系统上很容易出偏差。原因很直接普通请求的返回体大小基本固定而LLM的返回体大小取决于生成多少Token同一个模型、同一个并发下输出50个Token和输出2000个Token资源消耗是两个量级。如果压测脚本固定用短输出测出来的QPS拿到长对话场景当容量基线上线当天就等着告警轰炸。我见过不止一个团队拿Locust或JMeter按固定报文压模型服务测出吞吐很高高高兴兴上了生产结果真实用户一问就是长篇回答GPU直接跑满排队越来越严重P95从1秒飙到十几秒。这就是性能度量的维度没跟着系统形态一起变。AI系统里的新指标是必须把Token生产这件事考虑进去每秒生成的Token总数生成过程中每个Token的间隔首Token等多久这些才是用户能直接感知的东西。1.2 推理侧性能才是大多数团队的战场AI性能工程里有一半属于训练性能但那块主要是做大模型训练的团队在钻研研究的是MFU这类浮点利用率普通做应用和服务的团队基本不碰。真正跟我们日常绑在一起的是推理侧性能也就是模型训练完之后跑部署、跑服务、跑Agent应用时的资源配置、时延、吞吐、稳定性。推理侧性能有个特点它跟模型参数量、输入输出长度、量化方式、并发策略都有强耦合一个参数不对整条链路的体感就不对。而且推理性能的上限往往不取决于GPU算力有多猛而是取决于显存带宽、KV Cache命中、排队策略这些容易被忽略的细节点。所以做AI性能工程第一课是要把关注的度量从“请求”切到“Token”第二课就是要知道Token生产的两个阶段各自争抢的是什么资源。注意给AI系统做性能分析时别再只贴着QPS和响应时间这两个指标了。把它们降级成辅助指标主指标换成Token吞吐、TTFT、TPOT、TBT这类和生成过程强相关的维度方向才不会偏。2. 端到端时延拆解把一次问答拆到毫秒级2.1 三个核心指标TTFT、TPOT、TBT用户问一句话模型开始转圈到内容一个字一个字蹦出来这中间不是一整个不可拆的时间块而是明显分成几个阶段。TTFT首Token生成时间。用户在输入框敲完回车到看见第一个字之间的耗时决定了“这系统到底卡没卡”的第一印象。TTFT太高用户会以为服务挂了。TPOT单个Token生成时间。指模型在生成阶段平均每隔多少毫秒产出一个Token。这个决定了速度感比如你感觉字蹦得快不快主要看它。TBTToken与Token之间的间隔。跟TPOT有点相似但不一样它更强调生成过程中的均匀程度。如果前一个Token等了300毫秒下一个等了20毫秒体感就是一顿一顿的很不流畅。有些高性能的时间线指标里会看TPOT的波动率本质上就是盯TBT的稳定性。我整理了一张表方便对照着看指标衡量什么用户体感主要瓶颈TTFT首个Token生成耗时转圈时间、卡不卡排队、Prefill阶段计算量TPOT单个Token平均生成耗时字蹦得快不快显存带宽、KV Cache命中、Decode计算量TBTToken间隔均匀性是否一顿一顿调度抖动、并发争抢、网络传输我们做性能分析的时候只要把这四个指标拆出来问题定位就快了一大半。怕的不是指标不好而是所有指标混在一个响应时间里根本不知道是卡在排队、卡在计算、还是卡在网络上。2.2 Prefill和Decode是两套资源画像要理解上面这些指标为什么各说各话得回到Transformer生成的两阶段机制。第一阶段是Prefill也叫预填充。用户的输入Prompt一次性进入模型做并行计算把这段输入对应的Key和Value算出来并缓存。这个阶段是密集矩阵乘法每一步都要把权重矩阵全部过一遍所以它是典型的算力密集型GPU的FLOPS利用率高不高看的就是这个阶段。第二阶段是Decode也叫自回归生成。模型一个Token一个Token地往外蹦每生成一个新Token要拿它的向量去跟之前缓存好的KV做注意力计算。这个阶段的矩阵运算变成了瘦瘦长长的形状GPU算力单元大量闲置瓶颈在显存带宽也就是把权重和KV Cache搬运进计算单元的速度。几乎所有推理框架优化Decode效率的思路本质上都是在跟显存带宽较劲。拿生活类比一下Prefill就像餐厅备菜所有食材一次性切好配好火力全开Decode就像出菜一盘一盘往外端每盘都受限于传菜跑得够不够快。所以你在线上看到的现象往往是首Token等得久是备菜环节没优化内容生成慢、一顿一顿基本上就是传菜环节卡壳。2.3 一次请求的时间估算方法做性能工程最值钱的能力就是对系统的时间开销有一个大概的心算能力。以常见配置估算一下一次普通请求的成本分配假设输入Prompt是500个Token输出是300个Token用的模型是7B参数左右。Prefill阶段处理500个Token在单张主流数据中心的GPU上大约需要0.2秒到0.5秒这个数字会因为实现方式和输入长度差异浮动。Decode阶段输出300个Token每个Token大约需要30毫秒到60毫秒那么整体300个Token生成时间就是9秒到18秒。这么一算你就能理解为什么用户感觉“AI回答慢”。因为真正花时间的不是读题而是写答案。这个过程里TTFT可能只占零点几秒而TPOT累加起来轻轻松松就是十几秒。所以如果用户体验不好要优化的重点大概率在Decode阶段不在Prefill。理解了时间分配之后就能做一件很有价值的事给系统画一条Token吞吐预期线。比如我们知道目标场景的平均输入是500 Token、平均输出是300 Token单用户一次请求耗时15秒那么单并发下这个用户能感知到的生成速率就是20 Token每秒。如果要支持100个用户同时在线提问不考虑排队需要的总吞吐就是2000 Token每秒。这个数一算出来你应该选择什么规模的GPU、要不要上连续批处理、要不要做Prompt缓存心里就有数了。怕的就是连这个估算都没有稀里糊涂地压测然后看了一堆吞吐数字也不知道够不够用。实操提醒每次做容量评估之前先给核心业务场景定一个“标准请求画像”——输入Token分布、输出Token分布、并发目标、SLA目标。后面所有测试、扩容、缓存配置都拿这个画像做基准否则就是各测各的得不出可复用的结论。3. 把AI系统调快的四个实操方向3.1 推理引擎和量化取舍先选对再调优现在推理引擎选择已经很丰富了vLLM、SGLang、TensorRT-LLM、LMDeploy等各家都有自己的拿手绝活。我自己的经验是没有绝对最强的引擎只有跟场景匹配的引擎。如果躲不开兼容性问题或者做的是复杂Agent工具调用场景SGLang的灵活性和结构化输出能力更顺手。如果追求极致的吞吐和有状态的连续批处理vLLM的生态和文档更成熟。底层逻辑是使用PagedAttention这类显存管理手段来提升KV Cache利用率、支持连续批处理这两个能力决定了高并发下吞吐好不好。TensorRT-LLM在部分模型上有额外收益代价是编译时间长、运维成本高对大多数团队反而没那么必要。量化的选择要更谨慎。FP16是基准INT8损失很小显存占用和使用带宽能降一半INT4压缩最大但精度影响开始变得不可忽视。我在实际项目里的结论是常规业务场景先跑FP16显存吃紧再试INT8INT4只给那些对精度不敏感的场景比如代码生成里的简单模板、分类任务之类的。很多人只看“显存降了”这个好处没算精度损失导致的输出变差回头还得靠加大模型尺寸补回来得不偿失。3.2 连续批处理与KV Cache吞吐翻倍的秘密连续批处理是最近几年推理吞吐提升最大的功臣。传统批处理逻辑是攒够一批请求全部跑完再放下一批进来缺点很明显每个请求的长度不一样快的要等慢的而且新请求必须等到整批结束才能被处理。连续批处理则是“边走边补位”谁先生成完谁先走空出来的位置马上补进新请求。这就像餐馆翻台吃完一桌马上让下一桌坐进来而不是等所有桌都吃完才统一打扫。KV Cache是这里面的核心资源。模型每处理一个Token都要把Key和Value缓存下来留到后面的Token做注意力计算时用。这个缓存的大小有公式推KV Cache大小 2 × 层数 × 每层头数 × 每个头的维度 × Token数 × 每字节位数 / 字节位数系数。用绝对一点的说法就是2乘以层数、注意力头数和头维度乘以Token数再乘以存储精度。举例来说一个7B参数的模型假设32层每个Token的KV Cache占用约为几百KB级别如果支持8K上下文长度那么单条请求的KV Cache可能占到几个GB。你说这个资源省不省所以高并发下KV Cache的命中、分页、复用能力直接决定了引擎的吞吐上限。实际优化动作有两个方向一个是让更多的请求挤进同一块显存另一个是减少重复计算比如对常见Prompt做前缀缓存。vLLM这类框架支持自动前缀缓存多轮对话场景里历史对话的头几百个Token的KV Cache可以被复用实测TTFT能降一个量级。这个参数在框架里通常叫enable_prefix_caching值得翻出来好好研究是那种改了之后立刻见效的低垂果实。3.3 并发控制与排队策略为什么P50好看P99崩了这个问题在传统后端也常见但在AI系统里更严重。原因是LLM推理的显存占用是动态的每条请求的Token长度不一样占用的显存量也不一样。假设并发数设得比较高短请求确实能跑得很轻松但一旦进来几条长请求显存一下被吃光后续所有请求都要排队。于是出现了经典现象P50时延看起来挺正常但P99飚得离奇。解决排队问题的思路分两层。第一层是限制最大并发请求数、最大Token生成数让系统大多数时间跑在设计水位以内不是疯狂压榨显存换QPS数字。第二层是给不同业务配不同的队列优先级比如在线问答的SLA比离线批量分析更敏感那就让在线请求插队或预留资源。框架里的max_num_seqs、max_running_requests、max_model_len这些参数不是给你无脑调大用的而是用来平滑资源申请的。我见过好几次调大了并发数之后吞吐没上去多少反而OOM频繁就是因为没搞清楚KV Cache总预算。另一个跟排队强相关的是流式输出。AI应用基本都会做流式用户看到字在蹦就不觉得那么卡。但流式输出对性能的要求其实更高因为你不仅要控制整体生成时间还要控制每两个Token之间的间隔不能太悬殊。做性能压测的时候如果用非流式脚本去测得出的P99往往比真实体验好因为真实的流式体验更敏感轻微的抖动会被放大让人时不时觉得卡壳。3.4 应用层优化别让模型干所有事模型推理性能再优化也架不住应用层设计给自己挖坑。最常见的两个坑是每个请求都重复喂很长的上下文以及让模型承担太多非生成类的工作。第一类问题靠Prompt缓存和上下文压缩来解决。多轮对话场景里历史信息一遍遍跟着请求送进去TTFT很高还占大量KV Cache。成熟的方案是给公共前缀做缓存或者把历史对话摘要化让模型只拿精简后的上下文继续处理。我见过有团队靠着这个动作TTFT降了60%吞吐大幅提升而模型本身压根没动过。第二类问题靠流程重设计。分类、抽取、格式转换这种结构化任务用显式代码处理比让大模型做更稳定更快准确率还更可控。不一定要什么都丢给大模型模型解决的是理解和生成问题不是所有文本处理问题。性能工程里面最便宜的一次优化往往是把“没必要让模型做的事”从模型调用链里挪走而不是去折腾更低的量化精度。4. 观测之上性能工程真正要解决的是成本效率4.1 从优化时延转向优化单位成本很多团队做性能优化的终局目的其实是控制成本。大模型推理的每一块GPU都很贵单位时间内能产出的Token就是硬通货。同样一块GPU优化前每秒产出500 Token优化后每秒产出1500 Token意味着硬件成本直接摊薄。所以性能工程的KPI如果只看时延容易忽略掉更重要的维度成本效率。落地方式是把观测指标换算成钱。比如记录每日Token总量、GPU小时数、总成本算出每百万Token的成本。然后在优化完某个动作后对比这个值的走势。一个推理引擎切换、一次前缀缓存开启、一批量化压低精度到底值不值得都看这个单位成本的值变了多少。这个视角比单纯的“响应时间降低X%”更能说服老板也更容易在不同方案之间做取舍。4.2 容量规划的手算示例容量规划在AI系统里核心就一句话需求侧的Token吞吐目标是多少供给侧的GPU能给的Token吞吐是多少中间差多少要靠排队和扩容补。拿具体数字算一遍。业务目标是同时支持100路对话每个用户平均输出350 Token。假设每路对话的生成速度是25 Token每秒那么全部用户同时生成时系统需要支撑的总吞吐是100乘以25等于2500 Token每秒。再假设单张主流GPU在那个模型和量化条件下实测极限吞吐是1400 Token每秒那么至少需要2张GPU才能让这100路对话不排队。如果还希望留20%余量应对突发长回答就要按3000 Token每秒去规划那3张GPU更稳。真实的容量规划里还要叠加时延要求。如果SLA要求P95在8秒以内而平均输出350 Token那么生成速率不能低于45 Token每秒这意味着并发用户数如果继续加就要对应提高每路请求的生成速率在队列里就要控制排队深度。这个计算逻辑在传统性能工程里差不多但参数从“请求长度”变成了“Token长度”一开始不适应算两次就顺了。4.3 实测经验快和稳是两回事我做过的最具迷惑性的性能活动不是把慢系统调快而是把一个测起来快、线上却很差的系统找出原因。首Token很快平均生成速率也不错但用户就是反馈卡。后来查出来是几类抖动叠加网络传输偶发延迟、并行请求调度争抢、引擎批处理里新补进来的长请求拖慢了老请求。这些都是单纯的吞吐数字看不出来的。所以做AI系统性能工程一定要建立两个独立视角一个是容量视角看吞吐和资源利用率另一个是稳定性视角看TPOT的分布和长尾。容量优化是让系统“能跑更多”稳定性优化是让系统“跑得均匀”。对AI这种流式生成系统来说稳定性可能比容量更贴近体验。压测报告里如果只有平均数和P50没有P95、P99和最大间隔那这份报告参考意义要打个问号。实践心得AI系统的性能观测不要只看指标平均值。以TPOT为例均值20毫秒看起来很好但如果30%的请求TPOT超过80毫秒实际体验一定很糟糕。压测和监控都请盯分布至少盯P95和P99有条件的话把Token级别的时间线完整记录到Trace里。5. 常见问题与排查技巧实录5.1 问题速查表性能问题排查最大的挑战是现象和根因往往隔着两层。我把过去碰到的高频问题整理成一张表可以按图索骥现象可能根因怎么查常用解法首Token慢Prefill耗时高或请求排队看TTFT分位值、队列长度开前缀缓存、降输入长度、扩并发生成字蹦得慢Decode阶段显存带宽吃紧看TPOT均值、GPU显存带宽利用率换更高带宽GPU、INT8量化、检查是否混跑其他负载输出一顿一顿调度抖动或网络不稳看Token级Trace、TBT分布调批处理策略、限制单请求最大输出Token数高并发时OOMKV Cache超预算看显存曲线、max_model_len压并发上限、开分页管理、降上下文长度压测P50好P99崩长请求拖尾区分长短请求的平均时延给长请求单独队列、限制最大Token数、调整并发水位换小模型反而慢小模型没吃满批量或精度影响对比同批次的吞吐和输出质量加大批大小、检查算子优化、确认量化精度够用5.2 几个反直觉的现象第一个反直觉是模型变小了反而感觉更慢。有些场景换了个参数量少的模型理论上单Token生成速度更快但因为输出质量下降应用层被迫加了更多后处理逻辑比如多次调用小模型补齐信息。整条链路一算总耗时反而增加了。性能优化要考虑端到端不要拿着单环节的加速就以为全局都提速了。第二个反直觉是加了GPU不解决问题。瓶颈在应用层时比如每次请求都重新嵌入一遍文档、检索库响应慢、业务逻辑里串行调用一堆模型那你加再多的GPU也只是把排队问题往后挪整体用户体感不会有质的提升。先梳理应用层调用链再做推理侧扩容是成本更低的路径。第三个反直觉是显存没占满但吞吐上不去。很多人以为显存是衡量推理资源利用的核心指标实际上推理引擎如果算子实现不好瓶颈可能卡在计算效率或者数据传输上显存只是被静态分配了。要观察的核心是计算利用率和显存带宽利用率单纯看“显存用了多少”是不够的。5.3 避坑清单这些都是我真正踩过、或者帮别人排查时见过的坑列出来供参考。第一个坑是忽略Token化耗时。很多排障排查到模型调用那一步就停了没想过Prompt文本转Token这个步骤本身就消耗几十毫秒如果用的是慢速Tokenizer或者加载了过大的词表这个开销会相当可观。排查TTFT比较高的问题第一眼先看Tokenizer耗时经常能白捡一段优化空间。第二个坑是日志和观测链路占用请求资源。流式输出的场景下每输出一个Token就写一条日志异步处理不当会拖慢整条流式链路。线上观测要做但要控制采样率不要把每个Token的日志都打到业务代码里同步写。第三个坑是忘记预热。模型部署之后没有做请求预热就直接压测测出来的首Token时延惨不忍睹因为CUDA Kernel第一次加载、显存分配都需要时间。压测之前必须先跑几轮请求让引擎热起来数据才可信。第四个坑是压测脚本里的输入长度太固定。真实业务的输入Prompt长度是长尾分布如果压测只用一个固定长度的Prompt测出来的Prefill耗时就脱离真实。压测数据要覆盖短、中、长多种情形最好从线上日志采样真实Prompt长度分布来生成测试语料。这些都是常规压测文档里不会告诉你的东西但踩过一次就会长记性。我自己最近的一个体会是AI系统性能工程跟传统性能工程最不一样的地方不是工具链换了而是你必须把“模型是怎么生成答案的”这件事想清楚再来谈优化。Prefill和Decode的资源画像、Token吞吐的计算、KV Cache的显存预算、连续批处理的调度逻辑这些底层机制不掌握优化就是撞运气。而一旦把这些底层逻辑内化成直觉做性能优化会变得特别顺看到TTFT高就知道去查前缀缓存看到TPOT差就知道该看显存带宽看到P99崩就知道是长请求拖尾——招招都能打在点上。最后再分享一个小技巧如果你刚开始给AI系统做性能工程不用一上来就堆一堆监控图先盯三个数字就够——生产环境的标准请求TTFT分位值、TPOT分位值、每日Token总量。把这三个数字记到题板上每次做改动前记录改动后对比。坚持一两周你自然就知道接下来该优化哪里了。