ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

大模型性能测试指标介绍:用 TaoToken 统一 Key 跑通 TTFT、TPOT、TPS 实测

大模型性能测试指标介绍:用 TaoToken 统一 Key 跑通 TTFT、TPOT、TPS 实测 1. 为什么我劝你别只看“总耗时”TTFT、TPOT、TPS 到底在测什么如果你正在做 LLM 或 MLLM 推理服务的性能评测大概率遇到过这种困惑同一个模型A 同学说“挺快的”B 同学说“卡得不行”结果一对比两人测的根本不是同一个东西。有人测的是从点击发送到答案全部出完的总时间有人测的是第一个字蹦出来的速度还有人测的是每秒能吐多少 token。这三个视角对应到工程指标上就是 TTFT、TPOT 和 TPS。先把这三个词用大白话拆开。TTFTTime To First Token是首 token 时间从你发出请求到收到第一个流式文本块之间的耗时它决定了用户“感觉系统有没有在干活”。TPOTTime Per Output Token是每输出一个 token 的平均耗时可以理解成“吐字间隔”它决定了答案回显的流畅度。TPSTokens Per Second是每秒生成的 token 数本质上是 TPOT 的倒数用来衡量吞吐能力。三者不是互相替代的关系而是从“响应启动”“持续输出”“整体吞吐”三个维度描述同一次推理。这套指标不只适用于纯文本 LLM对多模态 MLLM 同样通用。因为多模态模型在完成视觉编码之后解码阶段依然是自回归逐 token 生成TTFT 里包含了图像预处理和视觉编码的时间TPOT 和 TPS 则反映文本解码阶段的效率。所以无论你测的是纯语言模型还是图文模型这三个指标都能直接套用。我见过不少团队只盯着“总完成时间”做优化结果把首包延迟压下去了用户感知却没变好因为 TPOT 太高答案还是一个字一个字往外挤。反过来只优化 TPOT 而忽略 TTFT用户会在点击后盯着空白屏幕等好几秒同样会怀疑服务挂了。真正可复现的评测流程必须把这三个指标拆开采集、分别记录再结合业务场景判断哪个是瓶颈。这篇内容会带你从零搭一套可复现的观测流程用统一的 Key 和 API 通道发起流式请求在客户端记录首 token 到达时间、每个 token 的间隔、总 token 数和总耗时最后算出 TTFT、TPOT、TPS。中间会给出可复制的压测脚本骨架和 settings.json 示例也会把常见的坑列出来。你不需要一开始就上专业压测平台先用一段脚本把指标跑通后面再扩展并发和场景才有意义。2. 前置准备用 TaoToken 统一 Key 打通请求通道做性能评测最怕的一件事是测到一半发现请求通道本身不稳定指标忽高忽低你根本分不清是模型慢还是网络抖。所以第一步不是写脚本而是先把请求入口固定下来。我自己的做法是用 TaoToken 作为统一的 API 通道一个 Key 覆盖多家模型这样切换被测模型时不用改鉴权逻辑指标才有可比性。TaoToken 在这里扮演的角色是统一的模型调用入口。你可以在官网了解它的能力范围然后到控制台创建一个 API Key。整个流程不复杂注册登录后进入控制台在 API Keys 页面生成一个 Key复制保存好。这个 Key 后面会写进环境变量脚本里不硬编码避免泄露。拿到 Key 之后你需要确认两件事。第一是 API 基地址TaoToken 的 API 入口是https://taotoken.net/api注意这个地址不带任何查询参数直接作为 base_url 使用。第二是你要测的模型名称不同模型的 TTFT 和 TPOT 差异很大建议先固定一个模型跑通流程再横向对比。如果你更习惯在图形界面里先手动验证一次请求是否通可以打开模型对话页面选一个模型发一条消息确认能正常返回。这一步的意义是排除 Key 或权限问题避免后面脚本报错时你分不清是代码写错了还是 Key 没生效。手动验证通过后再回到脚本侧做流式采集。对于长期要做编码类或 Agent 类评测的场景可以考虑 Coding Plan它更适合持续性的调用需求。但如果你只是做一次性的性能摸底按量调用加统一 Key 就够了。接入文档里有完整的参数说明和示例遇到字段不确定时优先查文档比在脚本里反复试错快得多。这里要提醒一句性能评测的变量控制很重要。同一轮对比里尽量保持 API 通道、网络环境、并发数、输入 prompt 长度一致只改变被测模型这一个变量。否则你测出来的差异可能来自通道抖动而不是模型本身。3. 可复制配置settings.json 与压测脚本骨架先把配置抽出来。下面这个settings.json示例把 Key、base_url、模型名、测试参数都集中管理脚本读取它即可方便你换模型时只改一个文件。{ api_key_env: TAOTOKEN_API_KEY, base_url: https://taotoken.net/api, model: your-model-name, stream: true, max_tokens: 512, temperature: 0.2, prompt: 请用三百字介绍大模型推理中的首token延迟。, repeat: 5, timeout_seconds: 60 }几个字段说明一下。api_key_env指向环境变量名脚本运行时从环境变量读取真实 Key不写进文件。stream必须为true否则你拿不到逐 token 的到达时间TTFT 和 TPOT 都无从谈起。repeat是重复次数单次测量噪声大跑 5 次取中位数更稳。max_tokens控制输出长度太短会导致 TPOT 样本不足太长则单次耗时过久512 是个折中值。接下来是压测脚本骨架用 Python 写依赖requests和标准库即可。核心思路是记录请求发出时间逐行读取 SSE 流遇到第一个内容块时记下首 token 时间之后每个内容块记录到达时间流结束时统计总 token 数和总耗时。import json import os import time import statistics import requests with open(settings.json, r, encodingutf-8) as f: cfg json.load(f) API_KEY os.environ[cfg[api_key_env]] URL cfg[base_url].rstrip(/) /v1/chat/completions HEADERS { Authorization: fBearer {API_KEY}, Content-Type: application/json, } def run_once(cfg): payload { model: cfg[model], stream: True, max_tokens: cfg[max_tokens], temperature: cfg[temperature], messages: [{role: user, content: cfg[prompt]}], } start time.perf_counter() first_token_time None token_times [] token_count 0 with requests.post(URL, headersHEADERS, jsonpayload, streamTrue, timeoutcfg[timeout_seconds]) 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[len(data:):].strip() if data [DONE]: break chunk json.loads(data) delta chunk[choices][0].get(delta, {}) content delta.get(content) if content: now time.perf_counter() if first_token_time is None: first_token_time now token_times.append(now) token_count 1 end time.perf_counter() ttft (first_token_time - start) if first_token_time else None total end - start if token_count 1: intervals [token_times[i] - token_times[i-1] for i in range(1, len(token_times))] tpot statistics.mean(intervals) else: tpot None tps token_count / total if total 0 else None return {ttft: ttft, tpot: tpot, tps: tps, tokens: token_count, total: total} results [run_once(cfg) for _ in range(cfg[repeat])] for r in results: print(json.dumps(r, ensure_asciiFalse))这段脚本的关键点在于时间基准统一用time.perf_counter()它比time.time()精度更高适合测毫秒级间隔。iter_lines逐行读取 SSE遇到data:前缀才解析[DONE]作为流结束标志。TTFT 用第一个内容块到达时间减去请求发出时间TPOT 用相邻 token 到达间隔的均值TPS 用总 token 数除以总耗时。注意一个细节有些实现会把首个 chunk 里的 role 字段也算作一个事件但它没有实际内容。脚本里用delta.get(content)过滤只有真正带文本的块才计入 token这样 TTFT 才准确。如果你把空 delta 也算进去TTFT 会偏小指标失真。4. 验证请求跑出 TTFT、TPOT、TPS 并读懂结果配置和脚本就绪后先设置环境变量再运行。Linux 或 macOS 下export TAOTOKEN_API_KEY你的Key python bench.pyWindows PowerShell 下$env:TAOTOKEN_API_KEY你的Key python bench.py跑通后你会看到类似这样的输出每行是一次测量的结果{ttft: 0.62, tpot: 0.021, tps: 47.6, tokens: 312, total: 6.55} {ttft: 0.58, tpot: 0.019, tps: 52.1, tokens: 318, total: 6.10} {ttft: 0.71, tpot: 0.023, tps: 43.5, tokens: 305, total: 7.01}怎么读这些数字TTFT 在 0.6 秒左右说明首包响应很快用户几乎无感。TPOT 约 0.02 秒意味着每 20 毫秒吐一个 token回显比较流畅。TPS 在 45 到 52 之间属于中等偏上的吞吐水平。注意 TPS 和 TPOT 不是严格倒数关系因为 TPS 用的是总耗时含 TTFT而 TPOT 只统计解码阶段的间隔两者口径不同别混用。如果你想更严谨可以把多次结果做聚合取 TTFT 和 TPOT 的中位数TPS 用总 token 数除以总耗时再平均。中位数比均值抗离群值网络偶发抖动不会把结果带偏。下面这段聚合逻辑可以直接加到脚本末尾ttfts [r[ttft] for r in results if r[ttft]] tpots [r[tpot] for r in results if r[tpot]] total_tokens sum(r[tokens] for r in results) total_time sum(r[total] for r in results) print(TTFT median:, round(statistics.median(ttfts), 3)) print(TPOT median:, round(statistics.median(tpots), 4)) print(TPS overall:, round(total_tokens / total_time, 2))实测下来同一模型在稳定网络下多次测量的 TTFT 波动通常在 10% 以内如果波动超过 30%优先排查网络和通道而不是怀疑模型。另外prompt 长度会显著影响 TTFT因为预填充阶段要处理全部输入 token。做横向对比时务必用同一个 prompt否则 TTFT 差异可能来自输入长度而非模型性能。对于 MLLM你可以在 messages 里加入图像内容观察 TTFT 是否因为视觉编码而明显上升。通常图像分辨率越高TTFT 增加越明显而 TPOT 变化不大因为解码阶段和纯文本类似。这个对比能帮你判断多模态场景下延迟主要花在哪一段。5. 本篇常见错排查指标跑不通时先看这几处第一个高频问题是 TTFT 为None或异常大。常见原因是流式没开stream字段为false时服务端一次性返回完整结果你只能在最后拿到内容首 token 时间自然测不到。检查 settings.json 里stream是否为true以及请求体里是否真的带上了这个字段。第二个问题是 TPOT 算出来是负数或极小值。这通常是因为把非内容事件也计入了 token 时间比如 role 声明块、心跳块。解决办法就是脚本里那行if content:过滤只统计有实际文本的块。另外如果 token 数只有 1TPOT 无法计算脚本里已经做了token_count 1的判断。第三个问题是请求返回 401 或 403。先确认环境变量名和 settings.json 里的api_key_env一致再确认 Key 没有多余空格。如果手动在模型对话里能通、脚本不通多半是 Header 拼写问题注意是Authorization: Bearer KeyBearer 和 Key 之间有一个空格。第四个问题是 TPS 忽高忽低。除了网络因素还要看max_tokens是否太小。如果只生成几十个 token总耗时里 TTFT 占比很大TPS 会被拉低且不稳定。建议max_tokens不低于 256让解码阶段有足够样本。同时repeat次数别只跑一次单次结果参考价值有限。第五个问题是超时。timeout_seconds设得太小长输出会被中断指标不完整。如果被测模型本身较慢把它调到 120 秒。但要注意超时时间只影响客户端等待不影响服务端实际耗时别用它来“优化”指标。还有一个容易忽略的点并发。上面的脚本是串行请求测的是单请求延迟。如果你想测吞吐上限需要引入并发但并发一上来TTFT 和 TPOT 都会变化因为服务端要排队。这时候要区分“单请求延迟指标”和“系统吞吐指标”别把并发下的 TPOT 和单请求的 TPOT 直接对比。做容量规划时建议固定并发数观察 TTFT 和 TPS 随并发上升的变化曲线找到拐点。6. 把指标观测流程固定下来后续才好对比跑通一次不难难的是让每次评测都可复现。我的建议是把 settings.json、脚本和结果输出目录一起纳入版本管理每次评测记录模型名、prompt、并发数、时间戳结果存成 JSON 或 CSV。这样过一段时间回头看你能清楚知道某个版本的 TTFT 变化是模型升级带来的还是通道调整带来的。如果你后续要做更系统的评测可以在当前脚本基础上加两件事一是并发控制用线程池或异步请求模拟多用户二是分位数统计除了中位数把 P95、P99 的 TTFT 也记下来因为用户体验往往由尾部延迟决定。接入文档里有更多参数和调用方式遇到字段不确定时优先查文档。需要长期跑编码或 Agent 类评测的话Coding Plan 在持续调用场景下更省心。最后留一个实用习惯每次改完脚本或换模型先跑一轮小样本确认指标量级合理再跑正式评测。别一上来就大规模压测否则一个字段写错浪费的是整轮时间。指标本身不复杂复杂的是让采集过程稳定、口径一致。把这两点做到TTFT、TPOT、TPS 就能真正成为你优化推理服务的依据而不是一堆看不懂的数字。
返回列表