首页
/ vLLM auto_tune:在延迟预算下自动搜索最优服务参数的调优实战指南

vLLM auto_tune:在延迟预算下自动搜索最优服务参数的调优实战指南

2026-09-05 14:54:36作者:董灵辛Dennis

vLLM 仓库内置了一套自动调参脚本(位于 benchmarks/auto_tune/),用于在给定输入/输出长度、P99 端到端延迟上限和(可选的)前缀缓存命中率约束下,自动搜索 max-num-seqsmax-num-batched-tokens 的最优组合,使服务吞吐量最大化。本文基于官方文档与脚本源码,完整讲解这套自动调优工具的配置变量、运行方式、输出解读,以及其背后的"OOM 安全内存搜索 + 延迟感知吞吐搜索 + 最优配置 Profiling"三段式算法流程,读完即可在自己的 GPU/TPU 集群上直接复现。

一、auto_tune 要解决什么问题

vLLM 服务端的两个核心参数——max-num-seqs(单步最多并发序列数)和 max-num-batched-tokens(单步最多批处理 token 数)——直接决定吞吐与延迟的平衡点:

  • 两者设得过大:吞吐上限提高,但 P99 端到端延迟(E2E Latency)恶化,可能无法满足 SLA;
  • 两者设得过小:延迟轻松达标,但硬件利用率不足,吞吐偏低。

auto_tune.sh 把"人肉改参数 → 起服务 → 跑压测 → 看结果"的循环自动化:它在给定参数空间内枚举每一个组合,对每个组合先用无穷请求率(--request-rate inf)压出极限吞吐与 P99 E2E 延迟;若不满足延迟约束,则从极限吞吐点开始逐步降低请求速率,找到该配置下"满足延迟约束的最高可持续吞吐"。此外它还支持 MIN_CACHE_HIT_PCT(前缀缓存命中率下限)约束,用于模拟多轮对话、Agent 等大量前缀复用的真实场景。

仓库内共涉及三个文件:

文件 作用
benchmarks/auto_tune/README.md 使用文档(本文主要依据)
benchmarks/auto_tune/auto_tune.sh 单轮自动调优主脚本
benchmarks/auto_tune/batch_auto_tune.sh 批量调优:按 JSON 配置文件顺序执行多组实验并汇总

二、前置条件(Prerequisites)

README 的要求,运行前需要完成:

  1. 获取 vLLM 源码并切到目标分支:将 vLLM 仓库克隆到本地(当前仓库即是),进入 vllm 目录并切到要调优的分支:

    git clone <vLLM仓库地址>
    cd vllm
    # git checkout <your-branch>
    
  2. 安装运行环境:安装或更新对应的推理环境;如果使用 TPU,需要激活 conda 环境并安装匹配的 torchtorch_xla 版本。

  3. 模型配置就位:如果使用自定义模型,确保其配置文件放置正确且可访问。

  4. 隐式依赖(从 auto_tune.sh 源码可见):脚本执行 pip install -q datasets 自动安装数据集依赖;压测解析依赖 bc(浮点比较)与 curl(健康检查);批量脚本依赖 jq。脚本内部还通过 git rev-parse HEAD 记录当前提交 hash,因此需要在 git 仓库内运行。

三、配置变量(Configuration)

所有变量既可以在 auto_tune.sh 顶部直接修改,也可以在运行时用环境变量覆盖(脚本对每个变量都写了 VAR=${VAR:-默认值} 形式的默认值)。官方给出的典型调用方式是:

MODEL=meta-llama/Llama-3.3-70B-Instruct SYSTEM=TPU TP=8 DOWNLOAD_DIR='' \
INPUT_LEN=128 OUTPUT_LEN=2048 MAX_MODEL_LEN=2300 MIN_CACHE_HIT_PCT=0 \
MAX_LATENCY_ALLOWED_MS=100000000000 NUM_SEQS_LIST="128 256" \
NUM_BATCHED_TOKENS_LIST="1024 2048 4096" VLLM_LOGGING_LEVEL=DEBUG bash auto_tune.sh

完整变量说明(默认值取自脚本源码,与 README 配置表对应):

