☰
大模型推理速度全解析:从FTL/MLT指标到压测方法与优化实践
2026/10/19 4:26:30 网站建设 项目流程

大模型的推理速度,最近几乎成了所有技术群里的高频词。有人盯着反复横跳的tokens数字发愁,有人在纠结到底该用并发压测还是单线测试,还有人压根分不清“生成速度”和“响应速度”是不是一回事。这篇东西,我想把推理速度这件事从里到外拆一遍,包括它到底受什么影响、怎么测才靠谱、测完怎么看数据,以及那些文档里不会写的坑。适合刚接触大模型服务的开发者,也适合正在做技术选型或成本评估的团队参考。

1. 先搞清楚推理速度到底慢在哪

很多人一上来就测吞吐量,测完发现数字不好看,就开始调并发、换显卡,折腾一圈还是没头绪。其实推理延时这件事,拆开看就两块:计算耗时和排队耗时。前者是你的硬件和模型结构决定的,后者是你服务端调度和并发策略决定的。两者混在一起测,你根本分不清瓶颈在哪。

1.1 两个关键指标:FTL 与 MLT

业内聊推理速度,最常挂在嘴边的是两个缩写:FTL(首Token延迟,First Token Latency)和MLT(平均Token生成延迟,Mean Latency per Token)。

FTL指的是从请求发出去到第一个token生成的时间。这个数字最直观地反映“用户感知的响应速度”。它包含网络耗时、鉴权耗时、请求排队、预填充(Prefill)计算、甚至显存分配的开销。你要是做交互式应用,比如聊天机器人、Copilot 类产品,FTL 就是用户等第一句话开口的时间,直接决定体验分。

MLT指生成后续每个token的平均耗时。这个数字反映的是“持续流式输出的稳定性”。MLT 若是 80ms,换算成大家更熟悉的“每秒输出token数”就是 1000 / 80 = 12.5 tokens/s。这个速度够不够看场景——做一个实时会议转写,12.5 tps 勉强能跟得上语速;但做代码补全,用户手速也就每秒几个字符,反倒是 FTL 更重要。

提示:一个常见的误区是拿“总延时/总Token数”当作MLT。总延时里包含了FTL,直接除会把生成速度算慢一大截。正确的做法是分别统计。

1.2 系统层面的瓶颈:TPOT 与并发膨胀

再往下拆,生成阶段还有一组指标:TPOT(Time Per Output Token),它受并行性、批处理大小和显存带宽的共同影响。TPOT 和 MLT 的区别在于,TPOT 是系统单次输出 token 的耗时,而 MLT 是从用户视角看整个生成阶段的平均延迟。两者在并发场景下数字会拉开——并发越高,排队带来的额外等待越多。

我见过团队在单并发下测出很漂亮的 30ms/token,一上到 20 并发,MLT 直接飙到 300ms。问题就出在排队,而不是算力不够。所以测试推理速度,必须分“无竞争基准”和“有竞争压力”两套数据,前者看核心能力,后者看系统设计。

完整记忆这套指标,你在测试报告里写“FTL 235ms / MLT 45ms / TPOT 38ms / 20并发下P95 TPOT 92ms”,内行人一看就知道你的系统状态。

2. 测试工具与方法选型

推理速度测试的工具有不少,选型的逻辑不是“哪个流行用哪个”,而是“你要哪一层的数据”。我按下面两类来讲。

2.1 黑盒测试:API 层压测

黑盒测试不关心服务内部怎么调度,只关心请求进来响应是什么。最常用的工具就是JMeter和wrk。对于纯 HTTP 接口、需要组装 header 或 body 的场景,JMeter 的图形界面和 JSON 断言能力更顺手;对于纯流式接口、需要看数据包到达节奏的场景,wrk 配上 lua 脚本反而更轻盈。

