资讯动态

【性能基准】LLM 接口压测指南:用 TaoToken 统一 Key 实测 TTFT、吞吐量与并发瓶颈

发布时间:2026/9/29 20:44:12 来源:尧图企业网站定制
1. 为什么你的压测数据总在骗你如果你正在做 LLM 接口的性能基准大概率遇到过这种场景本地跑一个流式请求首字延迟TTFT看着挺漂亮一百多毫秒一旦把并发拉到 64 或 128TTFT 直接飙到两三秒吞吐量曲线却几乎躺平。你以为是推理引擎不行了换框架、调 batch、加显存折腾一圈发现瓶颈根本不在服务端。问题往往出在压测链路本身。LLM 接口压测和传统 HTTP 压测最大的区别在于它是流式生成一个请求会持续占用连接、持续吐 token客户端如果只有一个事件循环在收流高并发下光解析 SSE 就能把自己堵死。这时候你测到的 TTFT其实是「客户端排队时间 服务端首字时间」数据自然失真。这篇内容聚焦一件事用统一的 Key 和 API 通道对 LLM 接口做可复现的压测。我会从 settings.json 和 config.toml 两个配置骨架起步覆盖 TTFT、tokens/s 吞吐量以及并发梯度下怎么定位瓶颈。适合已经在调推理服务、或者准备给线上接口做容量评估的开发者。整套流程你可以直接复制到自己的环境里跑跑完能拿到一张「并发档位 vs 延迟/吞吐」的对照表。统一 Key 的价值在于压测最怕变量太多。如果每个并发档位换一个 Key、换一个 endpoint你根本分不清性能波动是模型服务的问题还是接入层的问题。用同一个通道、同一套鉴权把变量收敛到「并发数」这一个维度上曲线才有可比性。2. 压测前的统一通道准备2.1 为什么压测要用统一 Key做并发梯度测试时最忌讳的就是「每个档位换一套凭证」。原因很直接不同 Key 可能命中不同的限流策略、不同的路由节点甚至不同的后端实例。你测出来的 TTFT 差异可能只是路由抖动而不是并发本身导致的。统一 Key 的另一个好处是结果可复现。今天用这个 Key 跑出 64 并发下 P95 TTFT 是 800ms下周复测还是同一个 Key、同一个 endpoint数据才有对比意义。否则你连「优化有没有生效」都判断不了。TaoToken 在这里的角色是一个统一的 API 通道你拿一个 Key就能通过 OpenAI 兼容协议访问多个模型压测脚本不用为每个模型改鉴权逻辑。对做基准测试来说这能省掉大量「适配不同厂商 SDK」的杂活。2.2 拿 Key 与确认接入信息先到控制台创建 API Key地址是 https://taotoken.net/api-keys 。创建后复制保存页面只显示一次。接入的 base URL 用 https://taotoken.net/api 它兼容 OpenAI 的/v1/chat/completions和/v1/models接口。也就是说你现有的 OpenAI 客户端代码只要把 base_url 和 api_key 换掉就能直接用压测工具同理。模型列表可以先拉一次确认可用型号curl https://taotoken.net/api/v1/models \ -H Authorization: Bearer $TAOTOKEN_API_KEY返回的 JSON 里data[].id就是你可以填进压测脚本的模型名。建议压测时固定一个模型不要在一次梯度测试里混用否则吞吐量数据没有可比性。注意压测会产生真实调用量建议先用小并发跑通流程确认计费和限流符合预期后再上梯度。3. 可复制的压测配置骨架3.1 settings.json客户端连接配置很多压测工具以及一些 IDE 插件、Agent 框架用 settings.json 管理连接信息。下面是一个最小骨架把统一 Key 和 base URL 集中在一处脚本只读这个文件{ llm: { base_url: https://taotoken.net/api, api_key_env: TAOTOKEN_API_KEY, model: your-model-id, timeout_seconds: 120, stream: true }, bench: { concurrency_levels: [1, 4, 8, 16, 32, 64], requests_per_level: 40, warmup_requests: 5, input_tokens: 256, output_tokens: 512, percentiles: [50, 95, 99] } }几个参数值得说明。concurrency_levels是并发梯度从 1 开始逐档翻倍这样能清楚看到饱和点出现在哪一档。requests_per_level是每档的请求总数太小会被冷启动噪声干扰40 是个折中值。warmup_requests用来预热连接池和 KV cache正式统计前先打几发。input_tokens和output_tokens要贴近你的真实业务。对话场景输入输出接近RAG 场景输入远大于输出代码生成则相反。压测参数和真实负载不匹配测出来的饱和点没有参考价值。3.2 config.toml压测任务参数如果你用的是支持 TOML 的压测框架可以把任务参数单独拆出来和连接配置解耦[target] base_url https://taotoken.net/api endpoint /v1/chat/completions model your-model-id [load] mode concurrency_gradient levels [1, 4, 8, 16, 32, 64] duration_per_level 30 max_tokens 512 [metrics] record_ttft true record_tpot true record_throughput true output_json bench_result.json [client] stream true timeout 120 retry 0retry 0是故意的。压测时不要自动重试否则失败请求会被悄悄补上你看到的成功率是假的。失败就让它失败记录错误码后面排查才有依据。record_ttft和record_tpot分别对应首字延迟和每 token 耗时。TPOT 反映生成流畅度TTFT 反映用户感知的响应速度两个都要留。3.3 并发阶梯脚本下面是一个 Python 压测脚本的核心逻辑用 asyncio httpx 实现流式请求逐档递增并发。关键点是每个请求独立记录 TTFT而不是等整个响应结束才算时间import asyncio, time, json, os import httpx BASE_URL https://taotoken.net/api/v1/chat/completions API_KEY os.environ[TAOTOKEN_API_KEY] MODEL your-model-id LEVELS [1, 4, 8, 16, 32, 64] REQ_PER_LEVEL 40 async def one_request(client, sem, results): async with sem: payload { model: MODEL, messages: [{role: user, content: 用一句话解释什么是KV cache。}], max_tokens: 512, stream: True, } headers {Authorization: fBearer {API_KEY}} start time.perf_counter() ttft None token_count 0 try: async with client.stream(POST, BASE_URL, jsonpayload, headersheaders) as resp: async for line in resp.aiter_lines(): if not line.startswith(data:): continue data line[5:].strip() if data [DONE]: break if ttft is None: ttft time.perf_counter() - start token_count 1 total time.perf_counter() - start results.append({ttft: ttft, total: total, tokens: token_count, ok: True}) except Exception as e: results.append({ttft: None, total: None, tokens: 0, ok: False, err: str(e)}) async def run_level(level): sem asyncio.Semaphore(level) results [] async with httpx.AsyncClient(timeout120) as client: tasks [one_request(client, sem, results) for _ in range(REQ_PER_LEVEL)] await asyncio.gather(*tasks) return results async def main(): all_results {} for lv in LEVELS: res await run_level(lv) ok [r for r in res if r[ok]] ttfts sorted(r[ttft] for r in ok) total_tokens sum(r[tokens] for r in ok) wall max(r[total] for r in ok) if ok else 1 all_results[lv] { success_rate: len(ok) / len(res), ttft_p50: ttfts[len(ttfts)//2] if ttfts else None, ttft_p95: ttfts[int(len(ttfts)*0.95)] if ttfts else None, throughput_tps: total_tokens / wall, } print(lv, all_results[lv]) with open(bench_result.json, w) as f: json.dump(all_results, f, indent2) asyncio.run(main())这段脚本里sem asyncio.Semaphore(level)控制同时在飞的请求数ttft在收到第一个data:块时就记录throughput_tps用总 token 数除以该档位的墙钟时间。跑完会输出每一档的成功率、TTFT 的 P50/P95 和吞吐量。注意单进程 asyncio 在高并发下自身也会成为瓶颈。如果 64 并发时你发现客户端 CPU 打满说明测的是客户端而不是服务端这时候要拆成多进程每个进程负责一部分并发。4. 验证请求与结果校验4.1 先跑单请求确认链路通正式压测前先用一个请求确认鉴权、模型名、流式解析都正常curl -N 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:说三个字}], stream: true, max_tokens: 16 }-N关闭 curl 缓冲你能看到 token 一个个吐出来。如果这里就卡住或者报 401先解决鉴权问题再谈压测。4.2 结果校验三条曲线一起看跑完梯度脚本后把bench_result.json里的数据画成三张图并发 vs TTFT、并发 vs 吞吐量、并发 vs 成功率。判断标准是这样的现象含义下一步吞吐随并发上升TTFT 缓慢上升未饱和健康区间继续加并发吞吐持平TTFT 陡增到达饱和点记录该并发为容量上限成功率下降出现超时过载回退到上一档TTFT 高但吞吐也高可能是客户端瓶颈拆多进程复测饱和点的定义是并发继续增加吞吐量不再有意义增长的那个水平。找到它你就知道这套接口在当前配置下能扛多少。4.3 用模型对话做交叉验证压测脚本测的是接口层如果你想确认某个模型在真实交互下的表现可以直接在模型对话页面手动发几轮观察流式输出的首字速度和连贯度。手动体验和脚本数据对得上说明你的压测链路没有引入额外偏差。5. 本篇常见错排查5.1 TTFT 异常高但服务端日志正常最常见的原因是客户端单进程事件循环饱和。表现是并发越高 TTFT 越大但服务端记录的 prefill 时间很短。解决办法是把压测脚本拆成多进程每个进程处理一部分并发或者用多台机器分布式压。另一个原因是流式解析写得太重。如果你在aiter_lines里做了复杂的 JSON 解析或日志落盘每收一个 token 都同步写文件客户端自己就慢了。压测时把非必要的处理去掉只记录时间戳和 token 计数。5.2 吞吐量上不去卡在某个值先看是不是max_tokens设得太小。如果每个请求只生成 16 个 token连接建立和首字的开销占比过高吞吐量自然上不去。把输出长度调到贴近真实业务比如 256 或 512。再看并发档位是不是跳得太快。从 1 直接跳到 64中间没有 4、8、16你根本看不到饱和点在哪。梯度要密才能定位拐点。5.3 成功率不是 100%先区分是超时还是报错。超时通常是并发超过服务端承载回退一档即可。如果是 4xx 错误检查 Key 是否有效、模型名是否正确、请求体是否符合 OpenAI 格式。如果是 5xx可能是上游波动隔几分钟复测一次看是否稳定复现。压测时retry一定要设为 0否则失败被重试掩盖你看到的成功率是虚高的。5.4 两次压测结果对不上检查三次模型是否同一个、输入输出长度是否一致、预热是否充分。KV cache 和连接池在冷启动阶段表现差异很大前几个请求的 TTFT 会明显偏高。正式统计前至少预热 5 个请求或者直接丢弃前 10% 的数据。6. 把压测变成常规动作压测不是跑一次就完事。模型版本更新、推理框架升级、并发策略调整都会改变饱和点。建议把上面这套脚本固化成 CI 里的一个任务每次发版前跑一遍梯度把bench_result.json存档对比历史曲线。如果你要长期做编码类或 Agent 类的高频调用压测单次按量计费的成本会累积得很快可以看看 Coding Plan 这类包月方案把压测和日常开发共用同一个通道成本更可控。接入细节和鉴权方式在接入文档里有完整说明配置骨架可以直接复用本文的 settings.json 和 config.toml。最后留一个实操建议压测报告里不要只写「P95 TTFT 800ms」要带上并发档位、输入输出长度、模型名和客户端架构。缺了任何一个这个数字都没法复现也就没有参考价值。

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

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

免费获取报价 →
↑