vLLM 正在调整内部实现,以继续追求前沿推理性能。一个直接影响是:这些实现变化可能无法再满足 torch.compile(fullgraph=True) 的严格要求。对大多数只通过 vLLM API 部署模型的团队,这未必意味着服务会失效;但如果你的集成层强制要求单一完整计算图,升级就可能暴露编译错误、回退行为或性能偏差。
这里最重要的判断不是“vLLM 还能不能使用 torch.compile”,而是:你的系统是否把 fullgraph 兼容性 当成了必须成立的契约。
fullgraph 不是普通的性能开关
torch.compile(fullgraph=True) 要求被编译的函数完整捕获为一个计算图。一旦遇到无法纳入图中的 Python 控制流、自定义操作、运行时调度或显式禁用编译的区域,它通常不会像默认模式那样安静地发生 graph break,而是直接报错。
下面这个最小示例可以帮助团队理解两种模式的差异。运行前需要安装 PyTorch:
python -m pip install torch
保存为 fullgraph_demo.py:
import torch
@torch.compiler.disable
def eager_only(x: torch.Tensor) -> torch.Tensor:
return x.sin()
def model(x: torch.Tensor) -> torch.Tensor:
y = x * 2
return eager_only(y) + 1
x = torch.randn(8)
# 默认模式允许在必要时切分计算图。
compiled_flexible = torch.compile(model, fullgraph=False)
print("flexible:", compiled_flexible(x))
# fullgraph=True 要求整个函数形成一张图,通常会在这里报错。
try:
compiled_strict = torch.compile(model, fullgraph=True)
print("strict:", compiled_strict(x))
except Exception as exc:
print("fullgraph failed:", type(exc).__name__)
print(str(exc).splitlines()[0])
执行:
python fullgraph_demo.py
这个例子并不是在复现 vLLM 内部实现,而是在说明 fullgraph 的约束边界:只要高性能运行时需要离开单一 PyTorch 图,严格 fullgraph 就可能与运行时设计发生冲突。
“硬件无关”不等于“所有后端共用一张图”
硬件无关模型更合理的含义,是把模型语义与具体执行后端分开:模型层描述注意力、归一化、采样等行为,底层根据 GPU、加速器和内核能力选择执行路径。
这类分层通常需要保留一定的运行时自由度,例如:
- 根据硬件能力选择不同算子或内核;
- 在模型逻辑之外管理缓存、批处理和调度;
- 调用无法被通用 PyTorch 图完整表达的自定义操作;
- 对关键路径采用专门的编译或执行机制。
这些例子是理解架构取舍的实践视角,并不代表摘要确认了 vLLM 使用其中每一种技术。可以确定的是,vLLM 为追求更高性能而进行的内部变化,正在与 fullgraph 模式的“整段程序必须形成单图”要求产生不兼容。
这也意味着,不应把“与 fullgraph 不兼容”扩大解读成“完全不支持 torch.compile”或“模型无法运行”。默认编译模式、局部编译以及 vLLM 自己管理的优化路径,是不同层次的问题,应分别验证。
哪些用户最容易受到影响
风险最高的通常不是标准 API 调用方,而是对 vLLM 做了额外封装的团队:
-
外层框架强制开启
fullgraph=True
平台可能为了统一管理模型,把所有nn.Module或推理入口都包进严格编译流程。 -
CI 把“零 graph break”作为升级门槛
即使结果正确,内部实现变化也可能让这类断言失败。 -
自定义模型或算子依赖完整图导出
如果后续流程还要导出、重写或缓存整张图,兼容性要求会更严格。 -
通过编译指标判断性能健康度
graph 数量、重编译次数和捕获比例发生变化后,旧告警阈值可能不再适用。
升级前可以先扫描项目中是否显式使用了严格模式:
grep -RIn --exclude-dir=.git \
-E 'fullgraph[[:space:]]*=[[:space:]]*True|torch\.compile' \
.
同时开启 PyTorch 编译日志,运行现有测试或服务入口:
TORCH_LOGS="graph_breaks,recompiles" python app.py 2>&1 | tee compile.log
日志格式和可用类别可能随 PyTorch 版本变化,因此应把它当作诊断手段,而不是稳定的生产接口。
用服务级验收替代“必须是一张图”
如果部署目标是提供推理 API,更可靠的验收标准通常是输出正确性、首 token 延迟、吞吐量、显存占用和长时间稳定性,而不是要求整个引擎必须被外部编译成一张图。
下面是一套可以改造的 vLLM 冒烟测试。假设机器已经配置了受支持的加速器,并且当前 vLLM 版本提供 vllm serve 命令;请将模型名替换为你实际使用且有权限访问的模型:
python -m pip install vllm
export MODEL="Qwen/Qwen2.5-0.5B-Instruct"
vllm serve "$MODEL" \
--host 127.0.0.1 \
--port 8000
在另一个终端发送请求:
curl --fail --silent http://127.0.0.1:8000/v1/chat/completions \
-H 'Content-Type: application/json' \
-d "{
\"model\": \"${MODEL}\",
\"messages\": [
{\"role\": \"user\", \"content\": \"用一句话解释什么是张量。\"}
],
\"temperature\": 0,
\"max_tokens\": 64
}" | python -m json.tool
在真实升级测试中,可以把同一批固定请求分别发送给旧版本和候选版本,比较:
- HTTP 成功率与错误类型;
- 确定性配置下的输出差异;
- 首 token 延迟和每秒 token 数;
- 峰值显存与持续运行后的显存变化;
- 不同输入长度、批量大小和并发度下的表现。
如果平台本身还有一层 torch.compile(..., fullgraph=True),优先考虑把 vLLM 引擎划为由其自身管理的执行边界,而不是继续在引擎外部强制捕获完整图。是否关闭严格模式,应通过上述基准测试决定,不能只凭“编译成功”判断。
升级时应保留哪些边界
可以把迁移检查压缩成四项:
- 搜索并记录所有
fullgraph=True的调用点,确认是谁设置、为什么设置; - 不要把 fullgraph 不兼容误判为所有编译和优化能力都不可用;
- 使用真实模型、真实硬件和代表性流量做版本对照测试;
- 将正确性、吞吐、延迟和显存设为发布门槛,把单图捕获降为诊断指标。
硬件无关模型的价值,在于让上层模型定义不必绑定某一种设备实现;代价则是底层运行时需要更大的优化空间。对 vLLM 用户而言,稳妥的做法不是坚持旧的编译假设,而是明确系统边界:由 vLLM 管理推理执行,由业务平台验证可观察的服务结果。