这里有个容易踩的坑:大部分压测工具默认不区别流式响应。模型按流式(SSE)逐token吐出内容,工具却可能把整段响应收到结束才记录结束时间。这样你的“响应时间”统计的是全部生成完的时间,FTL 和 MLT 完全没法算。想测对,得自己处理 SSE 数据流:标记 first chunk 到达时间作为 FTL,全程 chunk 数量除总生成时长作 MLT。

2.2 白盒测试:推理框架内部观测

白盒测试是直接在推理服务日志或框架层观测,目前主流推理引擎都提供详细延时统计。比如vLLM、TensorRT-LLM、SGLang,它们的日志里自带 prefill 耗时、decode 耗时、调度等待耗时。如果你用的框架是其中之一,强烈建议直接把日志的耗时字段透出到监控系统,不要只依赖外部压测。

这一层的意义在于定位归属。外部 API 测出 FTL 高,你无法直接判断是网络慢、调度慢还是 prefill 慢。但白盒观测里能看到调度器队列长度、prefill 时长、decode 时长的具体数值,一对比就知道问题在哪一环。

我通常在测试时把黑盒、白盒同时开——黑盒数据跑业务指标,白盒数据写根因分析。两者对不上,说明客户端统计口径或中间链路有偏差,本身就是一个值得排查的问题。

2.3 自研脚本与按“生命周期”采集

市面上大多数人忽略的另一个问题是:模型输入输出的上下文长度不同,速度差异巨大。长上下文场景下 prefill 的计算量会随 prompt 长度线性增长,生成阶段的显存占用也随之加大。你用 512 token 的 prompt 测出来的速度,和真实业务 2K 上下文的场景完全不是一回事。

所以更稳妥的做法是自己写脚本按“生命周期”采集:固定 prompt 长度(比如 2K)、固定输出长度(比如 1K token),然后测 FTL、MLT、总延时。脚本逻辑简单,但关键是把每个阶段拆开记录:

import time import requests def test_completion(url, prompt, max_tokens): start = time.perf_counter() first_token_at = None tokens = [] with requests.post(url, json={"prompt": prompt, "stream": True, "max_tokens": max_tokens}, stream=True, timeout=300) as resp: for line in resp.iter_lines(): if not line: continue if first_token_at is None: first_token_at = time.perf_counter() if b"[DONE]" in line: break tokens.append(line) total_time = time.perf_counter() - start return { "ftl_ms": (first_token_at - start) * 1000, "mlt_ms": (total_time - (first_token_at - start)) * 1000 / max(len(tokens), 1), "total_s": total_time }

这个脚本没有做鉴权和容错,但核心思路是关键:首Token时间单独记录、生成阶段时间单独记录。拿到这三个数,你就能分析出输入长度增加时,慢的到底是 Prefill 还是 Decode。

3. 完整测试流程与实操记录

下面这套流程是我实际跑过很多遍的,直接照着做就行。它覆盖了环境准备、单并发基准、并发压测和结果统计四个环节。

3.1 环境准备与参数设置

测试前锁死下面这些变量,否则数据没法横向比较:

  • 模型版本、精度(FP16/BF16/INT8/INT4),不同精度速度差异很大。BF16 通常是在精度和速度间的平衡点,INT4 能显著提升生成速度但会牺牲一定效果。
  • 推理框架版本及启动参数(max batch size、max sequence length、显存分配策略)。max batch size设得越大,并发调度时的吞吐上限越高。
  • GPU 型号与数量。同型号 GPU 下,驱动版本和 CUDA 版本也可能造成 5% 左右的性能浮动。
  • 输入 prompt 长度与输出 max_tokens 上限。

给一个可复现的参考配置示例:使用 2 张某主流数据中心级 GPU 跑 7B 模型,BF16 精度,单卡 max sequence length 8192,单请求输出 512 tokens。这类配置下 7B 模型的 MLT 通常会落在 30-60ms 区间,但实际值会因框架和驱动版本浮动。

3.2 并发与输出长度数据记录

