模型测试——性能测试:用 vLLM 与 SGLang 搭建可复现的压测基线
1. 推理服务上线前为什么必须做一次可复现的压测基线模型测试里的性能测试说白了就是回答三个问题这套 vLLM 或 SGLang 服务在目标并发下能扛多少吞吐、首 token 要等多久、长尾延迟会不会失控。很多人上线前只跑一条 curl 看能不能出字结果流量一上来 P99 直接飙到十几秒用户端表现为“转圈半天蹦一个字”。问题不在模型本身而在于没有一套可复现的压测基线参数改来改去两次测试口径都不一样结论自然没法对比。我这次要做的是把 vLLM 和 SGLang 放在同一套压测方法下对照固定数据集、固定输入输出长度分布、固定随机种子只改并发梯度量化吞吐request throughput / output token throughput、TTFT首 token 延迟、TPOT每 token 时间和 P99。这样你调--max-running-requests或--max-prefill-tokens时才知道改动到底带来了多少收益而不是凭感觉。适合谁看正在做推理服务上线的算法/后端工程师手里有 1 到 8 卡准备用 vLLM 或 SGLang 部署需要一份能直接抄的压测脚本和排障清单。整篇围绕模型测试、性能测试、vllm、sglang、压测这几个关键词展开所有命令都可复制。先明确一个原则压测客户端和服务端要分开。服务端跑模型客户端只跑vllm bench serve两边通过 HTTP 通信。这样客户端自身的 CPU 消耗不会污染服务端的延迟数据。下面从环境准备讲到结果校验中间会说明怎么用 TaoToken 统一 Key/API 通道接入被测服务保证多轮测试口径一致。2. 前置准备vLLM 与 SGLang 服务端环境搭建与 TaoToken 通道接入2.1 服务端与客户端分工准备两台机器或两个容器/Pod。服务端部署模型服务客户端安装 vllm 用于执行压测命令。客户端不需要 GPU但建议 CPU 核数多一点否则高并发下客户端自己会成为瓶颈。先在客户端上 curl 一下服务端接口确认网络通curl -s http://127.0.0.1:8000/v1/models | head能返回模型列表说明链路没问题。如果连不上先排查端口、防火墙、容器网络别急着跑压测。2.2 下载模型用 modelscope 拉模型到本地目录服务端和客户端都建议放同一路径方便 tokenizer 对齐modelscope download --model Qwen/Qwen3.5-35B-A3B --local_dir /root/Qwen3.5-35B-A3B2.3 SGLang 启动服务服务端用 SGLang 起服务--tp-size 2表示两张卡张量并行--mem-fraction-static 0.9控制静态显存占比--context-length 32768限制上下文长度nohup python -m sglang.launch_server \ --model-path /root/Qwen3.5-35B-A3B \ --port 8000 \ --tp-size 2 \ --mem-fraction-static 0.9 \ --context-length 32768 \ sglang.log 21 tail -f sglang.log日志里出现The server is fired up and ready to roll之类的字样就说明服务起来了。vLLM 侧对应命令是python -m vllm.entrypoints.openai.api_server参数含义类似--tensor-parallel-size对应--tp-size。2.4 用 TaoToken 统一 Key/API 通道多轮测试最怕口径不一致这轮用本地直连下轮换了地址和 Key延迟数据就没法比。我的做法是把被测服务的访问入口统一走 TaoToken 的 API 通道Base URL 固定为https://taotoken.net/apiKey 用同一个模型 ID 也固定。这样无论后端是 vLLM 还是 SGLang压测脚本里的--base-url和鉴权头都不用改只换服务端部署参数结论才可复现。具体操作在 TaoToken 控制台创建一个 Key然后在压测命令里通过环境变量注入避免把 Key 写死在脚本里。模型 ID 填服务端实际加载的模型名比如Qwen3.5-35B-A3B。如果你要对照多个模型建议每个模型单独建一个 Key 或打标签方便归因。需要看模型对话效果时可以直接在模型对话页面验证长期跑编码类 Agent 压测可以了解 Coding Plan 的额度策略接入细节查接入文档Key 管理在 API Keys 页面。注意压测时不要把 Key 提交到 Git用.env或环境变量注入脚本里只读$TAOTOKEN_API_KEY。3. 可复制配置并发梯度与请求长度分布怎么设计3.1 压测命令模板客户端执行vllm bench serve下面这条是单并发基线先跑通再上梯度export TAOTOKEN_API_KEY你的Key nohup vllm bench serve \ --backend openai-chat \ --model /root/Qwen3.5-35B-A3B \ --tokenizer /root/Qwen3.5-35B-A3B \ --dataset-name sonnet \ --percentile-metrics ttft,tpot,itl,e2el \ --max-concurrency 1 \ --num-prompts 10 \ --endpoint /v1/chat/completions \ --base-url https://taotoken.net/api \ --seed 1000 \ --ignore-eos \ --sonnet-input-len 256 \ --sonnet-output-len 100 \ --dataset-path /workspace/datasets/sonnet.txt \ bench.log 21 tail -f bench.log参数逐个说清楚--backend openai-chat指定走 OpenAI 兼容的 chat 接口--model和--tokenizer通常填同一路径--dataset-name sonnet用 sonnet 数据集也可以用random--percentile-metrics指定要统计百分位的指标--num-prompts是总请求数调试阶段给小值--seed 1000保证每次生成的提示序列一致这是可复现的关键--ignore-eos让模型忽略结束符强制生成到指定长度避免不同请求提前结束导致统计口径漂移--sonnet-input-len 256和--sonnet-output-len 100固定输入输出长度。如果换random数据集参数名变成--random-input-len。3.2 并发梯度设计单点数据没有意义要跑梯度。建议并发取1, 4, 8, 16, 32, 64每个并发点跑固定--num-prompts比如 200观察吞吐随并发上升、TTFT 和 P99 何时拐头。用速率模式时--request-rate 3.5表示每秒 3.5 个请求适合模拟稳定到达率并发模式适合压极限。两种模式不要混用否则曲线没法对比。3.3 用 JSON 固化测试配置为了让每轮测试口径一致把配置写成 JSON脚本读取后拼命令。下面这份可以直接存成bench_config.json{ base_url: https://taotoken.net/api, endpoint: /v1/chat/completions, model: Qwen3.5-35B-A3B, tokenizer: /root/Qwen3.5-35B-A3B, dataset_name: sonnet, dataset_path: /workspace/datasets/sonnet.txt, seed: 1000, ignore_eos: true, sonnet_input_len: 256, sonnet_output_len: 100, num_prompts: 200, concurrency_gradient: [1, 4, 8, 16, 32, 64], percentile_metrics: [ttft, tpot, itl, e2el] }如果你用 TOML 管理多套环境可以这样写[bench] base_url https://taotoken.net/api endpoint /v1/chat/completions model Qwen3.5-35B-A3B seed 1000 ignore_eos true [bench.dataset] name sonnet path /workspace/datasets/sonnet.txt input_len 256 output_len 100 [bench.load] num_prompts 200 concurrency [1, 4, 8, 16, 32, 64]3.4 服务端调参对照SGLang 侧重点调这几个--cuda-graph-bs 1 24 48控制 CUDA Graph 的 batch size 档位--max-running-requests 48对应 decode 阶段并发请求数服务日志里请求数接近这个值说明打满了此时 TPOT 高就调低TPOT 低就调高--max-prefill-tokens 8196对应 prefill 阶段的新序列 token 数输入 1024 时 8196 约等于 8 条 prefill 请求接近即打满TTFT 高就调大。注意 prefill 和 decode 是插队机制prefill 请求太多会插队 decode导致 TPOT 上升、整体吞吐下降非 PD 分离部署时尤其明显。4. 验证请求与成功结果指标怎么看、结果怎么校验4.1 先做一次冒烟验证正式跑梯度前用--num-prompts 10 --max-concurrency 1跑一次确认能出结果。成功时bench.log里会打印一张指标表包含 TTFT、TPOT、ITL、E2EL 的均值与 P99以及 request throughput 和 output token throughput。如果日志里出现reading choices相关报错多半是响应体解析失败先检查--endpoint是否和 TaoToken 通道的路径一致。4.2 指标含义与判读TTFT 是从发请求到收到第一个 token 的时间直接决定用户“等多久才看到字”TPOT 是生成每个 token 的平均时间决定“出字快不快”ITL 是相邻 token 间隔抖动大说明调度不稳E2EL 是端到端完整延迟。最关键的吞吐指标是 request throughput 和 output token throughput前者是每秒完成请求数后者是每秒输出 token 数。压测报告里一定要同时给 P99只看均值会被长尾掩盖。4.3 结果校验步骤第一确认--seed和数据集路径每轮一致否则提示序列不同数据不可比。第二确认--ignore-eos开启否则输出长度参差吞吐统计失真。第三对比两轮同配置结果波动应在 5% 以内超过说明环境有干扰比如别的进程占卡。第四把每轮结果落盘成 CSV字段包含并发、吞吐、TTFT P99、TPOT P99方便画曲线。第五服务端日志里核对max-running-requests和max-prefill-tokens是否打满把打满状态和延迟指标对应起来看。4.4 一个可复现的对照结论示例在固定输入 256、输出 100、seed 1000 的条件下并发从 1 升到 32吞吐通常持续上升TTFT P99 缓慢增长到 64 时若max-running-requests打满TPOT P99 会明显抬升吞吐反而下降。这个拐点就是你的服务容量上限。把 vLLM 和 SGLang 在相同梯度下各跑一遍就能看出哪套调度在你的硬件上更稳。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth压测跑不起来九成是下面几类错。逐个对照。401 UnauthorizedKey 没注入或写错。检查环境变量TAOTOKEN_API_KEY是否导出请求头里Authorization: Bearer $TAOTOKEN_API_KEY是否带上。用 TaoToken 通道时Base URL 必须是https://taotoken.net/api不要多加路径。如果脚本里同时写了本地直连和通道地址确认实际生效的是哪一个。local proxy failed / connection refused客户端连不到服务端。先 curlhttps://taotoken.net/api/v1/models确认通道可达再 curl 服务端本地端口确认服务活着。容器场景下注意127.0.0.1在客户端容器里指向自己要换成服务端容器名或宿主 IP。reading choices 报错响应体里没有choices字段通常是--endpoint写错或者后端返回的是非 chat 格式。确认--backend openai-chat和--endpoint /v1/chat/completions配套。如果服务端只开了 completions 接口要相应调整。OAuth / 鉴权失败多见于用了需要额外鉴权的网关。TaoToken 通道用标准 Bearer Key 即可不需要 OAuth 流程。如果报 OAuth 相关错误检查是不是误配了别的鉴权中间件。Codex auth.json / CC Switch / Cline MCP 场景如果你在压测之外还要接编码工具配置三件套要写全——Base URL 填https://taotoken.net/apiKey 填控制台生成的 KeyModel ID 填服务端实际模型名。三者缺一工具侧就会报鉴权或模型不存在。Cline 的 MCP 配置里同样遵循这三件套不要只填 Base URL 漏掉 Model ID。吞吐上不去但延迟也不高多半是客户端 CPU 打满或--num-prompts太小。加大请求数观察客户端top必要时把压测客户端单独放一台机器。两轮结果差异大检查 seed、数据集、ignore-eos 是否一致以及服务端是否有其他负载。可复现的前提是所有变量受控。6. 把压测基线固化下来接入与验证走统一通道压测做完不是终点把配置和结论固化才算数。我的习惯是每个模型版本对应一份bench_config.json加一份结果 CSV提交到仓库下次回归直接跑。服务端调参时一次只改一个变量改完重跑同一梯度对比 P99 和吞吐变化避免多变量混在一起说不清。接入侧统一走 TaoTokenBase URL 固定https://taotoken.net/apiKey 在 API Keys 页面管理接入细节查接入文档验证模型效果用模型对话长期跑编码类 Agent 压测看 Coding Plan。这样无论后端换 vLLM 还是 SGLang压测脚本里的通道配置不动多轮测试口径一致结论才站得住。最后一步把冒烟命令再跑一遍确认环境没漂移然后开始你的并发梯度。