变量 说明 示例值 脚本默认值
BASE 必填。 vLLM 仓库目录的父目录绝对路径(脚本会 cd "$BASE/vllm" 并在 $BASE/auto-benchmark/ 下落日志) "$HOME" 脚本相对路径 ../../..
MODEL 必填。 vLLM 要服务的 Hugging Face 模型标识符 "meta-llama/Llama-3.1-8B-Instruct" 同左
SYSTEM 必填。 硬件平台,TPUGPU(其他硬件可能不支持保存 profile) "TPU" TPU
TP 必填。 张量并行(tensor-parallel)规模 1 1
DOWNLOAD_DIR 必填。 模型权重的下载/加载目录 ""(默认下载路径) ""
INPUT_LEN 必填。 请求输入长度(token) 4000 4000
OUTPUT_LEN 必填。 请求输出长度(token) 16 16
MAX_MODEL_LEN 必填。 模型最大上下文长度 4096 4096
MIN_CACHE_HIT_PCT 前缀缓存命中率约束(0–100,百分比),设为 0 表示不启用 60 0
MAX_LATENCY_ALLOWED_MS 允许的最大 P99 端到端延迟(毫秒);设为极大值(如 100000000000)等效于忽略延迟约束 500 100000000000
NUM_SEQS_LIST 空格分隔的 max-num-seqs 候选值列表 "128 256" "128 256"
NUM_BATCHED_TOKENS_LIST 空格分隔的 max-num-batched-tokens 候选值列表 "1024 2048 4096" "512 1024 2048 4096"

两个重要提示(来自 README 与脚本预检逻辑):

  • 长度预检:脚本启动时会校验 INPUT_LEN + OUTPUT_LEN <= MAX_MODEL_LEN,不满足直接以红色报错退出(见 auto_tune.sh),不需要等到起服务才发现问题。
  • 候选列表按场景调整:默认的 NUM_SEQS_LIST / NUM_BATCHED_TOKENS_LIST 面向中等长度的输入输出场景。对于极短上下文(例如 20 输入 / 20 输出 token),可能需要把 max-num-seqs 的候选值调得更大。

四、如何运行(How to Run)

  1. 配置:编辑脚本或按上节用环境变量设置参数。

  2. 执行:由于全程耗时较长,官方强烈建议在 tmuxscreen 中运行,防止断连导致实验中断:

    cd <FOLDER_OF_THIS_SCRIPT>
    bash auto_tune.sh
    

    这里 <FOLDER_OF_THIS_SCRIPT>benchmarks/auto_tune/ 目录。

关键陷阱:README 特别警告,运行命令(包括所在路径)中不能包含关键字 vllm(完整或部分均可),因为脚本内部通过 pkill -if "vllm serve" 清理上一轮的服务进程,若本脚本自身路径匹配到 vllm 关键字,pkill 会把正在执行的调优脚本一并杀掉。从 auto_tune.sh 可以看到清理动作出现在 start_server() 每次启动服务前、run_benchmark() 开始前和结束后,因此这一约束贯穿整个流程。

五、典型使用案例(Example Use Cases)

README 给出了三种不同目标的配置示例,全部基于 1800 输入 / 20 输出的短请求场景(MAX_MODEL_LEN=2048):

案例 1:最大化吞吐(无延迟约束)

目标:找出让 1800 输入 / 20 输出吞吐最高的参数组合。

INPUT_LEN=1800
OUTPUT_LEN=20
MAX_MODEL_LEN=2048
MIN_CACHE_HIT_PCT=0
MAX_LATENCY_ALLOWED_MS=100000000000   # 极大数,等效关闭延迟约束

案例 2:带延迟要求的最大化吞吐

目标:在 P99 E2E 延迟必须低于 500ms 的前提下最大化吞吐。

INPUT_LEN=1800
OUTPUT_LEN=20
MAX_MODEL_LEN=2048
MIN_CACHE_HIT_PCT=0
MAX_LATENCY_ALLOWED_MS=500

案例 3:前缀缓存命中率 + 延迟双约束

目标:假设业务有 60% 前缀缓存命中率,且 P99 E2E 延迟要求 500ms,找最优参数。

INPUT_LEN=1800
OUTPUT_LEN=20
MAX_MODEL_LEN=2048
MIN_CACHE_HIT_PCT=60
MAX_LATENCY_ALLOWED_MS=500

