大模型性能指标TTFT与TPOT详解:从首字延迟到生成速度的优化实战
2026/9/18 18:01:06 网站建设 项目流程

前阵子帮一个做AI客服工具的朋友排查线上问题,用户反馈很直接:“我点完发送,等了老半天才蹦出第一个字,后面又一个字一个字往外吐,急死人。”我们打开监控一看,有点意思——服务端平均首字延迟只有800毫秒,文档上也写着生成的token速度是“每秒5个”,看起来不算离谱。但用户为什么会觉得慢?问题就出在这两个指标上:一个叫TTFT(Time To First Token,首token延迟),一个叫TPOT(Time Per Output Token,单token生成耗时)。它们一个管“第一个字什么时候来”,一个管“后面的字多久能出来”,合在一起,才是大模型应用真正的“用户体感速度”。

这篇文章我打算把这两件事彻底讲透。不管你是做AI应用开发、本地部署大模型,还是纯粹想搞懂“为什么同样一个模型,别人跑得那么快”,都可以顺着往下看。我会把指标定义、生命周期、测试方法、优化手段、常见坑全部串起来,尽量用我实际踩过的例子说话。

1. 从用户体感说起:为什么大模型应用的“快慢”不能只看一个数字

1.1 两个典型的卡顿场景

做AI应用的人,一定遇到过两类完全不同的“慢”。

第一类是“半天不出字”。用户输入问题,点击发送,然后界面一直在转圈,等了三四秒才看到第一个字。这种慢最伤体验,因为用户会怀疑服务是不是挂了。

第二类是“首字秒出,但越等越急”。比如输入问题之后,几乎是秒回,马上看到第一个字出来了,但接下来的内容是一点一点往外冒,每个字间隔明显,一句话300个字,能拖拉一分钟。用户觉得你在“挤牙膏”,体验同样很差。

这两类慢,正好对应两个完全不同的性能指标。前者是TTFT太高,后者是TPOT太高。很多团队上线大模型应用时,只看一个平均首字延迟,或者只盯着“完整响应时间”,结果就是用户体感和监控数据严重脱节。

1.2 TTFT与TPOT的直观定义

TTFT,全称Time To First Token,指的是从客户端发出请求开始,到客户端收到模型返回的第一个token所经过的时间。用吃饭来打比方,这就好比你去餐厅坐下点菜,从下单到第一道菜端上桌的间隔。这个时间越短,用户越觉得“响应快”。

TPOT,全称Time Per Output Token,是模型在生成阶段产出每一个token所消耗的平均时间。放到餐厅场景里,就是第一道菜上来之后,后续每道菜之间的上菜间隔。如果间隔太长,你吃一顿饭会非常煎熬。

这两个指标通常一起使用。TTFT决定了“能不能快速抓住用户注意力”,TPOT决定了“后续内容能不能顺畅阅读”。两者相加,再加上网络传输、前端渲染等开销,才是一个用户的完整等待体验。

1.3 为什么传统Web性能指标不够用

做过Web开发的人都知道,传统后端接口最关心的指标是“响应时间”,也就是从请求发出到响应体完全返回的总耗时,顶多再看一个“首字节时间”(TTFB)。但这个思路放到大模型应用上,天然水土不服。

原因在于,大模型接口是流式输出的。正常情况下,你不会等几百上千个token全部生成完才把结果推给用户,而是用Server-Sent Events(SSE)或者WebSocket把token一个个推给前端。用户看到的是“边生成边展示”,不是等一个巨大的JSON包。

这时候,如果你还拿“完整响应时间”来衡量性能,结果会非常极端:生成长文可能要几十秒,短回答可能只要一两秒。你不能说长文的性能就比短文差,因为两者的输出长度根本不一样。

更重要的是,用户对“慢”的感知是分两段的:第一段是“有没有反应”,第二段是“速度顺不顺”。传统指标完全没法表达这种感受。所以TTFT和TPOT才会成为大模型应用性能评估的核心指标。后面我会拆开讲它们的本质、影响因素和优化思路。

