vLLM为了新GPU拆掉旧抽象,为什么还要再造一套可移植层

13 阅读1分钟

大模型推理框架长期追求“一套代码跑多种芯片”,但新模型的注意力、混合专家和通信方式越来越专用,新GPU又要求更激进的融合内核。vLLM的新答案不是继续扩大万能抽象,而是双轨:热门模型走硬件专用快车,长尾模型和非主流加速器保留可编译、可扩展的通用路径。

发生了什么

PyTorch基金会在9月22日发布vLLM硬件无关模型层的技术说明。目前vLLM同时存在新的Flat Model、旧模型定义和Transformers后端。Flat Model为特定模型与硬件直接做融合和专用优化,不依赖完整图的torch.compile;代价是旧GPU、消费卡和树外加速器可能需要各自维护实现。

新方案在核心仓库建立独立的硬件无关层,遵循四项原则:完整图可编译、允许CustomOpPluggableLayer覆盖、与硬件专用层隔离、优先使用原生PyTorch或Triton、Helion等可移植领域语言。官方在H100上比较三种近期模型,称几何平均总Token吞吐与原生路径相差不超过3.4%。这是有限模型和单类GPU的结果,不能外推到所有模型、延迟和显存。

技术原理:性能可移植性不是最高性能

torch.compile能追踪模型图,再由后端把图降低到目标硬件;树外插件还能覆盖少数特殊算子。它的优势是共享模型逻辑,弱点是遇到动态控制、独特内存布局或硬件专用通信时,抽象会限制优化。

Flat Model反过来:直接按NVIDIA、AMD或其他硬件写最合适的模型定义和融合内核,性能上限高,但维护矩阵迅速膨胀。硬件无关层的目标并非击败专用路径,而是在“换卡仍能运行、性能损失可接受、插件仍能扩展”之间给出稳定基线。

flowchart TD
    A[模型请求] --> B{有硬件专用Flat Model?}
    B -- 是 --> C[专用模型定义与融合内核]
    B -- 否 --> D[Transformers模型定义]
    D --> E[重连到硬件无关层]
    E --> F[torch.compile完整图]
    F --> G{后端需要特殊布局?}
    G -- 是 --> H[插件覆盖CustomOp]
    G -- 否 --> I[PyTorch Triton或Helion实现]
    C --> J[同一业务基准]
    H --> J
    I --> J
    J --> K[比较吞吐 延迟 显存 正确性]

最小实践:先把“差不多”变成门槛

官方文章给出演示开关,但vLLM主分支代码当前暴露的环境变量名是VLLM_USE_HW_AGNOSTIC。预览功能变化快,应锁定提交并以目标版本源码为准。下面脚本比较两次压测生成的JSON文件,不依赖GPU;保存为compare.py,运行python3 compare.py native.json portable.json

import json
import sys

def load(path):
    with open(path, encoding="utf-8") as file:
        data = json.load(file)
    required = {"tokens_per_s", "p99_ms", "peak_gib", "passed"}
    missing = required - data.keys()
    if missing:
        raise ValueError(f"missing fields: {sorted(missing)}")
    return data

native, portable = map(load, sys.argv[1:3])
ratio = portable["tokens_per_s"] / native["tokens_per_s"]
p99_growth = portable["p99_ms"] / native["p99_ms"] - 1
memory_growth = portable["peak_gib"] / native["peak_gib"] - 1

accepted = (
    native["passed"] and portable["passed"]
    and ratio >= 0.95
    and p99_growth <= 0.10
    and memory_growth <= 0.10
)
print(f"throughput_ratio={ratio:.3f}")
print(f"p99_growth={p99_growth:.1%}")
print(f"memory_growth={memory_growth:.1%}")
print("accepted=", accepted)

先用同一模型、提示分布、并发和生成长度分别跑原生与通用路径,把结果写入两个JSON,再由脚本统一判定。本次用合成结果实际运行:吞吐比0.970、P99增长5.0%、峰值显存增长3.0%,门禁通过。没有安装vLLM或在GPU上推理,不能视为官方3.4%结论的复现。

压测文件中的passed不应只是“服务返回200”。至少要覆盖固定随机种子下的输出一致性、结构化工具调用能否解析、长上下文是否越界,以及并发期间有没有静默回退到CPU。吞吐也要同时报告输入与输出Token,P99按请求阶段拆成排队、预填充和解码;否则一种路径可能用更低吞吐换来更稳的尾延迟,却被单一数字错误淘汰。

开发者应该怎样选

若你只服务少数热门模型、硬件固定且吞吐决定成本,优先专用路径;若模型长尾、硬件供应经常变化,或要支持自研加速器,通用路径能减少移植与升级成本。现实做法通常不是二选一,而是建立能力表:每个模型—硬件组合记录正确性、支持的量化、最大上下文、吞吐和回退路径。

具体场景是云平台临时拿不到目标GPU。通用路径若能在另一类卡上以可接受性能启动,可以避免业务停摆;但“能启动”不等于数值一致,采样输出、工具调用JSON、长上下文和并发下的尾延迟都要重新验收。

建议维护一张可自动生成的兼容矩阵:横轴是模型版本与量化方式,纵轴是硬件和vLLM提交,单元格记录实现路径、已知回退、正确率、吞吐、P99和峰值显存。升级前先跑受影响单元格,而不是把一次H100成功当成整个集群的通行证。

我的判断、边界与行动清单

**这次变化承认了一个现实:极致性能与广泛可移植性已经不是同一份模型代码自然兼得的属性。**双轨架构比把所有差异藏进一层抽象更诚实,也把选择权交给部署团队。

当前实现仍在建设,有限层已进入主分支,部分Flat Model的通用版本尚未落地。上线前锁定vLLM提交和镜像;确认环境变量与模型实现路径;用真实流量分别测吞吐、P50/P99、峰值显存和任务正确率;故意禁用专用内核验证回退;最后把结果写进CI,而不是只保存一次跑分截图。

不适合直接采用的情况包括:强依赖尚未覆盖的定制算子、必须追逐Blackwell等最新硬件极限,或没有资源维护双套基准。此时应等待支持成熟,或明确承担专用实现成本。

你的推理服务更愿意牺牲3%的吞吐换可移植性,还是为每类硬件维护专用路径?

关注「蜗牛聊AI」,一起看懂技术变化背后的真正机会。


本文首发于 java4u.cn,转载请注明出处。