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

文章来源声明: 原文作者:蜗牛聊AI; 来源站点:掘金; 原文链接:https://juejin.cn/post/7689043294571823155; 本文基于上述来源整理/加工,觅优补充点评,仅供技术学习交流。版权归原作者所有。
觅优短评

双轨架构把性能与可移植性的取舍摆上台面:硬件固定、模型集中选专用快车,硬件多变或要支持自研芯片则用通用层兜底,适合有CI门禁与兼容矩阵的推理团队。

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

发生了什么

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

新方案在核心仓库建立独立的硬件无关层,遵循四项原则:完整图可编译、允许CustomOp或PluggableLayer覆盖、与硬件专用层隔离、优先使用原生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。

<span>import</span> json
<span>import</span> sys

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

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

accepted = (
    native[<span>"passed"</span>] <span>and</span> portable[<span>"passed"</span>]
    <span>and</span> ratio >= <span>0.95</span>
    <span>and</span> p99_growth <= <span>0.10</span>
    <span>and</span> memory_growth <= <span>0.10</span>
)
<span>print</span>(<span>f"throughput_ratio=<span>{ratio:<span>.3</span>f}</span>"</span>)
<span>print</span>(<span>f"p99_growth=<span>{p99_growth:<span>.1</span>%}</span>"</span>)
<span>print</span>(<span>f"memory_growth=<span>{memory_growth:<span>.1</span>%}</span>"</span>)
<span>print</span>(<span>"accepted="</span>, 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,转载请注明出处。