2. 核心指标拆解:TTFT到底在衡量什么

2.1 TTFT的完整生命周期:从请求到第一个token

别急着只看时间戳差值。TTFT不是一个简单的网络耗时,它背后包含了好几个阶段。我按一个典型请求的路径拆给你看。

  • 客户端发起请求,请求经过网络传输到达服务端。
  • 服务端网关、鉴权、路由等中间层处理。
  • 请求进入推理引擎的调度队列,等待被某个GPU worker接收。
  • GPU开始执行“预填充”阶段(Prefill),把用户输入的整个Prompt做一次前向计算,生成第一个输出token,同时把过程中产生的KV Cache写入显存。
  • 推理引擎把这个token通过网络返回给客户端。
  • 客户端收到第一个token。

这里最关键的是第4步,也就是Prefill阶段。它和我们通常理解的“生成”不太一样。在自回归大模型里,生成第一个token时,模型需要“看”完用户给的所有输入,再做一次完整的Transformer前向推理。输入越长,这一步的计算量就越大。

我做一个粗略估算。假设你输入了1000个token,模型是7B参数规模,Prefill阶段大概需要执行次数为2乘以模型参数量再乘以输入长度的前向计算,也就是约2乘以70亿再乘以1000,等于1.4×10^13次浮点运算。如果用的是单张A100 80G,FP16算力大约在312 TFLOPs,理论上最快也要45毫秒左右。实际运行中,还要算上显存读写、注意力计算、调度切换、网络开销,所以拿到几百毫秒的TTFT是非常正常的。

如果模型是70B,输入还是1000token,理论计算量直接涨到1.4×10^14次浮点运算,单卡得跑近半秒,还不包括多卡通信。所以你会发现,很多大模型服务强调“我们TTFT低于1秒”,听起来很轻松,实际上对模型规模和硬件的要求相当高。

2.2 影响TTFT的关键因素

搞清楚生命周期之后,TTFT的影响因素就很清楚了。我按重要程度列一下,方便你排查时有个方向。

第一,模型规模和输入长度。模型参数越大,单次Prefill的计算量就越大;输入Prompt越长,需要处理的数据越多。两者都是线性叠加的关系,所以“用大模型处理长文档摘要”和“用大模型做一句话问答”,TTFT完全不是一个量级。

第二,推理引擎和调度策略。同一张GPU,你用原生HuggingFace Transformers跑,和用vLLM、TensorRT-LLM这类带连续批处理和PagedAttention的框架跑,TTFT可能差好几倍。原因很简单:原生框架往往是一个请求占用整张卡,别的请求来了只能排队;而vLLM会把多个请求动态拼在一个batch里做Prefill,显存利用率高,排队时间就短。

第三,算力和显存带宽。Prefill阶段更吃算力,GPU的FP16/BF16算力越高,Prefill跑得越快。这也是为什么做在线大模型推理,大家更愿意用A100、H100这类数据中心卡,而不是普通游戏卡。

第四,队列和并发。请求多了之后,即使单个Prefill计算量不大,排队也会把TTFT拉高。实际生成场景中,并发一旦上来,TTFT的P95会快速上升,这一条后面我会专门讲。

第五,网络RTT。如果你本地部署,网络开销很小;如果用API调用,跨地域请求,光网络往返就可能占掉TTFT的一半以上。测API的时候一定要把这个因素分开,别把网络抖动算成服务端性能问题。

2.3 怎么测TTFT才靠谱

很多人测TTFT,就是拿SDK简单记一下时间差。但有几个细节不处理好,数据会非常虚。

第一个细节是“起点”。TTFT的起点应该是客户端“真正发出请求”的时刻,而不是你在代码里创建任务对象的时刻。如果用Python的openai库,建议在调用stream接口之前记录时间戳,而不是等到进入for循环再记录。

