1. 为什么单请求跑通不等于服务可用vLLM 部署完之后很多人第一件事是 curl 一个请求看到返回正常就认为大功告成。但真正上线之后才发现QPS 一上来延迟就飙、吞吐卡在某个数字上不去甚至偶发超时。问题不在于 vLLM 本身不行而在于你没有在高并发下量化过它的行为边界。这篇内容聚焦一个具体场景用benchmark_serving.py对 vLLM 发起阶梯式并发压测同时用rocprof采集 GPU 侧 kernel 执行数据定位吞吐骤降和延迟毛刺到底出在哪个环节。适合已经在跑 vLLM 推理服务、想搞清楚性能天花板在哪的开发者。整条链路里模型请求的鉴权入口我用 TaoToken 统一 Key 来管理这样压测脚本、coding agent、日常调试都走同一套凭证不用在多个配置文件之间来回改 base_url 和 api_key。下面按“环境准备 → 压测脚本配置 → rocprof 采集 → 指标对比 → 排障”的顺序展开每一步都给可复制的命令和参数。2. TaoToken 统一 Key 的前置配置压测脚本本身不直接调 TaoToken但你的 vLLM 服务如果需要对上游模型做转发或对比测试统一 Key 能省掉很多切换成本。TaoToken 的 API 入口是https://taotoken.net/api兼容 OpenAI 风格的请求格式所以benchmark_serving.py里如果走--backend openai模式可以直接把 base_url 指过去。先拿 Key打开https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite创建一个新 Key复制出来。然后在你常用的编辑器或终端配置里写入。以 VS Code 的settings.json为例{ taotoken.apiKey: sk-你的Key, taotoken.baseUrl: https://taotoken.net/api, taotoken.defaultModel: claude-sonnet-4-20250514 }如果你用的是命令行工具链config.toml骨架如下[taotoken] api_key sk-你的Key base_url https://taotoken.net/api default_model claude-sonnet-4-20250514 timeout 120这样配置之后压测过程中如果需要对比不同模型在同一并发下的表现只需要改default_model字段不用动脚本里的请求逻辑。接入文档在https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite里面有完整的参数说明。3. benchmark_serving.py 可复制启动参数vLLM 仓库自带benchmarks/benchmark_serving.py核心思路是模拟真实流量分布按阶梯并发逐步加压。先确认你的 vLLM 服务已经启动比如python -m vllm.entrypoints.openai.api_server \ --model /path/to/your/model \ --host 0.0.0.0 \ --port 8000 \ --tensor-parallel-size 1 \ --max-num-seqs 256 \ --max-num-batched-tokens 8192 \ --gpu-memory-utilization 0.90服务起来之后另开一个终端跑压测。下面这组参数是我实测下来比较能暴露瓶颈的配置python benchmarks/benchmark_serving.py \ --backend vllm \ --base-url http://localhost:8000 \ --model /path/to/your/model \ --dataset-name sharegpt \ --dataset-path ./ShareGPT_V3_unfiltered_cleaned_split.json \ --num-prompts 2000 \ --request-rate inf \ --concurrency 1 4 8 16 32 64 128 \ --output-json result_$(date %s).json几个关键参数说明--concurrency是核心它定义同时向服务端发请求的客户端数量。从 1 开始阶梯递增能观察到系统从空闲到饱和再到过载的完整曲线。--request-rate inf表示不限制请求速率让并发数成为唯一变量。--num-prompts 2000保证每个并发档位有足够的样本量避免统计噪声。数据集建议用 ShareGPT 或你自己的业务日志确保输入输出 token 长度分布贴近真实场景。如果手头没有 ShareGPT 文件可以用--dataset-name random快速跑一轮但随机数据的长度分布均匀可能掩盖长尾问题。跑完之后会生成一个 JSON 文件里面包含每个并发档位的 RPS、TTFT、TPOT、Token/s 等指标。把这些数据拉出来画曲线重点关注两个拐点RPS 不再线性增长的点和 TTFT 开始陡升的点。4. rocprof 采集 GPU 侧指标压测只能告诉你“慢了”但慢在哪需要 rocprof 来回答。在 AMD Instinct GPU 上rocprof 可以记录每个 kernel 的执行时间、显存拷贝量和 SM 利用率。启动 vLLM 服务时挂上 rocprofrocprof --stats \ -o vllm_trace \ --timestamp on \ python -m vllm.entrypoints.openai.api_server \ --model /path/to/your/model \ --port 8000 \ --max-num-seqs 256 \ --max-num-batched-tokens 8192--stats会输出汇总统计-o指定输出文件名前缀--timestamp on让每条记录带时间戳方便和压测时间线对齐。服务启动后正常跑一轮benchmark_serving.py然后停掉服务rocprof 会生成vllm_trace.csv和vllm_trace.stats.csv。打开 stats 文件按耗时排序重点看这几个 kernelpaged_attention_kernel是 vLLM PagedAttention 的核心算子调用最频繁。如果它的执行时间随并发数增加呈非线性上升说明 KV Cache 的显存访问模式在高压下变得随机显存控制器成为瓶颈。reshape_and_cache_kernel负责把新计算的 KV 写入 cache block。如果这个 kernel 耗时占比异常高可能是 block 分配策略有问题碎片率过高导致频繁分配释放。还要关注 H2DHost to Device拷贝的耗时。如果发现大量小尺寸拷贝说明 CPU 侧准备输入数据的效率跟不上 GPU 消费速度需要检查 tokenizer 是否成为瓶颈。一个实操技巧把 rocprof 的输出和压测的 JSON 结果按时间戳对齐就能看到“并发数升到 64 时paged_attention_kernel 耗时从 2ms 跳到 15ms”这种直接关联。5. 压测前后的指标对比验证光看单轮数据不够需要做前后对比才能确认调优是否有效。建议按这个流程走第一轮用默认参数跑 baseline保存result_baseline.json和vllm_trace_baseline.stats.csv。然后调整 vLLM 启动参数比如把--max-num-batched-tokens从 8192 降到 4096或者把--block-size从 16 改成 8重启服务再跑一轮同样的压测保存为result_tuned.json。对比时重点看三个指标的变化指标baselinetuned变化并发 64 时 RPS12.315.828%并发 64 时 TTFT P993200ms1800ms-44%paged_attention_kernel 平均耗时8.7ms5.2ms-40%如果 RPS 提升但 TTFT 没降说明吞吐上去了但排队时间没改善可能需要开--enable-chunked-prefill。如果 TTFT 降了但 RPS 没变说明调度策略优化了但计算资源仍是瓶颈考虑加卡或降精度。验证请求是否走通可以用一个简单的 curl 确认服务状态curl -s http://localhost:8000/v1/models | python -m json.tool返回模型列表就说明服务正常。压测脚本跑完后检查 JSON 里completed字段是否等于num-prompts如果有大量失败请求先排查服务端日志里的 OOM 或超时错误。6. 本篇常见错排查报错一Connection refused或Max retries exceeded压测脚本连不上 vLLM 服务。先确认--base-url的端口和服务启动端口一致再检查防火墙是否放行。如果 vLLM 启动时绑的是127.0.0.1而压测脚本在另一个容器里跑需要改成0.0.0.0。报错二CUDA out of memory或HIP out of memory并发数太高导致 KV Cache 爆显存。降低--max-num-seqs或--gpu-memory-utilization也可以减小--max-num-batched-tokens。如果用的是 AMD GPU确认--block-size和--swap-space配置合理swap 太小会导致频繁换出。报错三rocprof 输出为空或只有 header通常是权限问题。rocprof 需要访问 GPU 性能计数器确认当前用户在video或render组里。另外如果 vLLM 是以 daemon 方式启动的rocprof 可能挂不上去建议用前台进程跑。报错四TTFT 正常但 TPOT 很高首字延迟低说明 prefill 阶段没问题但每个输出 token 的生成时间高通常是 decode 阶段的计算或显存带宽瓶颈。检查paged_attention_kernel的耗时如果它占了大头尝试减小--block-size提高显存访问局部性。报错五压测结果波动大同一并发两次跑差异超过 20%检查后台是否有其他进程占用 GPU比如另一个推理服务或训练任务。用rocm-smi确认 GPU 利用率基线。另外--num-prompts太小会导致统计不收敛建议至少 1000 条以上。7. 继续压榨性能的下一步跑完这一轮你手里应该有了三条曲线并发-RPS、并发-TTFT、并发-Token/s以及一份 rocprof 的 kernel 耗时排名。接下来可以做的把--enable-chunked-prefill打开观察 P99 延迟是否改善尝试--quantization fp8看吞吐能提升多少或者用 TaoToken 的模型对话入口快速对比不同模型在同一压测配置下的表现入口在https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite。如果你打算把压测流程固化到 CI 里长期跑编码 agent 做自动化回归可以看看 Coding Plan 的配置方式https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite。控制台在https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite里面可以看 Key 的调用量和余额。性能优化没有终点但每一次基于数据的调整都比盲目改参数靠谱。先把 baseline 跑出来再谈优化。