以 use_rocm_custom_paged_attention 实测;定制内核对比 Triton 兜底,带来 2 到 7 倍加速
在 gfx11 上,vLLM 的 ROCm 定制 paged-attention 内核只有在 query head 与 KV head 的比值达到 3 或更高时才会启用。而在被排除的区间内,定制内核比本该启用的 Triton 兜底快 1.70 到 7.28 倍。面对如此长序列的求和,两者的数值在 bf16 精度容限内完全等价。六十个格子、两个 vLLM 版本,无一例外。
这道门限由
vllm#17004 引入。
该 PR 自身的描述写明,该内核“支持 gqa_ratio 到 16,并在 gqa_ratio 为 3 或更高时相对现有内核有性能收益”。因此这道闸门是性能启发式,而非功能能力限制。在 CDNA 架构上,同一个函数从 ratio 1 起便直接调用该定制内核。这两点在 main 分支上依然成立。
但实测表明,这条启发式是反的。在涵盖五种形状、六种上下文长度、跨越两代 vLLM 的全部六十个实测格子里,定制内核在每一个格子中都更快。在两个版本上,被排除区间的性能带与放行区间的性能带完全重叠。这道门限没有区分出任何区间。
最初引发关注的报告 vllm#50603 认为长上下文下的精度退化应归咎于兜底路径。但数据并不支持该假说。在两种架构的 90 个格子上,相对误差均稳定在 2.12e-3 到 4.72e-3 之间,且任意两个深度的汇总中位数差异均未超过 1.09 倍。然而,只有当测试体系具备捕获故障的能力时,平坦的误差带才有说服力。为此,同一套测试框架被注入了人工故障:在最后一个 KV tile 未写入数据的尾部注入 NaN,两条路径都被污染,且恰好只在跨越该边界的长度上出现,其余长度都没有。
在真实模型上进行端到端评估,放宽这道闸门在 1K 上带来 1.02 倍加速,在 32K 上则达到 1.17 倍;在较新镜像上调换运行顺序重复测试,结果均成功复现。这项修改已提交于 vllm#54210,本页面即为其支撑依据。
两条执行路径均经由 vLLM 原生的 chunked_prefill_paged_decode 分发逻辑驱动,仅强制切换 use_rocm_custom_paged_attention 配置。因此两组之间的参数编排完全一致。测试针对纯解码负载展开:每个序列仅包含一个 query token,上下文已全量进入缓存,与原报告描述的场景一致。fp32 参考基准基于分页之前的 KV 张量计算,而非读取缓存,确保分页层面的缺陷不会相互抵消。评估指标采用以参考基准自身量纲归一化的相对 L-infinity 误差,从而保证了不同上下文长度之间的可比性。
测试的形状集合覆盖 head size 128 与 block size 16 配置下的 ratio 1、2、3 和 4,外加 32/16。这最后一个形状源自 gemma-3-27b-it,满足 gfx11 这道闸门除比值外的所有准入条件。至于 CUDA 对照组,则是将内核原样提取至独立模块并编译同一份 Triton 文本,其算子调用参数完全镜像了分发处的实参列表。
核心论据在于两条分布带的整体重叠,而非个别单元格的最大加速比。在 0.23.1 上,被排除比值的加速范围在 1.84× 至 7.28× 之间,而放行比值的加速范围则是 2.35× 至 7.40×。在 0.27 上,被排除形状的加速比跨越 1.70× 到 6.05×,放行形状则是 2.20× 到 6.05×。两个版本中两者的分布带完全重叠,这正是“该界限未能区分任何实际差异”的实质所在。
原始报告描述了两个症状。其一是系统走到了兜底路径,这一现象已获证实,前文所有论述均围绕它展开。其二是兜底路径的精度会随上下文长度增长而恶化。这一结论在此处完全可测,但数据并不支持该假说。
单纯的内核微基准测试不足以构成提交 PR 的充分理由。为此,我们在开启 CUDA graphs 的条件下,使用 gemma-3-27b-it-w4a16 在 TP=2 配置下开展了端到端模型实测。解码延迟通过 64 与 8 的差分测试进行剥离,每个单元格均启动一个全新容器,避免源码修改在两组之间发生交叉污染:
算子路由状态直接在 TP worker 内部记录,而非通过耗时反推。在 0.23.1 上测试覆盖了每一个格子;而在 0.27 上,由于原地记录器未捕获到任何数据,随后通过专门的一轮测试单独记录了 1K 长度下的两组表现。全注意力层的输入参数满足 head size 128、block size 16、原生 KV 布局以及 2 的幂 block 大小,因此比值条件是其未能通过准入闸门的唯一原因;至于滑动窗口层,在两组中均直接受阻于 sliding_window == 0,执行路径从未改变。
放开一道闸门,潜藏其后的问题也会随之引入。上游 PR vllm#53856 修复的正是该定制内核中的一处真实缺陷:虽然内核对序列末尾之外的 logit 施加了掩码,但其向量化的 V-cache 读取操作仍会加载整个末尾数据块,导致尾部未写入的 NaN 污染计算输出。该 PR 的测试计划特别提到,由于作者缺少相关硬件,gfx11 平台仅通过了编译验证。
强制执行那一列与本项提议息息相关。一旦强行绕过闸门,原样对照组受污染的单元格便从四个增加到八个:ratio 2 在跨越边界的长度上以相同方式出现故障,涵盖两种 dtype 以及仅投毒 V 和同时投毒 K+V 的场景。而在打上补丁的那一组中,这八个格子全部恢复正常。因此,放宽这道准入闸门便会将 ratio 2 纳入该内核,连带放进来的正是这一处故障。这正是两项改动密不可分的原因,也是我们应作者请求向该 PR 提供 gfx11 运行时验证数据的初衷。
当前测试仅排除了一个假说,即 Triton 2D 内核会随上下文增长而损失精度。报告者观察到的精度退化发生在不同的硬件与此地未配备的 VLM 模型上;本测试仅针对算子内核本身,未涉及模型端到端输出。原报告反映的症状仍有待进一步阐明。
滑动窗口形状、fp8 KV、预填充形状以及内核层面的 TP>1 均未涉足。模型基准测试虽使用了 TP=2,但属于系统级宏观测量,并不代表单内核的并行行为。
鉴于 allocate_kv_cache 会对显存做零填充,该缺陷并非未初始化残留垃圾所致;vllm#53856 将其归因于休眠模式与分配器的特定行为。本测试套件系主动注入 NaN,旨在观察内核在面对脏数据时的处理反应。
全部测量均在一台 gfx1100 设备上完成。针对关联问题,实测证实 Model Runner V2 确会触发相同的保护逻辑,37 行测试中有 36 行与 V1 完全一致。至于投机解码是否会导致 MRv2 下的解码携带多行 query,因当前镜像缺乏可用 checkpoint 而在这台机器上未做实测,该判断依托于静态代码研读。
use_rocm_custom_paged_attention 的 gfx11 分支做了一行改动:一增一删。截至 2026-09-09 仍处于开启状态:维护者于 2026-09-07 要求内核正确性与完整 gsm8k 结果,当天已补交;尚无正式评审。PR 正文汇总了本页各表,仓库中完整保存了计算这些结果的数据行。
verify_doc_figures.py 重新计算生成,不再采用手工抄录。
verify_doc_figures.py 锁定。
benchmarks/vllm-50603/README.md 中的记录。那里的路由信息来自 route_027.py 在 1K 长度下对两组的专项独立运行。正文现已准确体现该区别。此外,摘要中的英文原词为“not supported”,中文初稿曾译为语气更坚决的“不成立”,现已修正以保持一致。
全部测试单元格。所有探针源码、输出 JSONL 以及容器日志均归档于
benchmarks/vllm-50603/,包括独立提取出的 CUDA 对照内核以及校验两侧编译相同 Triton 文本的哈希断言。本页所有图表均由
verify_doc_figures.py
直接依据上述文件重新计算验证。
上游链接:vllm#50603 为原始报告, vllm#17004 引入了该准入界限, vllm#54210 为单行改动提议, vllm#53856 修复了图 4 中所示的缺陷。