第二个细节是“终点”。终点是客户端收到第一个数据块(chunk)里的第一个token的时刻。所以要确保你用的是流式接口,并且解析的是第一个包含content增量的事件,而不是建立连接的事件。

第三个细节是“预热”。模型冷启动时,权重还没加载到显存,管线还没建立,第一次请求会特别慢。测试之前至少要发几个请求让模型热起来,不然你会把一个冷启动问题当成所有请求的典型表现。

第四个细节是“统计口径”。TTFT受排队影响非常大,单次测试不能说明问题。至少要压测几十上百个请求,看P50、P95、P99,而不是只报平均值。平均值很容易被大量短请求拉低,长尾请求的实际体验往往更差。

我常用的测量方式是这样的:用Python写一个脚本,通过OpenAI兼容的流式接口发送固定长度的Prompt,记录“发送时间”和“第一个content增量到达时间”,跑若干轮之后统一算百分位。代码很简单,核心逻辑如下:

import time from openai import OpenAI client = OpenAI(base_url="http://your-service/v1", api_key="test") def measure_ttft(prompt: str) -> float: start = time.perf_counter() stream = client.chat.completions.create( model="your-model", messages=[{"role": "user", "content": prompt}], stream=True, max_tokens=128, ) first_token_time = None for chunk in stream: if chunk.choices and chunk.choices[0].delta.content: first_token_time = time.perf_counter() break return first_token_time - start if first_token_time else None

这里有个小技巧:提前构造好一个固定长度的Prompt,比如重复一段话凑成300个token,这样每次测试的输入复杂度一致,数据才有可比性。如果每次测试用的Prompt长度不一样,TTFT根本没有横向对比的意义。

3. 核心指标拆解:TPOT、吞吐量与“每秒多少个字”的错觉

3.1 TPOT的定义与单位

TPOT的字面意思已经很清楚了。我习惯把它叫“单token延迟”,单位通常是毫秒/token或者秒/token。但在实际项目里,大家更常用它的倒数:tokens per second(TPS),也就是每秒能生成多少个token。比如TPOT是50毫秒,那么TPS就是1秒除以0.05秒,等于每秒20个token。

这里必须提一个容易混淆的概念:TPOT和“每字耗时”不一样。中文里,一个汉字在分词后的token数不一定是一,有时一个汉字可能对应一个token,有时一个词(两个汉字)可能被拆成一个或多个token。所以你不能直接把TPS当“每秒多少个汉字”来用,要结合具体分词器估算。粗略一点讲,中文场景下,一秒20个token大约相当于一秒十几个字到二十个字,这不算快,但已经能读了。

自回归模型的生成机制决定了TPOT有它的特殊性:模型在生成第N个token时,必须把前N-1个token对应的KV Cache保留在显存里,作为当前步的上下文。也就是说,每一步都依赖于前面所有生成结果,这是一个严格的串行过程,没法像训练那样多个token并行计算。用写文章来打比方,你只能一次写一个词,写完第一个才能写第二个,不能同时写两个词。

3.2 影响TPOT的关键因素

TPOT和TTFT的影响因素虽然都跟硬件、框架有关,但侧重点很不一样。我总结成三句话:TTFT看算力,TPOT看显存带宽,两者都怕显存不够。

为什么TPOT看显存带宽?因为在Decode阶段,每一步生成一个token,模型本身的计算量相对不大,但GPU必须把整个模型的权重从显存里读一遍,才能完成前向计算。这个“读权重”的时间,往往比真正计算的时间还要长。

举个具体例子。一个7B参数的模型,如果权重是FP16精度,占用显存大约是14GB。每一步生成token,理论上都要把14GB权重从HBM显存读到计算单元。假设你用的GPU显存带宽是2TB/s,那么单看读权重这一步,理论下限就是14GB除以2TB/s,等于7毫秒。这还没算KV Cache读取、中间激活、框架调度等开销。所以你会发现,7B模型在高端卡上通常能做到每token二三十毫秒,但在老显卡上可能跑到一两百毫秒,差距就是这么来的。