完整跑一轮测试,要覆盖的输出长度至少取三档:短输出(64 tokens)、中等输出(512 tokens)、长输出(2048 tokens)。每一档分别测 1 并发、4 并发、8 并发、16 并发。记录的数据建议做成如下表格:

测试场景输出长度并发数FTL(ms)MLT(ms)TPOT(ms)总延时(s)吞吐(tokens/s)
短输出-低并发64118042382.130.5
短输出-中并发64824088763.2160.0
中输出-低并发5121210454112.341.6
中输出-高并发5121636015013225.4322.6

这张表的重点是回归关系:并发翻倍,MLT 涨多少?输出长度翻倍,FTL 涨多少?如果 MLT 随并发线性恶化,说明调度或显存带宽到了上限;如果 FTL 随输入长度线性恶化,说明 Prefill 阶段是瓶颈。

3.3 用统计口径消除“偶发毛刺”

单次测量不可靠。必须跑多轮取分位数,推荐至少跑 20 轮,记录P50、P90、P95。最终报告中延时数据用 P95 作为承诺值,P50 作为典型值。只报平均值的测试报告没有参考意义——大模型推理的延时分布带有典型的长尾特征,平均值容易掩盖偶发的调度抖动。

另一个容易忽略的细节是预热阶段。刚部署的模型服务,首次请求会触发额外的显存分配、CUDA 上下文初始化甚至权重页表加载,速度显著慢于稳定态。正式测试前先发 3-5 个请求做预热,否则 FTL 数据会被严重拉高。

3.4 测试过程实录

我在一次实际的框架对比测试中,跑 7B 模型、输出 512 tokens、单并发,某框架测得 FTL 190ms、MLT 45ms;另一框架 FTL 210ms、MLT 32ms。如果只看 FTL,前者领先;看 MLT,后者明显胜出。两种框架的调度策略不同,一个优先保证首字响应,一个优先优化生成吞吐。这个差异直接决定了选型方向——如果业务是客服对话,优先 FTL;如果业务是离线批处理总结,优先 MLT。

我也是从这组数据之后开始坚持分场景选指标的:先问业务形态,再定核心指标,最后才跑测试。

4. 测试结果分析与优化方向

数据测完了,更关键的是会看、会用。这里给三个判断框架。

4.1 判断敏感面

如果并发从 1 提至 8,FTL 涨幅超过 50%,说明请求排队是服务端主要矛盾。优先优化调度策略和最大批处理大小,而不是换更快 GPU。换成更大显存的卡能缓解显存交换,但如果排队时长占了延时大头,换卡就是浪费预算。

如果输出的 MLT 随上下文长度增长明显上涨,说明 Decode 阶段显存带宽已经吃满,此时优化方向应该是减少 KV Cache 占用或启用连续批处理(Continuous Batching),让多个请求共享显存空间。连续批处理的价值在于生成阶段并不占用全部计算资源,不同请求的 prefill 与 decode 可以在同一卡上交错执行,从而提高整体利用率。

4.2 定位具体瓶颈

测试数据如果出现“FTL 正常但整体响应偏慢”,问题往往在生成阶段时长过长。这时切回白盒数据,看 Decode 阶段的平均耗时和显存带宽利用率。如果“生成速度快但总延时高”,则优先查网络传输、跨节点调度和响应体序列化处理。

这里分享一个判断技巧:把你模型的上下文长度和输出长度加起来,如果总和已经接近显存容量上限,那无论怎么调参,速度都上不去。这种场景只能减长度、减精度、或者换大显存设备。

4.3 精度与速度的取舍

很多人舍不得从FP16切到INT4,怕精度损失。实际测试中,对通用对话和摘要类任务,INT4 与 FP16 的效果差异经常小到用户无感知,但生成速度可以提升 30% 以上,显存占用也能降低接近一半。如果你的业务场景对敏感细节(比如代码生成、数学推理)要求苛刻,建议保留 FP16/BF16 或做混合精度:关键层用高精度,非关键层用低精度。

