资讯详情

大语言模型参数指标详解:TTFT、TPOT、E2E Latency与吞吐量如何影响推理体验

📅 2026/10/18 20:18:57 | 华诺云谱 👁 阅读
大语言模型参数指标详解:TTFT、TPOT、E2E Latency与吞吐量如何影响推理体验
1. 为什么你调用的模型“感觉慢”四个指标的真实分工很多人第一次做 AI 应用选型时判断标准只有一句“这个模型快不快”。但真正上线后你会发现用户抱怨的“慢”其实分好几种有人是点了发送后半天不吐字有人是第一个字很快但后面像挤牙膏还有人是一次性批量跑几千条数据时总耗时长得离谱。这背后对应的是四个完全不同的指标——TTFT、TPOT、E2E Latency 和吞吐量。TTFTTime to First Token首 Token 时延衡量的是从你发出请求到收到第一个 token 的时间它决定了用户“等待感”的强弱。TPOTTime Per Output Token单 token 输出耗时衡量的是首 token 之后每多生成一个 token 平均要花多久它决定了流式输出“顺不顺”。E2E Latency端到端时延是整条请求从发出到最后一个 token 落地的总时间。吞吐量则是单位时间内系统能处理多少 token 或多少请求它决定了你的服务能扛多少并发。这四个指标不是孤立的它们之间存在明显的相互制约。你把 batch size 调大吞吐量上去了但 TTFT 和 TPOT 往往会被拖慢你为了压低 TTFT 而减少批处理吞吐量又会掉下来。所以选型和调优的本质是在你的业务场景里找到这四者的平衡点。这篇文章会先讲清楚每个指标的定义和测量方式然后结合 TaoToken 统一 API 通道给你一套可复制的压测脚本和指标采集配置让你能亲手验证这些指标随并发变化的趋势。2. TaoToken 统一 API 通道为指标采集准备一个稳定入口要做推理性能测量第一步是有一个稳定的、可编程调用的模型入口。TaoToken 提供统一 API 通道把不同模型的调用方式收敛成一套兼容接口这样你在写压测脚本时不用为每个模型改一遍代码。官网地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 入口是 https://taotoken.net/api 。在开始采集指标之前你需要先拿到 API Key。进入控制台的 API Keys 页面https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 创建一个新的 Key。创建时建议给它起一个能区分用途的名字比如perf-test-ttft这样后面你同时跑多组压测时不会搞混。Key 只在创建时完整显示一次复制后先存到本地环境变量里不要直接写进脚本明文。拿到 Key 之后你需要确认两件事Base URL 和 Model ID。Base URL 就是https://taotoken.net/apiModel ID 则取决于你要测哪个模型。你可以在模型对话页面https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 先手动发一条消息确认模型能正常返回然后在接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 里找到对应的模型标识。这里有一个容易被忽略的点指标采集对网络稳定性很敏感。如果你在本地用不稳定的网络去测 TTFT测出来的数字里混了大量网络抖动根本没法用来做模型选型。所以压测脚本最好跑在一台网络环境相对干净的机器上或者至少保证压测期间没有其他大流量任务在抢带宽。我试过在同一台机器上一边下载大文件一边跑 TTFT 测试结果首 token 时延直接从 400ms 飙到 2s 以上完全是噪声。另外TaoToken 的 API 兼容 OpenAI 风格的调用方式这意味着你可以直接用 OpenAI SDK 或者任何兼容的 HTTP 客户端来发请求。对于指标采集来说这一点很关键因为流式响应的解析逻辑可以复用现成的库你只需要在解析过程中打时间戳就行。下面一节我会给出完整的可复制配置和脚本。3. 可复制配置压测脚本与指标采集参数这一节给你一套可以直接跑的配置。核心思路是用 Python 写一个压测脚本通过流式接口发请求在收到第一个 token、每个后续 token、以及最后一个 token 时分别打时间戳最后算出 TTFT、TPOT、E2E Latency 和吞吐量。先准备一个配置文件把 Base URL、Key 和 Model ID 集中管理。你可以用 JSON 格式路径放在项目根目录的perf_config.json{ base_url: https://taotoken.net/api, api_key: sk-your-key-here, model_id: your-model-id, max_tokens: 256, temperature: 0.7, stream: true }注意api_key这里只是占位实际运行时建议从环境变量读取避免把 Key 提交到代码仓库。你可以在脚本里用os.environ.get(TAOTOKEN_API_KEY)来取。接下来是压测脚本bench_llm.py。它用requests库发流式请求逐行解析 SSE 数据记录关键时间点import json import time import os import requests from concurrent.futures import ThreadPoolExecutor, as_completed with open(perf_config.json, r) as f: cfg json.load(f) API_KEY os.environ.get(TAOTOKEN_API_KEY, cfg[api_key]) BASE_URL cfg[base_url].rstrip(/) MODEL_ID cfg[model_id] def one_request(prompt, max_tokens256): url f{BASE_URL}/v1/chat/completions headers { Authorization: fBearer {API_KEY}, Content-Type: application/json } payload { model: MODEL_ID, messages: [{role: user, content: prompt}], max_tokens: max_tokens, temperature: cfg[temperature], stream: True } t_start time.perf_counter() t_first None t_last None token_count 0 with requests.post(url, headersheaders, jsonpayload, streamTrue, timeout120) as resp: resp.raise_for_status() for line in resp.iter_lines(decode_unicodeTrue): if not line or not line.startswith(data:): continue data line[5:].strip() if data [DONE]: break try: chunk json.loads(data) except json.JSONDecodeError: continue delta chunk.get(choices, [{}])[0].get(delta, {}) if delta.get(content): now time.perf_counter() if t_first is None: t_first now t_last now token_count 1 t_end time.perf_counter() ttft (t_first - t_start) if t_first else None e2e t_end - t_start tpot ((t_last - t_first) / (token_count - 1)) if token_count 1 else None return { ttft: ttft, tpot: tpot, e2e: e2e, tokens: token_count } def run_batch(prompt, concurrency): results [] with ThreadPoolExecutor(max_workersconcurrency) as ex: futures [ex.submit(one_request, prompt) for _ in range(concurrency)] for fut in as_completed(futures): results.append(fut.result()) return results if __name__ __main__: prompt 请用大约200字解释什么是大语言模型的推理时延。 for conc in [1, 2, 4, 8]: res run_batch(prompt, conc) ttfts [r[ttft] for r in res if r[ttft]] tpots [r[tpot] for r in res if r[tpot]] e2es [r[e2e] for r in res] total_tokens sum(r[tokens] for r in res) wall max(e2es) print(f并发{conc} | TTFT均值{sum(ttfts)/len(ttfts)*1000:.0f}ms | fTPOT均值{sum(tpots)/len(tpots)*1000:.1f}ms | fE2E均值{sum(e2es)/len(e2es)*1000:.0f}ms | f吞吐{total_tokens/wall:.1f} tok/s)这个脚本的关键点在于t_start在发请求前记录t_first在收到第一个带 content 的 delta 时记录t_last在最后一个 content 时记录t_end在流结束后记录。TTFT 就是t_first - t_startTPOT 是首 token 之后到最后一个 token 之间的时间除以 token 数减一E2E 是t_end - t_start。吞吐量用总 token 数除以整批请求的墙钟时间。运行前先装依赖pip install requests。然后设置环境变量export TAOTOKEN_API_KEY你的key再执行python bench_llm.py。脚本会依次用并发 1、2、4、8 跑四组打印每组的指标均值。这样你就能直观看到并发上升时TTFT 和 TPOT 怎么变吞吐量又怎么变。如果你用的是 Claude Code 这类编码工具做长期压测可以在 Coding Plan 页面https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 了解适合持续调用的方案。对于需要反复跑压测、对比不同模型参数的场景统一通道能省掉大量切换成本。4. 验证请求观察指标随并发变化的真实趋势配置好脚本后你需要实际跑一遍确认每个指标都能被正确采集并且观察它们随并发变化的趋势。这一节我带你走一遍验证流程包括单请求验证和多并发对比。先做单请求验证。把脚本里的并发列表改成[1]跑一次看输出是否合理。正常情况下TTFT 应该在几百毫秒量级TPOT 在几十毫秒量级E2E 在几秒量级吞吐量在几十 tok/s 量级。具体数字取决于你选的模型和当时的服务负载。如果 TTFT 显示为None说明整个流式响应里没有解析到 content可能是模型返回格式不同或者请求被拒绝了需要先检查响应体。单请求验证通过后跑完整的并发梯度。你会看到类似这样的趋势并发从 1 升到 8 时TTFT 均值从 400ms 升到 900msTPOT 从 30ms 升到 55msE2E 从 3s 升到 6s但吞吐量从 40 tok/s 升到 120 tok/s。这就是典型的“时延换吞吐”曲线。如果你看到吞吐量不升反降说明并发已经超过了服务端的处理能力请求开始排队甚至超时。这里有一个测量细节要注意TPOT 的计算依赖 token 计数。如果你的 prompt 让模型输出很短比如只生成 5 个 token那 TPOT 的样本太少波动会很大。建议压测时把max_tokens设到 128 以上并且用固定 prompt保证每次生成的 token 数量接近这样指标才有可比性。另外流式和非流式的测量方式不同。非流式请求你只能拿到总时间没法拆出 TTFT 和 TPOT。所以做指标采集一定要用stream: true。如果你在脚本里忘了开流式会发现t_first和t_last几乎同时出现TTFT 约等于 E2ETPOT 接近 0这显然是错的。验证成功后你可以把结果整理成表格方便对比不同模型或不同参数。比如固定并发为 4分别测max_tokens128和max_tokens512看 TPOT 和 E2E 怎么变。通常输出越长E2E 线性增长TPOT 相对稳定但如果服务端有 KV Cache 优化TPOT 可能随长度增加而略微上升。如果你在验证过程中想快速确认某个模型当前是否可用可以直接在模型对话页面https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 手动发一条流式消息观察首字出现的时间和后续吐字速度作为脚本测量的交叉验证。5. 常见报错排查401、local proxy failed、reading choices 与 OAuth压测过程中最容易遇到的几个报错我按出现频率排一下并给出排查路径。401 Unauthorized这是最常见的问题基本是 Key 不对或没带上。检查三件事环境变量TAOTOKEN_API_KEY是否真的设置成功用echo $TAOTOKEN_API_KEY确认请求头里Authorization是否是Bearer加 Key注意 Bearer 后面有一个空格Key 是否已经过期或被删除。如果你在脚本里同时用了配置文件的 Key 和环境变量确认脚本读的是哪一个。另外Key 复制时容易带上首尾空格建议在脚本里做一次strip()。local proxy failed / connection refused这个报错通常出现在你本地设置了网络代理但代理没有正常工作时。压测脚本发请求时如果走了系统代理而代理不可用就会报这个。排查方式是检查环境变量HTTP_PROXY和HTTPS_PROXY如果不需要代理就清掉如果确实需要确认代理地址和端口正确。在 Python 里你也可以显式传proxies{http: None, https: None}来绕过系统代理。reading choices 相关报错这类报错通常出现在解析响应时choices字段为空或者结构不符合预期。常见原因是请求被服务端拒绝返回的是一个错误对象而不是正常的 completion 结构。你可以在脚本里先打印原始响应体确认返回内容。如果返回的是{error: {...}}那就按错误信息排查而不是继续解析 choices。另一个原因是流式响应里混入了非 data 行比如空行或注释行脚本里已经用startswith(data:)过滤了但如果你自己改了解析逻辑要注意保留这个过滤。OAuth / 认证方式不匹配如果你用的是某些编码工具比如 Claude Code 或 Codex 风格的客户端它们可能默认走 OAuth 而不是 API Key。这时候你需要确认客户端的认证配置。以 Claude Code 为例它需要设置 Base URL、API Key 和 Model ID 三件套。Base URL 填https://taotoken.net/apiAPI Key 填你创建的那个Model ID 填对应模型标识。如果只填了 Key 没填 Base URL客户端可能仍然往默认地址发请求导致认证失败。Codex 的auth.json也是类似需要确保里面的 base_url 和 api_key 都指向 TaoToken 的通道。排查时有一个通用方法先用curl发一个最小请求确认通道本身是通的。命令如下curl -s -X POST https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d {model:your-model-id,messages:[{role:user,content:hi}],max_tokens:8,stream:true}如果 curl 能正常返回流式数据说明 Key 和通道没问题问题在脚本或客户端配置。如果 curl 也报 401那就回到 Key 本身排查。这个方法能帮你快速定位问题是在通道层还是在应用层。6. 把指标用起来选型与调优的下一步跑完压测、拿到四组并发下的指标后你手里就有了一张决策表。如果你的应用是实时对话优先看 TTFT尽量控制在 500ms 以内同时关注 TPOT 是否稳定避免吐字忽快忽慢。如果是批量文档摘要优先看吞吐量TTFT 可以放宽到几秒因为用户不会盯着屏幕等。如果是 Agent 类应用需要多轮调用E2E Latency 的累积效应更关键每一轮都要尽量压低。调优时记住那个制约关系提高并发或 batch size 能提升吞吐但会牺牲 TTFT 和 TPOT。你可以通过压测脚本快速验证不同参数组合找到你业务能接受的最优平衡点。比如把并发从 4 降到 2TTFT 可能从 900ms 回到 500ms而吞吐只从 120 tok/s 降到 80 tok/s这对实时场景就是划算的。如果你需要长期跑这类压测或者把指标采集集成到 CI 流程里可以了解 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 它更适合持续性的编码和 Agent 调用场景。接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 里有完整的接口说明方便你把采集逻辑固化下来。API Keys 管理页面https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 可以给不同压测任务分配不同的 Key方便按用途追踪用量。最后提醒一个实操细节压测时尽量固定 prompt 和 max_tokens每次只改一个变量否则指标波动你分不清是参数影响还是输入差异。把每次压测的配置和结果记下来几轮之后你就能摸清所选模型在你业务场景下的性能边界。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