量化为什么对TPOT帮助巨大,原因也在这里。模型从FP16变成INT8,权重占用减半,从INT8变成INT4,权重占用再减半。同一个模型,INT4量化后,读权重的理论下限直接变成FP16的四分之一,TPOT自然大幅下降。当然,量化会带来一定的精度损失,具体能不能接受,要结合业务场景判断。

除了硬件和量化,Batch大小也是一个核心因素。GPU处理一个batch时,如果batch里有多个请求,那么读权重这件事是可以共用的:同一个模型权重被多个请求同时使用,显存带宽利用率更高,整体吞吐上去了。但对单个请求来说,因为计算资源被分摊了,TPOT通常会变差。这就是在线服务和离线批处理在性能优化目标上的根本矛盾。

3.3 从TPOT到TPS、吞吐量:几个容易踩的换算坑

测出TPOT之后,很多同学会直接拿它的倒数当“最终速度”汇报,但这里有几个坑。

第一个坑是“单个用户的TPOT不等于服务端吞吐”。假设你并发100个请求,每个请求的TPOT是200毫秒,那么对单个用户来说,每秒只有5个token。但服务端整体可能在同时处理多个不同的请求,每秒产出的总token数可能是500个,因为服务端在跑更大的batch。这里“单请求TPS”和“系统吞吐量”(Throughput)是两个概念。前者等于1000除以单请求TPOT,后者等于batch中所有请求的token总数除以总耗时。做性能压测时,一定要区分你是想评估用户体验,还是评估系统容量。

第二个坑是“用总生成时间除以token数量,不等于TPOT”。因为总生成时间包含了首个token的Prefill时间。比如一个请求生成了100个token,总耗时5秒,平均每个token是50毫秒,但实际TPOT可能更小,因为5秒里还包括了Prefill的几百毫秒。更准确的做法,是排除第一个token之前的时间,只统计首token之后到结束token之前的平均间隔。这个才是稳定的Decode速度。

第三个坑是“不考虑EOS停止”。模型生成到max_tokens上限时可能会被截断,也可能提前输出结束符。如果你用总输出长度去除以总时间,但输出长度没排除结束符,精度会受影响。建议在流式接口里统计content增量的实际数量,用它作为分子。

第四个坑是“把TPOT和TTFT相加当作完整响应时间”。实际上完整响应时间还要算上生成最后一段后的结果整理和网络收尾时间。不过在大模型场景下,用户通常不会等完整响应,而是边接收边阅读,所以“TTFT+总生成时长”更适合作为“如果前端不做流式展示”时的最坏体验。

3.4 为什么TPOT不可能是0

有次评审会上,产品经理问我:“既然现在GPU这么强,TPOT能不能做到0?”我只能苦笑。不止他,很多刚接触大模型的人都觉得生成速度应该快到无限。但自回归模型的每一步至少需要一次完整的Transformer前向计算,哪怕是单层很小的模型,也有数学运算和显存读写。只要不是“预先把所有答案背下来”,TPOT就一定大于0。

你可能会说,那种“模板化回复”是不是TPOT接近0?那是应用层直接返回固定字符串,根本没走模型生成,不算模型性能。真正的大模型应用,只要走模型,就得面对这个物理下限。我们在做架构设计时,应该先接受这个约束,然后再去追求“在合理成本下把TPOT压到可接受范围”。

4. 评测环境与工具选型:别让测试方法毁掉数据

4.1 本地部署和API调用,测法差在哪

现在很多团队喜欢“本地部署AI大模型”,自己做私有化推理服务。本地部署的好处是你能看到完整的日志、GPU监控、调度信息,坏处则是配置复杂度高,很容易测出一堆“看起来奇怪”的数据。

