资讯动态

【AIGC】大模型面试高频考点18-大模型压力测试指标:用TaoToken统一Key实测TTFT/TPOT/TPS

发布时间:2026/10/9 18:26:34 来源:尧图企业网站定制
1. 面试官为什么总盯着 TTFT、TPOT、TPS 这三个数大模型压力测试指标里TTFT、TPOT、TPS 是最容易被追问、也最容易答糊的三个。它们分别回答三个不同的问题用户等多久才看到第一个字、字与字之间流得顺不顺、整台服务每秒到底能吐多少 token。面试时如果只背定义面试官一句“那你压测时怎么采、并发上去之后哪个先崩”就能把你问住。这篇就把这三个指标拆开用 TaoToken 统一 Key 作为调用入口写一套本地能跑的压测脚本把并发梯度、指标采集、结果解读一次讲透。先说清楚这三个指标到底在测什么。TTFTTime To First Token是从你发出请求到收到第一个 token 的时间它包含排队、预填充prefill和首 token 解码的全部开销直接决定用户对“快不快”的第一印象。TPOTTime Per Output Token是相邻两个 token 之间的平均间隔反映解码阶段的流畅度TPOT 越大用户越觉得字是一个一个往外蹦。TPSToken Per Second是每秒生成的 token 数衡量整体吞吐长文本生成任务里它直接决定任务多久跑完。这三个指标不是孤立的。TTFT 主要吃 prefill 算力和排队调度TPOT 主要吃 decode 阶段的显存带宽和 batch 策略TPS 则是两者的综合结果。压测时并发一上来通常先看到 TTFT 飙升请求排队再看到 TPOT 变大batch 变大后单请求解码变慢最后 TPS 增长放缓甚至掉头向下——这就是所谓的吞吐拐点。面试里能把这层因果关系讲出来比背十个定义都管用。适合谁看正在准备大模型相关岗位面试的同学、需要给自家推理服务做容量评估的工程师、以及想搞明白“为什么我的服务一上并发就卡”的开发者。下面所有脚本都可以直接复制运行你只需要一个能调用的 API 通道。2. 用 TaoToken 统一 Key 搭压测入口省掉多模型切换的麻烦做压测最烦的一件事是不同模型要走不同厂商的 SDK、不同的鉴权方式、不同的返回格式脚本写一半全耗在适配上了。我这次用 TaoToken 的统一 Key 和统一 API 通道来解决这个问题——它对外暴露的是 OpenAI 兼容的接口你换模型只需要改一个 model 字段压测脚本本身不用动。这对做对比压测特别友好同一份脚本把 model 从 A 换成 B就能横向比 TTFT/TPOT/TPS。TaoToken 在这里扮演的角色是统一的调用入口不是替代你的压测工具。压测逻辑、并发控制、指标采集还是你自己写它负责把请求稳定地送到模型并流式返回 token。因为返回是标准 SSE 流式格式我们才能在客户端精确记录“第一个 token 到达时间”和“每个 token 到达时间”这两个时间戳正是算 TTFT 和 TPOT 的原始数据。接入前你需要准备三样东西这也是后面所有配置的基础Base URLhttps://taotoken.net/api所有请求都打这个地址注意不要带多余的路径后缀。API Key在控制台创建形如sk-开头的一串字符压测时建议单独建一个 Key 方便统计用量。Model ID你要压测的模型标识比如对话模型或代码模型的具体名字填在请求体的model字段里。获取 Key 的入口在控制台的 API Keys 页面创建后复制保存页面只完整显示一次。如果你还没决定压哪个模型可以先去模型对话页面手动发几条请求确认通道通、返回正常再进压测环节。对于要长期跑压测或做 Agent 评测的场景Coding Plan 会更划算因为压测本身 token 消耗不小。这里要提醒一句压测请控制并发和总请求量别把公共通道当成无限资源猛打。合理的做法是从低并发起步逐步加压观察指标变化即可目的是拿到数据、理解趋势不是把服务打挂。3. 可复制的压测配置请求模板、并发梯度与采集脚本这一节是全文的核心给你一份能直接跑的 Python 压测脚本。它做三件事按并发梯度发请求、流式接收并记录每个 token 的时间戳、最后算出 TTFT/TPOT/TPS 的均值与分位数。依赖只有openai和numpy装完就能跑。先看配置部分。把下面这段存成config.json路径和字段名保持原样脚本会直接读它{ base_url: https://taotoken.net/api, api_key: sk-你的Key填这里, model: 你的模型ID, prompt: 请用300字介绍大模型推理中的prefill和decode两个阶段, max_tokens: 300, temperature: 0.7, concurrency_levels: [1, 2, 4, 8, 16], requests_per_level: 8, timeout: 60 }字段说明一下concurrency_levels是并发梯度从 1 到 16 逐级加压requests_per_level是每个并发档位发多少个请求样本太少分位数没意义建议至少 8 个max_tokens控制生成长度压测时固定它才能公平比较 TPS。temperature设 0.7 是为了让输出长度有自然波动更接近真实场景。下面是采集脚本bench.py核心是用流式接口逐 token 打时间戳import json import time import asyncio import numpy as np from openai import AsyncOpenAI with open(config.json, r, encodingutf-8) as f: CFG json.load(f) client AsyncOpenAI( base_urlCFG[base_url], api_keyCFG[api_key], timeoutCFG[timeout], ) async def one_request(): t_send time.perf_counter() t_first None t_last None token_times [] n_tokens 0 try: stream await client.chat.completions.create( modelCFG[model], messages[{role: user, content: CFG[prompt]}], max_tokensCFG[max_tokens], temperatureCFG[temperature], streamTrue, ) async for chunk in stream: delta chunk.choices[0].delta if delta and delta.content: now time.perf_counter() if t_first is None: t_first now token_times.append(now) t_last now n_tokens 1 except Exception as e: return {ok: False, err: str(e)} if t_first is None or n_tokens 2: return {ok: False, err: no token received} ttft (t_first - t_send) * 1000 gaps np.diff(token_times) * 1000 tpot float(np.mean(gaps)) tps (n_tokens - 1) / (t_last - t_first) total (t_last - t_send) * 1000 return { ok: True, ttft: ttft, tpot: tpot, tps: tps, total: total, n_tokens: n_tokens, } async def run_level(concurrency, n_req): sem asyncio.Semaphore(concurrency) async def worker(): async with sem: return await one_request() tasks [worker() for _ in range(n_req)] return await asyncio.gather(*tasks) def summarize(results): ok [r for r in results if r[ok]] if not ok: return None ttft [r[ttft] for r in ok] tpot [r[tpot] for r in ok] tps [r[tps] for r in ok] return { n_ok: len(ok), n_fail: len(results) - len(ok), ttft_p50: float(np.percentile(ttft, 50)), ttft_p95: float(np.percentile(ttft, 95)), tpot_p50: float(np.percentile(tpot, 50)), tpot_p95: float(np.percentile(tpot, 95)), tps_mean: float(np.mean(tps)), tps_sum: float(np.sum(tps)), } async def main(): for c in CFG[concurrency_levels]: results await run_level(c, CFG[requests_per_level]) s summarize(results) print(f并发{c} - {json.dumps(s, ensure_asciiFalse)}) if __name__ __main__: asyncio.run(main())跑之前先pip install openai numpy然后python bench.py。脚本会按并发 1、2、4、8、16 逐级打印结果。注意tps_sum这一列它是该并发档位下所有请求 TPS 之和代表集群总吞吐和单请求 TPS 要分开看——面试里区分这两个概念是加分项。关于并发梯度的设置逻辑从 1 开始是为了拿到“无竞争”的基线值后面每档翻倍是为了快速找到拐点。如果你压的是长上下文模型可以把max_tokens调大、并发档位调小因为长序列的 prefill 开销会主导 TTFT。反过来压短问答场景就把max_tokens压到 50 以内重点看 TTFT 和 QPS。4. 验证请求与成功结果一次真实压测的数据长什么样脚本跑通后你会看到类似下面这样的输出数值是示例实际以你的通道和模型为准并发1 - {n_ok: 8, n_fail: 0, ttft_p50: 420.5, ttft_p95: 510.2, tpot_p50: 18.3, tpot_p95: 22.1, tps_mean: 54.6, tps_sum: 436.8} 并发2 - {n_ok: 8, n_fail: 0, ttft_p50: 455.1, ttft_p95: 620.7, tpot_p50: 19.8, tpot_p95: 25.4, tps_mean: 50.5, tps_sum: 808.0} 并发4 - {n_ok: 8, n_fail: 0, ttft_p50: 530.9, ttft_p95: 810.3, tpot_p50: 23.6, tpot_p95: 31.2, tps_mean: 42.4, tps_sum: 1356.8} 并发8 - {n_ok: 8, n_fail: 0, ttft_p50: 720.4, ttft_p95: 1250.6, tpot_p50: 31.5, tpot_p95: 45.8, tps_mean: 31.7, tps_sum: 2028.8} 并发16 - {n_ok: 7, n_fail: 1, ttft_p50: 1180.2, ttft_p95: 2400.9, tpot_p50: 48.9, tpot_p95: 72.3, tps_mean: 20.4, tps_sum: 2284.5}先看怎么验证请求是成功的。n_ok等于requests_per_level说明全部成功n_fail出现非零就要去查错误。上面并发 16 时挂了 1 个可能是超时或限流这时候要结合错误信息判断而不是直接下结论说服务不行。再读数据。并发从 1 到 8tps_sum从 436 涨到 2028说明服务还在扩容吞吐随并发线性增长这是健康区间。但tps_mean单请求 TPS从 54.6 掉到 31.7说明每个请求变慢了——这就是 batch 变大后 decode 被摊薄的代价。到并发 16tps_sum只从 2028 涨到 2284增幅明显放缓同时ttft_p95冲到 2400ms、tpot_p95到 72ms还出现了失败请求。这个点就是吞吐拐点再加并发吞吐涨不动延迟和错误率却继续恶化。面试里怎么讲这组数据你可以说TTFT 的 p95 比 p50 涨得快说明排队是长尾的主要来源TPOT 随并发单调上升说明 decode 阶段是瓶颈TPS 总和在并发 8 之后接近饱和说明当前配置下的最优并发在 8 附近。调优方向也就出来了——要么加推理实例横向扩容要么优化 batch 调度策略要么对长请求做限流保护。如果你只想快速验证通道和指标采集逻辑对不对可以先跑并发 1 的单档确认n_ok全绿、TTFT 在几百毫秒量级、TPOT 稳定再放开梯度。手动验证时也可以去模型对话页面发一条同样的 prompt肉眼感受一下首字延迟和出字速度和脚本数据对得上就说明采集没问题。5. 压测常见报错排查401、超时、空 choices 怎么定位压测脚本报错和普通调用报错不太一样因为并发一上来很多问题是并发触发的。下面按真实会遇到的报错逐个说。401 Unauthorized / invalid api key最常见。先检查config.json里的api_key有没有多余空格、有没有把 Key 复制漏字符。TaoToken 的 Key 是sk-开头如果你用的是环境变量注入确认变量名没写错。还有一种情况是 Key 被禁用或额度耗尽去控制台 API Keys 页面看一眼状态。注意 Base URL 必须是https://taotoken.net/api写成别的路径也可能导致鉴权失败。Connection timeout / Read timed out并发高时请求排队超过timeout设置就会抛这个。先把timeout从 60 调到 120 试试如果还是超时说明该并发档位已经超过服务承载能力这本身就是有价值的压测结论不用强行压过去。排查时可以把并发降回上一档确认是并发问题还是通道问题。返回里 choices 为空 / no token received流式接口偶尔会返回只有 role 没有 content 的 chunk脚本里已经用if delta and delta.content过滤了。如果整条请求一个 content 都没有可能是max_tokens设得太小被截断或者 prompt 触发了内容策略。把max_tokens调到 100 以上再试同时换一条中性 prompt 排除输入问题。local proxy failed / 代理相关报错如果你本地配了网络代理OpenAI SDK 可能会走代理导致连接异常。压测时建议清掉HTTP_PROXY、HTTPS_PROXY环境变量让请求直连。这个报错和通道本身无关是本地网络配置问题。OAuth / 鉴权方式不匹配有些工具默认走 OAuth 或别的鉴权流程而 TaoToken 用的是 API Key 方式。如果你在 Cline、CC Switch 这类工具里配置记得选 API Key 模式Base URL 填https://taotoken.net/apiModel ID 填你要用的模型。这三件套Base URL Key Model ID缺一不可配错任何一个都会报鉴权或模型不存在。模型不存在 / model not foundmodel字段拼错了或者你填的模型 ID 当前通道不支持。去文档页核对可用的 Model ID 列表复制粘贴而不是手打。排查顺序建议固定成先看 HTTP 状态码401 查 Key、404 查模型、429 查限流、5xx 查服务端再看是单请求失败还是并发下才失败最后看是通道问题还是本地网络问题。把这套顺序讲出来面试官会觉得你是真压过。6. 把指标讲成故事面试与实战里的调优方向压测的终点不是一堆数字而是能回答“这个服务能扛多少并发、瓶颈在哪、怎么优化”。TTFT 高优先查 prefill 和排队方向是加实例、优化调度、缩短输入TPOT 高优先查 decode 阶段的显存带宽和 batch 大小方向是调 batch 策略、用量化、换更快的解码 kernelTPS 上不去先看是单请求慢还是总吞吐饱和前者优化单请求后者横向扩容。面试里被问到“你怎么定容量”可以这样答先用低并发拿基线再按梯度加压找到 TPS 拐点取拐点前 70% 的并发作为安全水位同时保证 p95 TTFT 在可接受范围内。这套方法不依赖具体模型换任何服务都能用。最后给个实操建议把每次压测的config.json和输出结果按时间存档模型版本、通道、并发梯度都记下来。下次服务变慢时翻出历史数据一对比是模型换了、通道抖了还是并发涨了一目了然。压测这件事数据积累比单次结果值钱得多。

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

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

免费获取报价 →
↑