大模型性能测试新视角:从QPS到TTFT与FT的实践指南

发布时间:2026/10/21 2:53:15
大模型性能测试新视角:从QPS到TTFT与FT的实践指南 前阵子帮一个团队复核性能测试报告报告上写着“系统 QPS 达到 500性能表现优秀”。我顺手问了句那用户从输入到看到第一个字要等多久报告里没有。用户侧的真实反馈是“半天不出字出来了又很慢”。这就是当前 AI 性能测试最尴尬的地方我们还在用 2015 年的指标测 2025 年的系统。当 RAG 已经成为标配流式生成变成默认交互方式QPS 这种传统吞吐指标越来越失真TTFT首 Token 时延和 FT完成整段回答的时间这类指标必须走上台面。这篇文章我会把我这几年的测试复盘、踩坑和一套能直接落地的方案写清楚适合正在做 LLM 应用性能评估、稳定性测试和容量规划的团队参考。1. 为什么 QPS 在生成式 AI 面前“失真”三个前提被打破1.1 传统 QPS 的三个隐含前提QPS 这个概念设计出来的时候面对的是典型的“短请求-短响应”服务一请求进来服务端算一下立刻返回一个固定大小的 JSON然后连接就释放了。在这种模型里QPS 是一个干净、直观的吞吐指标它的前提有三个请求处理时间短、响应长度基本固定、服务端不保留太多跨请求状态。但大模型推理服务完全不是这套逻辑。一次流式生成请求可能持续 5 秒、20 秒甚至几分钟响应是分块吐出来的长度取决于生成内容输入上下文又可能因为 RAG 拼接了很长的检索结果。三个前提全部失效。这时候你把 QPS 放在压测报告的核心位置数字本身没问题但解读方式已经错了。我举个简化计算假设服务端能同时处理 100 个并发会话平均一次会话要跑 5 秒那最大吞吐大概就是 100 / 5 20 QPS。数字很难看。但同一时刻服务端可能正以每秒几十个 Token 的速度往外吐数据如果压测脚本把每个数据块都当成一次“成功响应”瞬时 QPS 又能统计出几千。你说这个服务到底算 20 QPS 还是几千 QPS两个数字都不能回答用户真正关心的问题——我得多快能看到第一个字、完整回答需要多久。1.2 从“服务端吞吐”到“用户体感”QPS 本质是从服务端资源消耗视角衡量系统容量它默认“请求处理完毕”就是“用户体验完成”。但生成式 AI 的交互是流式的用户看到的不是“请求完成”而是一个连续展开的过程等待第一个 Token、等待内容滚动、等待结束。这个过程里用户的心理时钟是完全不同的。用 QPS 作为核心指标还会掩盖两类问题。第一长尾延迟被平均掉大量请求在 200 毫秒内返回首包但个别请求因为排队、资源竞争要等 5 秒平均值仍然好看。第二响应长度差异造成的吞吐假象一个回答 20 个字的短问和一个生成 2000 字的长文对系统资源的消耗差一个数量级QPS 却把它们当成同一个“单位”来计数。这也是为什么我觉得“从 QPS 到 TT/FT”不仅仅是换两个指标而是测试思维要从“评估服务器能吞多少请求”转变成“评估用户在多并发、多场景下需要等多久”。性能测试的意义不是给一个漂亮的吞吐数字而是回答系统在什么条件下会变得让人不可接受瓶颈到底卡在哪一层。2. TTFT 与 FT从“每秒处理多少”转向“用户等多久”2.1 指标定义和测量边界TTFT 是 Time To First Token 的缩写我把标题里的 TT/FT 理解为一组指标TTFT 负责衡量“从请求发起后到收到第一个返回 Token 的时间”FT 负责衡量“从请求发起到收到流式结束标记的时间”。后者也可以理解成整段回答的完成时延。它们合在一起描述了一次生成式交互的完整生命周期。测量边界非常关键。TTFT 计时起点是客户端准备发送请求的那一刻终点是客户端收到第一个业务 Token。注意是“业务 Token”不是 TCP 层第一个数据包因为有些服务端会先把 HTTP 响应头、SSE 的注释行发过来真正的内容还在后面憋着。如果测试脚本把“收到第一个字节”当首 Token 时间TTFT 会被低估。FT 的终点也有讲究。是读到流式接口的[DONE]结束标记还是业务自己定义的结束符我建议统一为“客户端判定业务完成”的事件。在 SSE 里通常是结束标记在 WebSocket 里可能是服务端发来的特定消息。报告里如果不写清楚终点定义后续团队对齐数据时一定会吵架。2.2 为什么必须看分位数而不是平均值延迟分布从来不是正态的特别是有 GPU 调度、排队、缓存竞争存在的场景尾部尤其肥。一个典型现象是100 个请求里 90 个 TTFT 在 300 毫秒左右9 个在 800 毫秒还有 1 个因为排队等了 5 秒。平均值大约 490 毫秒看起来完全没问题。但真实感受是每 100 次请求就有一次让人抓狂而这个“抓狂请求”平均指标完全看不出来。所以最关键的两个统计值是 P95 和 P99。P95 定义是“把所有请求的 TTFT 排序取 95% 位置上的值”代表大多数用户会遇到的最差体验P99 则近似代表最倒霉的那个用户。我建议 SLO 以 P95 为主、P99 做参考不要用平均值为准。平均值适合做容量趋势分析不适合做体验承诺。统计分位数时还要注意窗口长度。如果压测脚本每 10 秒算一次 P95然后报告里写“P95 平均是 1.2 秒”这个数字是没有意义的因为你是把一组不同窗口的分位数值又取了一次算术平均。正确做法是在稳定阶段选取一个完整时间窗口把窗口内所有原始请求样本合并、排序再取 P95。样本量至少几千条不然尾部噪声会很大。2.3 最简单的流式压测脚本长什么样用伪代码演示一个测量思路跑通之后再替换成你们团队熟悉的 HTTP 客户端和并发框架。核心点只有一个必须逐块读取流式响应在读取第一块时记录 TTFT在流结束时记录 FT。# 伪代码测量一次流式请求的 TTFT 和 FT import time start time.perf_counter() ttft None async for chunk in stream_response(url, payload): if ttft is None: ttft time.perf_counter() - start # 这里可以顺便累计 chunk 的字节数或 Token 数 # 便于最后计算 Token 吞吐 # 收到流式结束标记后 ft time.perf_counter() - start return ttft, ft很多人第一次写压测脚本时会犯一个致命错误用普通 HTTP 客户端请求等整个响应完整返回后再取数据。这么写得到的“TTFT”其实等于 FT因为客户端根本没有在首包到达时打点。还有另一个极端统计脚本把服务端推来的每个 SSE 块都当成一次独立请求QPS 瞬间飙升但这个数字除了误导没有任何价值。正确做法是请求级别打点每个请求记录 (ttft, ft, token数, 是否成功) 四条信息。并发跑完后再统一算 P50、P95、P99 和平均吞吐。3. RAG 接入后性能测试的“被测对象”发生了质变3.1 检索链路从模型单点测试到链路测试RAG 成为标配之前性能测试的核心对象是模型推理服务本身发 prompt收 completion测生成性能。RAG 接入之后一次请求要经历 Query 向量化、向量库检索、重排序、上下文拼装最后才进入模型生成。任何一个环节变慢都会直接体现在 TTFT 上。我见过最典型的翻车案例团队辛辛苦苦压测 LLM 服务报告显示 TTFT 很优秀上线后用户却觉得响应慢。一排查才发现慢在检索链路。Query 向量化服务和向量检索服务各自只有几十毫秒看起来都不高但串联起来再加上网络往返就多出了 300 到 500 毫秒如果重排序再引入一次模型调用总时延可能翻倍。单点测模型当然发现不了这个问题。所以做 RAG 应用的性能测试首先要做的不是选指标而是把被测链路画出来客户端发起请求、查询向量化、检索、重排、构造上下文、模型排队、模型预填充、模型逐 Token 生成、返回流。每一跳都要有独立的埋点和耗时记录。用应用层中间件打点即可不需要侵入模型内部。3.2 上下文长度、缓存命中率、并发叠加的复杂效应RAG 对性能的最大影响是上下文变长。原本一段 500 Token 的 prompt挂上检索结果后可能膨胀到 3000 Token。模型预填充时间随输入长度显著增长TTFT 会上升长上下文还推高 KV Cache 内存占用降低并发承载能力。如果测试报告不记录每次的 prompt Token 数只写“TTFT 从 300ms 涨到 800ms”别人根本不知道为什么会涨。缓存命中率是另一个隐藏变量。RAG 应用普遍会做 query 缓存、文档检索缓存、甚至整段 prompt 的响应缓存。线上真实用户的 Query 千差万别命中率可能很低但如果压测脚本里只有几十条固定 Query 来回循环测出来的数据会虚高到离谱。我在后面第四部分会详细展开这个问题。并发场景下的叠加效应更要命。单个请求检索耗时 50ms看起来毫无压力但 100 个并发同时发起检索向量库连接池、重排序服务的线程池、embedding 服务的批次排队全都会开始堆积。如果不做链路级并发测试你看到的瓶颈永远只是“压测机不够努力”而不是“服务端有瓶颈”。3.3 怎么设计一个可复现的 RAG 基准场景RAG 性能测试最怕不可复现。今天语料库 1 万篇文档明天改成 10 万篇检索耗时完全不一样。所以我建议在压测前把环境变量固定下来固定文档库版本固定切分参数和 topK 设置固定向量检索服务的索引版本固定 prompt 拼装模板固定 max_tokens 和采样参数。Query 集合需要按业务类型设计而不是随机造几百条。我常用的分类方法是分成三组高频短问长度短、意图清晰检索通常能拿到强相关片段模糊长问长度长、完整度低检索结果不固定召回数量和内容波动大超长文档问答需要召回的片段多拼装后的 prompt 明显膨胀。每组至少准备几百条真实或接近真实的 Query并保证组内不重复降低缓存命中干扰。每一轮测试都要记录场景基线语料库大小、平均 prompt Token 数、平均检索到多少片段、缓存命中率是多少。有了这些基线两轮测试之间才有可比性。否则你压测报告上的“本次 TTFT 比上次慢 20%”根本说不清是模型变慢了、检索变慢了还是数据集中不同导致的。4. 真正卡住 AI 性能测试的五个隐蔽瓶颈4.1 压测客户端反客为主成了瓶颈我看到过很多团队压测 AI 应用并发加到 200 的时候客户端先崩了然后得出一个“系统只能扛 150 并发”的错误结论。为什么客户端先崩因为流式请求会让连接长时间占用压测机需要同时维护大量挂起的 Socket 连接内存、文件描述符、带宽都会被快速消耗。加上解析 SSE 本身存在 CPU 开销压测客户端半路失效非常常见。排查方法很简单压测时同时监控压测机的 CPU、内存、TCP 连接数并且看服务端接入层的连接数是否随并发线性增长。如果客户端已经 100% CPU服务端还很空闲说明瓶颈不在被测系统。应对方案是压测机横向扩展、复用连接池、减少每请求不必要的日志输出。另外压测脚本不要记录每个 chunk 的完整内容只统计长度即可否则内存会被原始数据撑爆。4.2 冷启动数据污染结论没有参考价值大模型服务冷启动阶段有一堆额外开销权重加载、CUDA 上下文初始化、KV Cache 分配、缓存建立。如果上来直接压测第一批请求的 TTFT 可能比稳定期慢 3 到 5 倍。更麻烦的是冷启动期的慢请求会污染整个测试窗口的分位数统计特别是当测试时长本来就不长时。我的习惯是正式压测前先跑 5 到 10 分钟的预热流量目标是让模型推理服务进入稳定状态让缓存命中分布也接近真实。然后丢弃预热阶段的所有采样只统计稳定阶段数据。如果被测的是一套新部署的模型服务预热时长还要更长。报告里要写明预热方式和丢弃数据的时间范围否则换个团队来复测结果一定对不上。4.3 缓存命中率混入测试样本数据虚高RAG 应用的性能测试还有一个隐蔽污染源响应缓存。如果压测 Query 集合规模很小比如只有 100 条多轮循环之后缓存命中率会非常高。服务端根本不需要重新检索、重新生成直接返回缓存结果TTFT 自然低得漂亮。但线上用户输入几乎不会完全重复缓存命中率远低于这个水平所以这份报告会误导容量规划。对策是双管齐下。第一Query 集合要足够大且有真实分布尽量采集线上日志里的真实 Query按频次加权采样来重建压测流量。第二明确区分“缓存命中场景”和“缓存未命中场景”分别压测和出报告。缓存命中场景适合评估服务端能支撑多大并发缓存未命中场景才是评估真实用户体验和系统容量的关键数据。4.4 指标统计口径不统一团队对不上账性能测试团队之间最经典的吵架原因是口径不一致。同样说“TTFT”有人从连接建立完成开始计时有人从发送请求体开始计时有人计入 DNS 解析和排队时间有人不计入。同样说“并发”有人理解为每秒新建请求数有人理解为同时在途的会话数。压测工具显示出的 200 并发跟服务端监控里看到的活跃会话数可能完全不是一回事。我建议团队内部先定一套统一口径写进测试规范TTFT 的起止点是“客户端准备发送请求前”到“客户端收到第一个业务 Token”FT 的终点是“流式结束标记到达”并发指“同一时刻处于进行中状态的会话数”分位数指“完整时间窗口内所有请求样本排序后的位置值”。任何报告的指标都必须附带口径说明写不清楚视为无效数据。这个规则可以省掉一大半扯皮。4.5 排队与重试压力一大先崩的是中间层当并发超过系统的实际处理能力时请求并不会立刻失败而是进入各种队列排队应用线程池队列、任务调度队列、GPU 推理队列。排队时间会被记进 TTFT但你从模型服务本身可能看不出异常因为真正的问题在更靠前的调度层和接入层。还有一个连锁问题很多客户端设置了较短的超时时间超时后自动重试重试请求又排到队尾导致更多请求超时形成重试风暴。我在一次压测里见过服务端实际只接到了 60% 的原始流量剩余 40% 全是重试请求最后所有请求一起超时服务端日志还排得满满当当。排查方法是压测时分层观察接入层的连接拒绝数、应用层的队列长度、推理服务的等待任务数、客户端的错误类型分布。只要发现超时和重试形成正反馈就要先放宽超时时间、限制重试次数再继续加压。5. 从指标到结论一套可以直接抄走的 AI 性能测试方案5.1 场景矩阵先行别先选工具拿到一个 AI 应用要做性能测试我建议你先别急着搭压测工具先把场景矩阵列出来。参考模板如下场景输入特征并发目标核心关注指标短问答prompt 200 Token 以内输出 100 Token 以内高并发TTFT P95、成功率长文生成prompt 500 Token 左右输出 1500 Token 以上中低并发FT P95、Token 吞吐RAG 问答固定语料库检索片段拼装到 prompt中并发检索耗时、TTFT P95、FT P95冷启动突发无预热服务刚启动低并发TTFT P99、失败率场景不同核心指标完全不同。短问答主要看首 Token 时延长文生成主要看完成时间和吞吐RAG 场景必须拆开检索与生成两端分别评估。场景矩阵的作用是让所有人先对齐“要回答什么问题”再去选具体工具避免拿一把榔头把所有钉子都敲一遍。5.2 指标组合和 SLO 设定单看 TTFT 或单看 FT 都有偏颇我建议固定用一组组合指标TTFT P95 和 P99评估交互响应速度FT P95评估整段回答完成速度成功率包括 HTTP 错误、超时、空响应Token 吞吐评估生成效率单位可以是 Token/秒检索链路耗时占比区分 RAG 场景下检索和生成的贡献QPS 可以保留在报告里但它不再是核心指标而是作为容量表象的一个参考值。设定 SLO 时不要拍脑袋可以从线上真实请求的历史分位数倒推。比如线上 TP95 的 TTFT 是 1.8 秒那就先定 1.5 秒作为目标留一点余量等优化到 1.2 秒再压紧。SLO 设定也建议区分场景短问答和长文生成的 FT 目标必须不同用同一个阈值会导致短期调优严重偏向某一类场景。5.3 压测执行与数据汇聚的实操细节最后说一下执行流程按顺序走完基本不会出大错固定环境模型版本、索引版本、显存配置、并发上限、max_tokens、日志级别。准备数据集和 Query 集记录语料库大小和平均 prompt Token 数。预热 5 到 10 分钟丢弃预热数据。分阶段加压从 10 并发开始每 3 到 5 分钟提升一档观察 TTFT 和 FT 是否出现拐点。找到容量拐点后进行 30 分钟以上的长稳测试重点看连接泄漏、内存增长、响应延迟是否随时间劣化。汇总报告给出稳定阶段的分位数、吞吐、瓶颈分层和时间线拆分。时间线拆分是我特别推荐的呈现方式。比如一次典型的 RAG 请求Query 向量化 80ms、向量检索 45ms、重排序 120ms、prompt 拼装 5ms、模型排队 880ms、模型预填充 650ms、模型生成 4200ms。这张时间线一出来瓶颈在哪个环节一目了然。没有这种拆解报告上只有“TTFT 从 500ms 涨到 3s”团队只能凭感觉猜。我个人的实际体感是AI 性能测试真正难的不是学会数 QPS 或者 TTFT而是愿意先承认用户体感和服务端资源不是一回事。测过了几轮假数据之后我现在每做一个压测场景都会先问三句话用户能等多久服务端此刻有没有在排队检索和生成各花了多少时间把这三个问题回答清楚性能报告就不太容易翻车。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询