本地部署时,我最建议你用nvidia-smi或者DCGM持续监控显存、功耗、利用率。你会发现一个非常典型的现象:Prefill阶段GPU算力利用率很高,Decode阶段利用率可能不高,但显存带宽占用很大。如果你看到Decode阶段GPU利用率一直在90%以上,那说明当前batch已经非常大了,单个请求的TPOT大概率已经很难看。

API调用则完全是黑盒视角。你不能直接看GPU状态,只能通过客户端时间戳计算TTFT和TPOT。这时候网络波动影响很大,我建议至少分不同时段测多轮,同时记录“请求到达服务端”这种服务端指标,减少网络干扰。如果服务端不提供时间戳,那只能在客户端多测几次取中位数。

如果你想做本地部署,配置上有一个容易被低估的点:不要只看显存大小,更要看显存带宽。同一家公司出的两张卡,显存大小可能一样,但带宽差一截,跑大模型的速度会差很多。具体选择时,可以把模型权重显存、KV Cache显存、中间激活显存都估一遍,再拿带宽参数估算理论TPOT,心里更有底。

4.2 评测工具:LLM Perf、vLLM benchmark、自建脚本该选哪个

工具选型上,我见过不少团队用自研脚本测,也见过直接用框架自带benchmark的。我的建议是分层来看。

如果你只是想快速评估一个推理框架的基线性能,优先用框架自带的benchmark脚本。比如vLLM的benchmark_serving.py,它会给你输出TTFT、TPOT、吞吐量等指标,省去自己造轮子的时间。TensorRT-LLM也有类似的benchmark工具。这类工具的好处是测试方法和框架内置的trick保持了一致,不容易踩到“框架实际支持但脚本没用到”的坑。

如果你想做长期回归测试,或者你们有自己的线上流量特征,那我强烈建议写一套自己的评测脚本。原因很简单:线上Prompt长度、并发模型、输出长度都有固定模式,通用benchmark按固定参数测,不一定符合你的真实场景。

还有一个开源工具叫LLMPerf,专门用来测大模型服务的SLO指标,支持对TTFT、TPOT等做分布统计,也很适合放在压测流程里。不过工具只是辅助,最关键的是你要明确自己的测试目标:是测延迟、测吞吐,还是测稳定性?测试目标不同,脚本设计完全不同。

4.3 请求并发怎么设计:单路、并发、饱和度

评测大模型性能,并发设计是最容易草率的一步。很多人习惯“直接开100个并发跑一下”,然后看平均指标,发现TTFT很高,就说服务不行。其实这个并发可能已经远远超过服务的合理水位。

我更推荐“阶梯加压”的方式。从并发1开始,测一组TTFT和TPOT;然后并发4、8、16、32、64,慢慢加压,同时记录指标变化。你会看到一条曲线:

  • 并发很低时,TTFT和TPOT都很稳定,因为每个请求都能立刻被处理。
  • 并发逐渐升高时,TTFT开始缓慢上涨,因为排队时间变长;TPOT可能小幅上涨,因为batch变大、单请求分到的资源变少。
  • 并发超过某个临界点后,TTFT会快速上涨,TPOT也可能恶化到完全不可用,这时系统就已经“饱和”了。

这条曲线非常有用。你可以根据业务预期的最大并发,找到一个“既能满足TTFT/TPOT目标,又能最大化吞吐”的并发水位。比如线上预期峰值并发20,那么理想状态是并发20时,TTFT的P95低于1.5秒,TPOT的P95低于100毫秒。如果达不到,要么加卡,要么优化,要么降低并发目标。

还要注意客户端别成为瓶颈。压测脚本如果用单线程发请求,瓶颈可能在Python的GIL,不在服务端。建议用异步客户端或者多进程,确保压测流量真正打到服务端。

4.4 数据记录与统计:P50、P95、P99,别只报平均值

“平均TTFT 800ms”这句话,听起来不错,但可能掩盖了10%的用户在等3秒的事实。平均值是很好看的汇报指标,但做性能优化,我必须看百分位。