六、算法原理详解(How It Works,结合源码逐段解析)

README 概括了五个阶段:确定最大安全 gpu-memory-utilization → 枚举压测 → 延迟感知吞吐搜索 → 跟踪最优结果 → 最优配置 Profiling。下面结合 auto_tune.sh 的实现逐段展开。

6.1 第一步:OOM 安全的 gpu-memory-utilization 搜索

auto_tune.sh 可以看到:脚本从 gpu_memory_utilization=0.98 起步,以 0.01 为步长向下递减(直到 0.9 为止),每次用候选列表中最大的 max-num-seqsmax-num-batched-tokens 尝试启动服务——这是整个参数空间里显存压力最大的组合。一旦某个利用率能成功起服务,就固定该值用于后续所有压测;如果 0.9 仍起不来,则报错退出。这样保证后续所有 benchmark 都在"不 OOM 的最高显存利用率"下运行,结果最具可比性。

6.2 服务启动与健康检查

start_server()auto_tune.sh)的核心逻辑:

  • pkill -if "vllm serve" 清理残留服务,然后以统一参数数组后台启动:

    VLLM_SERVER_DEV_MODE=1 \
        vllm serve "$MODEL" \
            --port 8004 --host "$HOSTNAME" \
            --gpu-memory-utilization <util> \
            --max-num-seqs <seqs> --max-num-batched-tokens <tokens> \
            --tensor-parallel-size "$TP" \
            --enable-prefix-caching \
            --load-format dummy \
            --download-dir "$DOWNLOAD_DIR" \
            --max-model-len "$MAX_MODEL_LEN"
    

    注意两个实现细节:--load-format dummy 表示使用随机权重(调参阶段只关心调度/吞吐行为,跳过真实权重加载可显著加快每轮启动);--enable-prefix-caching 常驻开启,为案例 3 的命中率约束提供基础。

  • 启动后进入健康检查循环:每 10 秒请求一次 http://<host>:8004/health,同时用 kill -0 判断进程是否已崩溃,最多等待 60 次(10 分钟),超时即视为启动失败并返回错误码 1,由上层记录到对应 vllm_log_*.txt

  • VLLM_SERVER_DEV_MODE=1 并非摆设:它用于开启开发用端点。从 routers.py 可以看到,只有当该环境变量为真时才会注册 dev 路由(register_vllm_dev_api_routers),其中就包含下一节压测循环依赖的 POST /reset_prefix_cache(定义于 cache/api_router.py)。该端点仅在 dev mode 下暴露,官方文档 security.md 明确提示不应在生产环境开启。

6.3 第二步/第三步:延迟感知的吞吐搜索

run_benchmark()auto_tune.sh)对每个参数组合执行两轮压测,底层命令是 vllm bench serve(对应实现为 vllm/benchmarks/serve.py):

前缀命中率模拟MIN_CACHE_HIT_PCT 不是直接"要求缓存命中率达标",而是通过构造带公共前缀的随机数据集来模拟命中率。脚本计算 prefix_len = INPUT_LEN * MIN_CACHE_HIT_PCT / 100,令 adjusted_input_len = INPUT_LEN - prefix_len,压测时传 --random-prefix-len $prefix_len(每条请求共享该长度的公共前缀)和缩短后的 --random-input-len。命中率越高,前缀 prefill 复用越多,等效计算量越小。

第一轮:无穷请求率探极限(1000 条 prompt):

vllm bench serve \
    --backend vllm --model "$MODEL" --dataset-name random \
    --random-input-len $adjusted_input_len --random-output-len "$OUTPUT_LEN" \
    --ignore-eos --disable-tqdm \
    --request-rate inf \
    --percentile-metrics ttft,tpot,itl,e2el \
    --goodput e2el:"$MAX_LATENCY_ALLOWED_MS" \
    --num-prompts 1000 \
    --random-prefix-len $prefix_len \
    --host "$HOSTNAME" --port 8004

