☰
模型测试——性能测试:用 vLLM 与 SGLang 搭建可复现的压测基线
2026/10/9 17:42:57 网站建设 项目流程

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-A3B'

2.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 2>&1 & 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/api,Key 用同一个,模型 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 2>&1 & 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 Unauthorized:Key 没注入或写错。检查环境变量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/api,Key 填控制台生成的 Key,Model ID 填服务端实际模型名。三者缺一,工具侧就会报鉴权或模型不存在。Cline 的 MCP 配置里同样遵循这三件套,不要只填 Base URL 漏掉 Model ID。

吞吐上不去但延迟也不高:多半是客户端 CPU 打满或--num-prompts太小。加大请求数,观察客户端top,必要时把压测客户端单独放一台机器。

两轮结果差异大:检查 seed、数据集、ignore-eos 是否一致,以及服务端是否有其他负载。可复现的前提是所有变量受控。

6. 把压测基线固化下来,接入与验证走统一通道

压测做完不是终点,把配置和结论固化才算数。我的习惯是每个模型版本对应一份bench_config.json加一份结果 CSV,提交到仓库,下次回归直接跑。服务端调参时一次只改一个变量,改完重跑同一梯度,对比 P99 和吞吐变化,避免多变量混在一起说不清。

接入侧统一走 TaoToken:Base URL 固定https://taotoken.net/api,Key 在 API Keys 页面管理,接入细节查接入文档,验证模型效果用模型对话,长期跑编码类 Agent 压测看 Coding Plan。这样无论后端换 vLLM 还是 SGLang,压测脚本里的通道配置不动,多轮测试口径一致,结论才站得住。最后一步,把冒烟命令再跑一遍确认环境没漂移,然后开始你的并发梯度。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询