我习惯把压测结果整理成下面这种表格,自己复盘也好,跟团队汇报也好,都很直观:

并发数TTFT P50TTFT P95TPOT P50TPOT P95吞吐 tokens/s
1350ms400ms38ms42ms28
8520ms800ms55ms90ms145
16750ms1500ms80ms130ms210
321400ms3200ms120ms220ms260

注意,这个表格是示意数据,不是某个固定模型的结果。但你可以看到,只看平均值的话,并发16似乎还能接受;但如果你看一眼P95,可能会发现已经有用户经历了1.5秒的首字延迟。做性能SLO时,我更推荐以P95甚至P99作为硬性门槛,比平均值可靠得多。

还要统计时序趋势。压测持续10分钟,后半段和前半段可能差别很大,因为KV Cache碎片、显存占用、网络连接老化都会导致性能劣化。记录时间序列图,能帮你提前发现这类隐患。

5. 优化实战:从TTFT和TPOT两个方向把应用调快

5.1 降低TTFT:Prefill提速、动态批处理、KV Cache优化

聊完测试,进入最实际的部分:怎么把TTFT降下来。我的经验是,先分清你现在的瓶颈在哪个环节,再对症下药。

如果是Prefill计算太慢,优先考虑缩短输入长度。很多应用会把大量历史对话、系统提示词、检索结果全部塞进Prompt,结果输入长度轻松超过2000token。同样的模型,输入100token和输入2000token,TTFT差出好几倍。建议做Prompt压缩、历史摘要、动态选择相关上下文,能省一大截时间。我之前处理过一个检索增强生成(RAG)项目,把Top-K文档从10个砍到4个,输出质量几乎没下降,TTFT却从2.8秒降到了1.2秒。

如果是排队导致TTFT升高,优先看推理框架是否支持连续批处理。vLLM默认支持continuous batching,能把一个batch中已完成请求的算力立即让给新请求,而不是等整个batch结束。如果你还在用老式的静态批处理,并发一高TTFT必然爆炸。

如果是KV Cache导致显存不足,启用PagedAttention这类显存管理能力,能显著减少显存碎片,提升有效batch容量。另外,主流框架支持KV Cache量化,把KV Cache从FP16压到INT8,显存占用直接减半,能容纳更多并发请求,间接降低排队时间和TTFT。

最后,别忘了模型预热和常驻。如果服务会自动缩容到0,冷启动一次可能要几十秒,第一个请求的TTFT自然惨不忍睹。线上服务至少要保持一个最小实例常驻,才能给用户稳定的响应。

5.2 降低TPOT:Decode阶段优化、显存带宽、量化、并行策略

降低TPOT的核心思路很直接:减少每一步Decode需要读取的数据量,或者提高单位时间的读取能力。我按见效快慢给你排个序。

最立竿见影的是量化。把模型从FP16量化到INT8,权重读取量直接减半,TPOT通常也能近似减半。INT4效果更明显,但精度风险更高。如果应用对输出质量敏感,可以先试INT8,再结合AWQ、GPTQ这类量化方法看效果。我自己的经验是,7B模型INT8量化后,在大部分任务上质量下降幅度很小,但TPOT能提升30%到50%。

其次是升级硬件,换显存带宽更高的卡。这个不用多解释,但要注意性价比。同样是跑7B模型,消费级卡和数据中心卡的带宽差距很大,如果是生产环境,建议在预算允许的情况下选带宽高的型号。

再次是投机解码(Speculative Decoding)。它的思路是用一个很小的草稿模型先快速生成几个token,再由大模型一次性验证。验证通过就多跳了几步,省去大模型逐步解码的时间。这个技术在小模型上用得越来越多,但实现复杂,且草稿模型和正式模型的分布要对齐,否则收益可能为负。