脚本从日志里 grep 出三项指标:Request throughput (req/s)P99 E2EL (ms)Request goodput (req/s)goodputvllm bench serve--goodput 参数计算(解析逻辑见 serve.pyparse_goodput,按 DistServe 论文定义统计满足 SLO 的请求速率)。

  • P99 E2EL <= MAX_LATENCY_ALLOWED_MS:该配置下极限吞吐即为有效吞吐,request_rate=inf,直接进入最优结果更新;
  • 若延迟超标:进入降速搜索循环——从 int(throughput) + 1 开始,每轮把请求速率减 1,重新压测(此时改用 100 条 prompt 降低成本),直到延迟达标或速率降到 0。每个速率档之间都会先 curl -X POST http://<host>:8004/reset_prefix_cache 清空前缀缓存并 sleep 5,保证各轮起点一致(这正是 6.2 节 dev mode 端点的用途)。

结果记录:每个组合的结果(无论达标与否)都追加写入 result.txt。达标组合中吞吐更高者会刷新 best_throughput / best_max_num_seqs / best_num_batched_tokens / best_goodput / best_request_rate 五个全局最优变量(auto_tune.sh)。

双层循环:主流程把 NUM_SEQS_LISTNUM_BATCHED_TOKENS_LIST 解析为两个 bash 数组后做嵌套遍历(auto_tune.sh),即候选值数量的乘积决定总轮数——例如默认配置下 2×4=8 轮完整压测,每轮还包含可能的多档降速,因此务必使用 tmux

6.4 第四步:最优配置的 Profiling 采集

压测全部结束后,只要找到了有效最优配置(best_throughput > 0),脚本会用最优的 max-num-seqs / max-num-batched-tokens 带 Profiling 重新起一次服务auto_tune.sh):

  • 服务端通过 --profiler-config '{"profiler": "torch", "torch_profiler_dir": "<profile_dir>"}' 启用 torch profiler(对应 vllm/config/profiler.pyProfilerConfig,服务端侧参数解析见 arg_utils.py 中的 --profiler-config);
  • 压测端在同一最优请求速率下追加 --profile 标志触发采集;
  • trace 落盘到 $BASE/auto-benchmark/<时间戳>/profile/(GPU 上为 .json trace,TPU 上为 .xplane.pb),可用于 TensorBoard 等工具做逐算子级深挖。

如果没有任何配置满足延迟约束,脚本跳过 Profiling 并如实记录"未找到可行配置"。

七、输出物解读(Output)

脚本结束后,所有产物位于 $BASE/auto-benchmark/YYYY_MM_DD_HH_MM/(时间戳目录,同名目录会先被删除重建):

