vLLM 性能仪表板(Performance Dashboard):手动触发基准测试、结果解析与持续性能监控
vLLM 的性能仪表板(Performance Dashboard)用于确认代码变更在各种负载下是提升还是降低了推理性能。本文基于仓库文档 docs/benchmarking/dashboard.md 展开,覆盖手动触发基准测试的完整命令、运行时环境变量、结果可视化与 benchmark_results.json 对比方法,并结合 run-performance-benchmarks.sh 源码解析测试编排与自适应并发搜索的底层实现,帮助你既能本地复现 CI 基准流程,也能读懂仪表板数据的来源。
仪表板是什么、何时自动更新
性能仪表板的核心用途是回答一个简单但关键的问题:我的改动让 vLLM 更快还是更慢了? 它的结果在以下时机自动更新并发布到 PyTorch CI 托管的公开性能仪表板上:
- 提交带有
perf-benchmarks和ready两个标签的 commit 时触发基准运行; - PR 合并进 vLLM 主干时触发基准运行。
基准测试覆盖三类测试(在 performance-benchmarks-descriptions.md 中有完整定义):
- Serving 测试:测量请求处理与 API 性能。默认从 ShareGPT 数据集以固定随机种子随机采样 200 条 prompt,QPS 取 1、4、16 与 inf(inf 表示所有请求同时到达,其余 QPS 值按固定种子的泊松过程确定到达时间),评估吞吐、TTFT(首 token 时延,mean/median/p99)与 ITL(token 间时延,mean/median/p99);
- Latency 测试:固定输入 32 token、输出 128 token、批大小 8,评估端到端时延(mean、median、p99);
- Throughput 测试:以 ShareGPT 固定种子采样 200 条 prompt,批大小由 vLLM 动态决定以追求最大吞吐,评估吞吐指标。
基准覆盖的硬件平台包括 B200、A100、H100、Intel® Xeon® 处理器、Intel® Gaudi® 3 加速器以及 Arm® Neoverse™ CPU(见 .buildkite/performance-benchmarks/README.md),整体时长约 1 小时。GPU/HPU 上的默认模型为 llama-3.1 8B、llama-3 70B、mixtral 8x7B;CPU 平台为 llama-3.1 8B。
手动触发基准测试
使用 CI 镜像准备环境
手动运行基准测试时应使用与 CI 相同的 vllm-ci-test-repo 系列 Docker 镜像,并指定完整的 commit hash 作为镜像 tag,保证环境与被测代码一致。镜像后缀与运行环境对应:
- x86 CPU 环境:使用带
-cpu后缀的镜像; - AArch64 CPU 环境:使用带
-arm64-cpu后缀的镜像。
以下是 CPU 环境的 docker run 示例(GPU 环境下省略 ON_CPU 环境变量即可):
export VLLM_COMMIT=7f42dc20bb2800d09faa72b26f25d54e26f1b694 # use full commit hash from the main branch
export HF_TOKEN=<valid Hugging Face token>
if [[ "$(uname -m)" == aarch64 || "$(uname -m)" == arm64 ]]; then
IMG_SUFFIX="arm64-cpu"
else
IMG_SUFFIX="cpu"
fi
docker run -it --entrypoint /bin/bash -v /data/huggingface:/root/.cache/huggingface -e HF_TOKEN=$HF_TOKEN -e ON_CPU=1 --shm-size=16g --name vllm-cpu-ci public.ecr.aws/q9t5s3a7/vllm-ci-test-repo:${VLLM_COMMIT}-${IMG_SUFFIX}
要点说明:
HF_TOKEN必须是合法的 Hugging Face token(脚本会校验其以hf_开头),用于下载基准测试所需模型;--shm-size=16g为大并发基准提供充足的共享内存;- 将宿主目录
/data/huggingface挂载为/root/.cache/huggingface可复用模型缓存。
进入容器后,在 vLLM 仓库根目录下执行:
bash .buildkite/performance-benchmarks/scripts/run-performance-benchmarks.sh
运行完成后,结果保存在 benchmark/results 文件夹(实际为 benchmarks/results/,脚本内部会 cd benchmarks),并额外生成 benchmark_results.md 与 benchmark_results.json 两个汇总文件。
脚本做了什么:从源码看测试编排
run-performance-benchmarks.sh 的主流程(main() 函数,约 L836-L894)揭示了实际执行链路:
- 环境检查:CPU 模式(
ON_CPU=1)调用check_cpus检查 NUMA 节点数并把平台记为cpu或arm64-cpu;GPU 模式调用check_gpus,依次探测nvidia-smi、amd-smi、hl-smi(Gaudi)来确定 GPU 型号与数量,Gaudi 环境会自动追加-hpu的测试文件后缀。脚本随后根据所选测试 JSON 文件名拼接架构后缀,例如默认的serving-tests.json、CPU 的serving-tests-cpu.json、Gaudi 的serving-tests-hpu.json、Arm 的serving-tests-arm64-cpu.json,这些文件位于 tests/ 目录。 - 依赖与数据准备:安装
wget、curl、jq、lsof;ensure_sharegpt_downloaded(L83-L90)在缺少ShareGPT_V3_unfiltered_cleaned_split.json时自动从 Hugging Face 下载;并通过vllm collect-env把环境信息写入results/vllm_env.txt。 - 执行测试:serving 测试通过
vllm serve(模型为位置参数)启动服务、轮询http://localhost:8000/v1/models等待就绪后,用vllm bench serve按qps_list×max_concurrency_list的笛卡尔积逐点压测(约 L771-L820);latency / throughput 测试则通过run_benchmark_tests(L491-L560)把 JSON 中parameters字段的键按下划线转短横线的方式转换为vllm bench latency/vllm bench throughput的命令行参数。资源不足(如 GPU 数少于tensor_parallel_size,或 CPU 下 NUMA 数少于tp×pp)的用例会被自动跳过而不是报错。 - 清理与上传:每个用例结束后
kill_gpu_processes杀掉 8000 端口与 vLLM 进程并等待显存占用降到 1GB 以下;upload_to_buildkite把benchmark_results.md注解到 Buildkite 页面并上传全部 artifact。 - 后处理:安装
tabulate、pandas后依次运行convert-results-json-to-markdown.py生成 markdown 表格与 JSON 汇总,再运行compare-json-results.py -f results/benchmark_results.json输出单文件内的配置对比。
注意脚本刻意没有 set -e——注释说明 mixtral 8x22B 偶发崩溃时仍希望保留其余用例的结果。
测试用例 JSON 的结构
以 serving-tests.json 为例,serving 测试文件支持两种格式:顶层数组,或带 defaults 默认参数 + tests 列表的对象格式。当前仓库默认配置如下:
{
"defaults": {
"qps_list": ["inf"],
"max_concurrency_list": [12, 16, 24, 32, 64, 128, 200],
"server_parameters": {
"model": "meta-llama/Llama-3.1-8B-Instruct",
"tensor_parallel_size": 1,
"dtype": "bfloat16"
},
"client_parameters": {
"model": "meta-llama/Llama-3.1-8B-Instruct",
"backend": "vllm",
"ignore-eos": "",
"temperature": 0,
"num_prompts": 200
}
},
"tests": [
{
"test_name": "serving_llama8B_tp1_sharegpt",
"server_parameters": { "tensor_parallel_size": 1 },
"client_parameters": {
"dataset_name": "sharegpt",
"dataset_path": "./ShareGPT_V3_unfiltered_cleaned_split.json"
}
},
{
"test_name": "serving_llama8B_tp1_random_2048_2048",
"server_parameters": { "tensor_parallel_size": 1 },
"client_parameters": {
"dataset_name": "random",
"random-input-len": 2048,
"random-output-len": 2048
}
}
]
}
编写自定义 JSON 的规则(来自 .buildkite/performance-benchmarks/README.md):
test_name是唯一标识,serving 测试必须以serving_开头,latency 测试必须以latency_开头,throughput 测试必须以throughput_开头(脚本会强制校验,L503、L639);- JSON 里的参数键用下划线
_书写,脚本转换为 CLI 参数时会把_替换为-(json2args函数,L92-L106); - 不要在 JSON 中配置
--output-json、--save-result等保存结果的参数——脚本会自行保存结果文件(serving 测试自动附加--save-result --result-dir results/ --result-filename ...,latency 测试自动附加--output-json),重复配置会产生冲突。
latency 用例示例(latency-tests.json):
[
{
"test_name": "latency_llama8B_tp1",
"parameters": {
"model": "meta-llama/Meta-Llama-3.1-8B",
"tensor_parallel_size": 1,
"load_format": "dummy",
"num_iters_warmup": 5,
"num_iters": 15
}
}
]
throughput 用例示例(throughput-tests.json)则使用 "dataset": "./ShareGPT_V3_unfiltered_cleaned_split.json"、"num_prompts": 200、"backend": "vllm" 等参数喂给 vllm bench throughput。
运行时环境变量
以下环境变量控制基准运行行为,默认值均可从 run-performance-benchmarks.sh 源码核对:
| 变量 | 说明 | 默认值 |
|---|---|---|
ON_CPU |
在 Intel® Xeon® 与 Arm® Neoverse™ 处理器上设为 1 |
0 |
SERVING_JSON |
serving 测试使用的 JSON 文件名 | 空(用默认文件) |
LATENCY_JSON |
latency 测试使用的 JSON 文件名 | 空(用默认文件) |
THROUGHPUT_JSON |
throughput 测试使用的 JSON 文件名 | 空(用默认文件) |
REMOTE_HOST |
要压测的远程 vLLM 服务 IP | 空 |
REMOTE_PORT |
要压测的远程 vLLM 服务端口 | 空 |
PROMPTS_PER_CONCURRENCY |
计算 serving 测试 num_prompts 的乘数(num_prompts = max_concurrency × value),会覆盖 JSON 中的 num_prompts |
未设置 |
ENABLE_ADAPTIVE_CONCURRENCY |
设为 1 在静态 max_concurrency 扫描后启用基于 SLA 的自适应并发搜索 |
0 |
SLA_TTFT_MS |
自适应搜索的 TTFT SLA 阈值(毫秒) | 3000 |
SLA_TPOT_MS |
自适应搜索的 TPOT SLA 阈值(毫秒) | 100 |
ADAPTIVE_MAX_PROBES |
自适应搜索的额外探针数上限 | 8 |
ADAPTIVE_MAX_CONCURRENCY |
自适应搜索允许的最大并发 | 1024 |
补充说明(均来自脚本源码):
- 远程压测:设置
REMOTE_HOST(可选REMOTE_PORT)后脚本不再本地拉起vllm serve,而是给vllm bench serve追加--host/--port(约 L741-L763),适合对已部署集群做基准。 PROMPTS_PER_CONCURRENCY:设置后会剔除 JSON 派生的固定--num-prompts,按每个并发点动态计算,并用MIN_NUM_PROMPTS(默认 1)/MAX_NUM_PROMPTS(默认 1000000)做上下限约束。- 调试开关:脚本还支持
DRY_RUN=1(只打印命令、不启动服务、跳过 HF_TOKEN 校验)、TEST_SELECTOR(正则过滤用例名)、MODEL_FILTER/DTYPE_FILTER(dry-run 下过滤模型与精度)等内部变量,适合在提交前快速核对将要执行的命令。
结果可视化:markdown 表格与 JSON 汇总
convert-results-json-to-markdown.py
基准结束后,main() 会自动执行 python3 .buildkite/performance-benchmarks/scripts/convert-results-json-to-markdown.py。该脚本把 results/ 下的原始 JSON 整理为 markdown 表格,填充 performance-benchmarks-descriptions.md 模板中的 {latency_tests_markdown_table}、{serving_tests_markdown_table} 等占位符,产出:
benchmark_results.md:包含各测试类型表格 + 平台信息表,并在文末附表格的 JSON 版本,可像这样载入 pandas:
import json
import pandas as pd
benchmarking_results_json = """The json string"""
benchmarking_results = json.loads(benchmarking_results_json)
latency_results = pd.DataFrame.from_dict(benchmarking_results["latency"])
throughput_results = pd.DataFrame.from_dict(benchmarking_results["throughput"])
serving_results = pd.DataFrame.from_dict(benchmarking_results["serving"])
benchmark_results.json:所有表格数据的纯 JSON 汇总,供后续对比使用。
在 CI 中,表格会直接注解在 buildkite/performance-benchmark 作业页面上(若暂时看不到表格,请等基准跑完);每个用例的原始 JSON 结果与服务端/客户端完整命令保存在作业的 Artifacts 标签页中,其中 .commands 文件记录了 server_command、client_command 与 gpu_type,方便精确复现任意一次压测。
compare-json-results.py:性能对比与容量规划
compare-json-results.py 用于对比经 convert-results-json-to-markdown.py 转换出的 benchmark_results.json 文件,给出吞吐、Median/P99 TTFT、Median/P99 TPOT 的性能比值(perf_ratio);若只传入一个文件,则改为对比该文件内部不同 TP/PP 配置之间的差异——这正是脚本默认后处理步骤 compare-json-results.py -f results/benchmark_results.json 的用途。
对比两个结果的示例(相同模型、数据集、输入/输出长度,按最大并发与 QPS 对齐):
python3 compare-json-results.py -f results_a/benchmark_results.json -f results_b/benchmark_results.json
输出形如:
***Output Tput (tok/s) — Model : [ meta-llama/Llama-3.1-8B-Instruct ] , Dataset Name : [ random ] , Input Len : [ 2048.0 ] , Output Len : [ 2048.0 ]***
| | # of max concurrency | qps | results_a/benchmark_results.json | results_b/benchmark_results.json | perf_ratio |
| | -------------------- | --- | -------------------------------- | -------------------------------- | ---------- |
| 0 | 12 | inf | 24.98 | 186.03 | 7.45 |
| 1 | 16 | inf | 25.49 | 246.92 | 9.69 |
| 2 | 24 | inf | 27.74 | 293.34 | 10.57 |
| 3 | 32 | inf | 28.61 | 306.69 | 10.72 |
命令行参数(大多数场景只需指定 --file):
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
--file |
str(可多次指定) |
None | 输入 JSON 结果文件;可多次指定以对比多份基准输出 |
--debug |
bool |
False |
调试模式,打印所有可用信息以便排查与校验 |
--plot / --no-plot |
bool |
True |
是否生成性能曲线图,--no-plot 关闭绘图 |
--xaxis |
str |
# of max concurrency. |
对比图 X 轴使用的列名(如并发或批大小) |
--latency |
str |
p99 |
TTFT/TPOT 的聚合方式,支持 median 或 p99 |
--ttft-max-ms |
float |
3000.0 |
TTFT 图的参考上界(毫秒),常用于可视化 SLA 阈值 |
--tpot-max-ms |
float |
100.0 |
TPOT 图的参考上界(毫秒),常用于可视化 SLA 阈值 |
有效最大并发汇总(Valid Max Concurrency Summary):脚本还会按配置的 TTFT/TPOT SLA 阈值,为每份基准结果计算满足约束的最大有效并发,输出如下表。Max # of max concurrency. (Both) 列表示同时满足 TTFT 与 TPOT 约束的最高并发层级,常用于容量规划与部署规模评估:
| # | Configuration | Max # of max concurrency. (TTFT ≤ 10000 ms) | Max # of max concurrency. (TPOT ≤ 100 ms) | Max # of max concurrency. (Both) | Output Tput @ Both (tok/s) | TTFT @ Both (ms) | TPOT @ Both (ms) |
|---|---|---|---|---|---|---|---|
| 0 | results-a | 128.00 | 12.00 | 12.00 | 127.76 | 3000.82 | 93.24 |
| 1 | results-b | 128.00 | 32.00 | 32.00 | 371.42 | 2261.53 | 81.74 |
从源码结构看,脚本在对比前会把各文件的并发列统一重命名为规范列名 # of max concurrency.,并按 Model、Dataset Name、Input Len、Output Len、并发、qps 等关键列做集合对齐,因此两份结果的 max_concurrency_list 可以不一致——缺失的并发点会以 NaN 呈现而不是导致对齐失败。
持续基准测试(Continuous Benchmarking)
持续基准测试为 vLLM 在不同模型与 GPU 设备上提供自动化性能监控,用于长期跟踪性能特征、及时发现回退或改进。
触发机制:由 PyTorch 基础设施仓库(pytorch-integration-testing)中的 GitHub Actions 工作流触发,每 4 小时自动运行一次,执行三类测试——Serving 测试(请求处理与 API 性能)、Throughput 测试(token 生成速率)、Latency 测试(响应时间特征)。结果最终汇入前文提到的公开性能仪表板。
基准配置与新增模型:当前基准运行在预定义的一组模型上,模型清单配置在 pytorch-integration-testing 仓库的 vllm-benchmarks/benchmarks 目录(该目录属于外部仓库,不在当前 vLLM 仓库内)。要新增基准模型,步骤为:
- 进入基准配置中对应 GPU 的目录;
- 在相应配置文件中添加你的模型规格;
- 新模型会在下一次定时基准运行时自动被纳入。
小结:从一次提交到仪表板数字
把整条链路串起来:perf-benchmarks+ready 标签的 commit(或合并主干)触发 CI → CI 按 tests/ 中的 JSON 配置依次执行 vllm bench serve/latency/throughput → 原始 JSON 落入 benchmarks/results/ 并上传 artifact → convert-results-json-to-markdown.py 生成 benchmark_results.md/json → compare-json-results.py 输出单文件内的配置对比 → 结果发布到公开性能仪表板。开发者在本地只需一条 docker run + 一条 bash .buildkite/performance-benchmarks/scripts/run-performance-benchmarks.sh 即可完整复现该链路,并用 compare-json-results.py 对改动前后的两份 benchmark_results.json 做量化对比;开启 ENABLE_ADAPTIVE_CONCURRENCY=1 还能在静态并发扫描后按 SLA 自动二分逼近满足 TTFT/TPOT 约束的最大并发,直接服务于部署容量规划。
更多基准参数与用例细节可参考 性能基准测试说明 与 基准测试套件 README。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00