最后,可以从应用层规避。有些场景不需要特别低的TPOT。比如用户阅读中文的速度大约每秒5到10个字,如果你把TPOT优化到每秒50个token,用户是感觉不出来的,但成本可能翻了好几倍。所以TPOT不是越低越好,够用就好。

5.3 工程权衡:TTFT和TPOT此消彼长,怎么取舍

做工程的人都明白,系统优化永远是取舍。TTFT和TPOT虽然都是延迟指标,但在高并发场景下,它们通常会互相拉扯。

一个典型矛盾是batch size。增大batch size能提升整体吞吐,让服务在单位时间内处理更多请求,但每个请求的TPOT会变差,因为计算资源被摊薄了。同时,增大batch也可能让排队长度变短,因为请求被更快地吸收进批次,TTFT反而可能下降。所以你会看到一个现象:适度提高batch size,TTFT降低,TPOT升高;过度提高,TTFT和TPOT一起恶化。

面对这种矛盾,我的做法是给每个指标设定一个SLO上限,然后在这个约束下最大化吞吐。比如聊天场景,我要求TTFT的P95不超过2秒,TPOT的P95不超过100毫秒(也就是每秒至少10个token),那么我会从并发1开始逐步加压,找到同时满足这两个条件的最大并发。这个并发就是当前配置下的最优水位。超过它,要么接受多花钱加卡,要么接受降级。

还有一个思路是把不同场景拆开部署。交互式聊天服务单独部署一批GPU,走低延迟配置;离线批处理任务(比如批量文档总结)用另一批GPU,走超大batch和最高吞吐配置。两个场景的SLO完全不同,混在一起只会互相拖累。

5.4 真实项目中的优化优先级建议

如果你看完前面的内容,脑子里已经对优化方向有了很多想法,但不知道从哪儿下手,我按经验给你一个参考顺序。

第一步,先换推理框架。如果你还在用原生Transformers跑在线服务,赶紧换成vLLM或者TensorRT-LLM。这一步通常能带来一到三倍的性能提升,而且几乎没有风险。

第二步,做权重量化。结合精度评估,把模型量化到INT8甚至INT4。这一步能同时降低TTFT和TPOT,还能提高显存利用率,支撑更多并发。

第三步,调并发和batch。给服务设置合理的最大并发数,打开连续批处理,然后压测找出SLO边界。

第四步,做Prompt优化。压缩输入长度、增加缓存,把Prefill的负担降下来。

第五步,才考虑换卡或者加卡,以及上投机解码这类高阶技术。前四步做好,大多数项目的性能问题都能解决一大半。

6. 常见问题与排查技巧实录

6.1 本地部署配置到底怎么选才不踩坑

很多同学对“本地部署AI大模型配置”很感兴趣,但经常在选型时被显存吓住。其实只要记住一个粗略公式:模型权重显存约等于参数量乘以每个参数的字节数。比如7B模型,FP16精度,权重大约是14GB;INT8是7GB;INT4大概3.5GB。你选显卡时,显存至少要能放下权重,还得给KV Cache留余地。

KV Cache到底占多少?它和模型层数、头数、单头维度、并发数、序列长度都有关系。粗略估算公式是:2乘以层数乘以注意力头数乘以单头维度乘以batch大小乘以序列长度乘以每个字节数。7B模型一般有32层,假设hidden size是4096,那么每层KV Cache占用大约是4096乘以batch乘以序列长度乘以精度字节数,具体数值不展开算,但方向很清楚:序列越长、并发越大,KV Cache占用越夸张。

选卡时,不要只看显存够不够,还要看显存带宽。我的经验是,如果你主要做交互式聊天,尽量选带宽高的卡;如果你主要做离线批处理,显存容量和算力更值得关注。同一预算下,有时候两张中端卡比一张旗舰卡跑起来更稳,因为可以分摊负载,但也可能因为多卡通信带来额外开销,具体要看模型拆分方式。

6.2 并发一高,TTFT就飙升怎么办