产物 说明
vllm_log_<seqs>_<tokens>.txt(及 vllm_log_gpu_memory_utilization_*.logvllm_log_BEST_PROFILE.txt 每个参数组合的服务端日志
bm_log_<seqs>_<tokens>_requestrate_inf.txt / ..._requestrate_<rate>.txt 每轮 vllm bench serve 的压测日志(含全部 TTFT/TPOT/ITL/E2EL 百分位与 goodput)
result.txt 结果汇总,首行为 git hash,随后逐组合记录,末行为全局最优
profile/ 最优 run 的 profiler trace 目录

result.txt 的样例(来自 README):

# Example result.txt content
hash:a1b2c3d4...
max_num_seqs: 128, max_num_batched_tokens: 2048, request_rate: 10.0, e2el: 450.5, throughput: 9.8, goodput: 9.8
max_num_seqs: 128, max_num_batched_tokens: 4096 does not meet latency requirement 500
...
best_max_num_seqs: 256, best_num_batched_tokens: 2048, best_throughput: 12.5, profile saved in: /home/user/vllm/auto-benchmark/2024_08_01_10_30/profile

若末行是 best_max_num_seqs: 0, best_num_batched_tokens: 0, best_throughput: 0,说明没有找到可行参数——通常是服务根本没起来(检查对应 vllm_log_*.txt),或延迟约束过严(所有组合在速率降到 0 之前都未达标)。

八、批量调优:batch_auto_tune.sh

batch_auto_tune.sh 支持用单个 JSON 配置文件顺序跑多组 auto_tune.sh 实验,并把结果写回同一文件,适合做跨模型/跨场景的系统性调参矩阵。

前置依赖:必须安装 jq(解析 JSON);如需上传 GCS,还需安装并完成认证的 gcloud CLI(脚本会对这两者做启动检查,见 batch_auto_tune.sh)。

运行方式

bash batch_auto_tune.sh <path_to_json_file> [gcs_upload_path]
  • <path_to_json_file>:必填,JSON 配置文件路径;
  • [gcs_upload_path]:可选,形如 gs://my-bucket/benchmark-results 的 GCS 路径,每个 run 的日志与 profile 会通过 gcloud storage rsync 上传到 <gcs_upload_path>/<run_id>;留空则结果只留在本地文件系统(具体位置见该轮输出的 RESULT_FILE=... 行)。

配置文件格式:一个 JSON 对象数组,每个对象的 key 对应 auto_tune.sh 的配置变量(小写,运行时被转换为大写环境变量注入子进程),示例来自 README:

[
  {
    "base": "/home/user",
    "model": "meta-llama/Llama-3.1-8B-Instruct",
    "system": "TPU",
    "tp": 8,
    "input_len": 128,
    "output_len": 2048,
    "max_model_len": 2300,
    "num_seqs_list": "128 256",
    "num_batched_tokens_list": "8192 16384"
  },
  {
    "base": "/home/user",
    "model": "meta-llama/Llama-3.1-70B-Instruct",
    "system": "TPU",
    "tp": 8,
    "input_len": 4000,
    "output_len": 16,
    "max_model_len": 4096,
    "num_seqs_list": "64 128",
    "num_batched_tokens_list": "4096 8192",
    "max_latency_allowed_ms": 500
  }
]

结果回写:脚本以原地修改(in-place)方式把每轮结果写回输入 JSON 的对应对象,新增字段包括:

  • run_id:由时间戳目录名派生的唯一标识;
  • statusSUCCESSFAILUREWARNING_NO_RESULT_FILE(进程成功但找不到 result.txt);
  • results:该轮 result.txt 的完整内容;
  • gcs_results:产物在 GCS 上的 URL(若提供了 GCS 路径)。

实现上,脚本用 jq 遍历数组(batch_auto_tune.sh),每轮结束即把中间进度落盘(.tmp 原子替换),因此中途中断也能保留已完成轮次的结果;全部结束后打印成功/失败计数汇总,并列出失败轮次的序号与参数。

九、实战注意事项

结合文档与源码,以下细节直接影响调优结果的有效性:

  1. dummy 权重:服务以 --load-format dummy 启动,即随机权重。这对"调度参数对吞吐/延迟的影响"没有影响,但如果你的模型依赖特定量化/硬件路径,结果需要结合真实权重再复核一次。
  2. 端口固定为 8004:确保该端口空闲;脚本通过 --host $HOSTNAME 绑定本机主机名,不要同时手动跑其他 vllm serve 占用它。
  3. 路径避开 vllm 关键字:如 6.2 节所述,pkill -if "vllm serve" 的匹配范围是整条命令行,脚本自身工作目录/调用命令含 vllm 字样有被误杀风险。
  4. 10 分钟启动超时:大模型冷启动(下载权重、TP 初始化)超过 10 分钟会被判为启动失败,建议先用 DOWNLOAD_DIR 预拉权重。
  5. 延迟搜索的成本:延迟约束越严,每个组合的降速循环轮数越多(每档 100 条 prompt 的完整压测)。设置 MAX_LATENCY_ALLOWED_MS 时先粗估 SLO,避免候选值过多导致总时长失控;批量实验优先用 batch_auto_tune.sh 并配合 tmux
  6. 短请求要放大 NUM_SEQS_LIST:默认候选列表面向中等长度场景(如 4000/16),若跑 20/20 这类极短请求,应把 max-num-seqs 候选放大(README 已明确提示)。
  7. 结果可追溯result.txt 首行记录 git hash,跨版本对比调参结论时务必核对这一行,确保参数结论对应同一份代码。

十、小结

benchmarks/auto_tune/ 提供了一条从"参数候选空间"到"最优服务端配置 + 可深挖 profile"的完整自动化路径:auto_tune.sh 负责单模型单场景的 OOM 安全内存探测、延迟感知吞吐搜索与最优配置 Profiling;batch_auto_tune.sh 则以 JSON 驱动批量实验并回写结果,支持 GCS 归档。两者的配置变量、命令行示例与输出格式在 README 中均有完整说明,配合本文的源码级解析,即可在自有集群上快速复现并扩展这套调优流程。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
987
504
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384