我在实际选择中遵循一个原则:先按业务容忍度确定最小可用精度,再在该精度下榨干性能。顺序反了,容易白花钱。

5. 测试过程常见问题与避坑清单

这里把踩过的坑直接列出来,每一条都对应真实事故。

5.1 坑:请求重试导致数据失真

压测工具默认遇到超时或连接重置会重试,重试会增加额外排队压力,导致后续请求延时继续攀升,最终数据呈现“假性雪崩”。解决方法是压测配置里关闭自动重试,或设置仅对连接级错误重试、对读超时不重试。

5.2 坑:长上下文测试只测一次

长上下文输出的测试特别耗时,很多人图省事只跑一次。但长输出场景下显存换页、KV Cache 淘汰策略的偶发抖动,会让单次数据上下浮动 20% 以上。长上下文场景至少跑 5 轮,并且重点看最大延时,而不是平均延时。

5.3 坑:QPS 指标被吞吐指标偷换

有些报告写“系统 QPS 达到 100”,细看其实是并发总吞吐 100 tokens/s。这两个数字差了上百倍。对外汇报时注意区分是“每秒请求数”还是“每秒生成 token 数”。HTTP 请求维度的 QPS 大模型通常做不高,因为一个请求会产生几十到几千个 token 的输出;真正有业务意义的是tokens/s 吞吐。

5.4 测试环境不隔离

推理速度测试最怕共享机器。隔壁一个跑数据预处理的任务,能把你 MLT 拉高一倍还多。条件允许时用独立节点测试。如果必须在共享环境测,用nvidia-smi记录整机 GPU 利用率和显存占用曲线,识别异常抢占。

5.5 SSE 解析忽略心跳包

流式接口会周期性发送心跳空包保持连接。如果脚本把空包计入有效 token,MLT 会被严重拉高。解析 SSE 数据必须过滤掉非 data 行和无内容的注释行,只统计实际包含文本的 chunk。

5.6 并发数不等于连接数

压测 16 并发可能只是 16 个连接,模型服务端实际同时处理的批大小可能只有 4。看清楚服务端日志里实际 batch size,而不是压测工具的发包并发。

6. 测试报告怎么写给别人才有用

最后讲报告组织。一份好的推理速度测试报告,不需要长篇大论,但必须有这几个部分:

  • 测试环境:硬件、驱动、框架版本、模型精度、输入输出长度设定。缺失环境参数的测试报告等于不可复现。
  • 核心指标:单并发基准和规格化并发下的 FTL、MLT、TPOT、吞吐。每个指标注明统计口径和分位数。
  • 对比基线:如果有历史数据或竞对产品数据,必须标注基线测试条件。不同 prompt 长度、不同输出长度下的对比才有意义。
  • 瓶颈结论:用 3-5 句话写明慢在哪,是调度、prefill、decode、网络还是显存带宽。
  • 优化建议:给出可执行的下一步,比如调整批处理大小、切换调度策略、降低精度、扩容方案。

我的习惯是报告末尾永远附一张“测试原始数据可复现说明”,写出 prompt 样例、请求参数JSON、压测命令,方便团队内部复现或第三方审计。报告是给人决策用的,但原始数据是给信任用的。

注意:不同团队使用不同的推理框架时,同样的硬件和模型测出完全不同的数据是正常的。别急着下“谁强谁弱”的结论,先对齐测试条件再说。框架之间的性能差异,往往比人们预想的大得多。

从个人经验来说,做推理速度测试最忌讳的是追求“最好看的数据”,真正有用的是“最能解释业务的延时分布”。把上面这套流程完整跑一遍,你对服务性能的掌控力会明显上一个台阶。以后再遇到“为什么这么慢”的灵魂拷问,你至少能拿出一整份数据链条来说话,而不是靠猜。

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

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

立即咨询