这个问题几乎每个团队都会遇到。最直接的排查思路是:先看是不是排队了。

如果你用的是vLLM,看日志里有没有很多请求在队列里等待,以及max_num_seqs是不是设置得太小。如果请求已经进入GPU,但TTFT还是高,那就看Prefill阶段是不是被长Prompt拖累了。可以在请求进来前做一次Prompt长度分布统计,看看有没有极端长尾。

另一个容易忽略的地方是前端网关。有些团队在API网关层会做限流、鉴权、日志记录,这些操作本身也会增加延迟。如果网关和推理服务不在同一个网络区域,网络RTT也会算进TTFT。遇到并发一高TTFT就飙升,记得把网关指标和推理服务指标分开看。

实在不行,就只能降并发或者加实例。这里我特别想说一句:不要为了“看起来能扛高并发”而把最大并发数设得特别大,那样只会让所有用户的TTFT和TPOT一起变差。设定一个合理的并发上限,超过上限直接返回“系统繁忙”或者排队提示,体验反而更好。

6.3 首字挺快,但生成越来越慢是怎么回事

有一种很诡异的情况:第一秒看起来还行,TTFT只有几百毫秒,但生成到一半,速度肉眼可见地掉下来了。很多人以为是模型出问题了,其实这大概率是长上下文导致的KV Cache膨胀。

随着生成进行,KV Cache会越来越大,每一步Decode除了读模型权重,还要读整段KV Cache。序列越长,读KV Cache的时间越长,TPOT自然越来越慢。更严重的是,如果显存不够,框架可能会把KV Cache换出到CPU或者重新计算(recompute),这时候生成速度会断崖式下跌。

遇到这种情况,我的建议是:第一,限制最大输出长度,别让模型无限生成长文;第二,做显存监控,看是不是KV Cache占满之后触发了换出;第三,如果应用场景必须长输出,考虑滑动窗口注意力、稀疏注意力这类能控制KV Cache膨胀的模型架构。

6.4 指标测出来很好看,用户还是觉得慢

这应该是最扎心的一种情况。服务端TTFT和TPOT都达标了,用户还是觉得慢。这时候问题多半不在模型服务,而在前后端链路和感知设计。

先检查前端是否真的做好了流式渲染。有些前端框架默认是等接口全部返回再渲染,就算服务端是流式输出,用户还是什么都看不到。这种情况属于“后端快,前端拖后腿”,优化服务端没用,得改前端。

再检查网络链路。如果服务端和用户之间的网络RTT很高,比如跨地域,那就算服务端TPOT是20毫秒,用户端看到的每token间隔也可能是50毫秒以上。这种问题只能通过边缘节点、CDN或者就近部署来解决。

还有一个容易被忽略的因素是“心理感知”。当用户发出请求后,如果界面上没有任何反应,超过1秒就开始焦虑。就算你后面速度很快,等待的焦虑感已经产生了。很多产品会在等待阶段安排一些轻量交互,比如显示“正在思考中”,或者让光标有呼吸效果,这些设计虽然不算性能指标,但能显著改善体验。做性能优化不能只盯着数字,最终目标是让用户觉得“流畅”。

我自己这些年做AI应用性能优化的体会是:TTFT和TPOT不是两个孤立的技术指标,它们背后是一整套工程链路的选择和取舍。你选择了多大的模型、用什么推理框架、怎么设置并发、怎么设计Prompt,最后都会体现在这两个数字里。所以别把它们只当成监控面板上的数值,而是当作用户体验的“翻译官”。

最后再分享一个小经验:做性能评估前,先写下你业务场景的核心SLO。比如“聊天场景下,TTFT P95小于2秒,TPOT P95小于100毫秒”,后面所有优化围绕这个目标做,不要今天看TTFT不好就猛调TTFT,明天看TPOT不好又推翻重来。稳定的指标口径和清晰的业务目标是少走弯路的关键。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询