Prhub

#47205 [Kernel][XPU] Tensor-descriptor operand loads for Triton W8A8 scaled_mm

原始 PR 作者 oonyshch 合并时间 2026-08-10 08:56 文件变更 5 提交数 7 评论 10 代码增减 +121 / -8

执行摘要

Triton W8A8 scaled_mm 引入 Tensor 描述符加载并启用 XPU 路径

PR body 指出,压缩张量 W8A8 INT8 linear 路径此前未在 XPU 上启用;且 kernel 使用掩码加载时,每个元素都要做 64 位地址计算,绕过了 Xe XMX 的 2D 块加载路径。TD 可以在 K 循环外一次性描述张量布局,把寻址开销卸载给硬件。性能数据(吞吐 +139.7%、TPOT -57.6%)直接验证了动机。

值得精读。重点看 TD 与掩码加载的设计权衡、转置权重(checkpoint 存储为 w_q.t())如何通过 B_T 区分并在 tile 内转置、以及 tri-state 环境变量(VLLM_TRITON_USE_TD)在各平台上的默认策略。这个模式对后续其他 kernel 的 TD 迁移有参考价值。

讨论亮点

该 PR 没有实质性的 review 评论:claude[bot] 因 fork PR 自动停用 review;维护者 jikunshang 直接 APPROVED。唯一值得注意的评论是 jikunshang 在 CI 中说明 xpu sleep model is fail on main. merge this.,即 XPU sleep model 测试在 main 上已失败,与本次改动无关,因此不阻塞合入。由于缺少代码级讨论,设计决策主要依赖 PR body 中给出的 microbenchmark 和准确率数据支撑。

实现拆解

  1. 核心 kernel 支持 TD 加载:在 vllm/model_executor/layers/quantization/compressed_tensors/triton_scaled_mm.pyscaled_mm_kernel 中新增 USE_TDB_T 两个 tl.constexpr。开启时通过 tl.make_tensor_descriptor 分别描述 A([M,K])和 B 的布局,B 区分两种方向:通常 [K,N],而转换后的 checkpoint 权重为 [N,K]strides=(1,K)),通过 B_T 分支描述 N 主序缓冲并在 tile 加载后 tl.trans 转置。K 循环内用 a_desc.load / b_desc.load 直接块加载,替代原来的 tl.load + mask 计算,避免 int64 偏移逐元素寻址。
  2. TD 启用的 gate 与资源初始化triton_scaled_mm 新增 use_td: bool | None 参数,通过共享的 use_tensor_descriptor(use_td) 解析全局 VLLM_TRITON_USE_TD(XPU 默认开启,其他平台需显式开启)。启用条件包括内维连续(input.stride(1)==1,权重 stride(1)==1b_t)、16 字节 tile 对齐、tile 尺寸为 2 的幂。首次在某个设备启用 TD 时调用 set_triton_allocator 并记录到 _TD_ALLOCATOR_DEVICES
  3. XPU 平台接入vllm/model_executor/kernels/linear/scaled_mm/triton.pyTritonInt8ScaledMMLinearKernel.is_supported 允许 XPU;vllm/model_executor/kernels/linear/__init__.py_POSSIBLE_INT8_KERNELS 中为 PlatformEnum.XPU 添加 Triton kernel。
  4. XPU int8 量化 fallbackvllm/_custom_ops.pyscaled_int8_quant 增加 XPU 分支:非对称量化直接抛 NotImplementedError;静态 per-tensor 用量化公式模拟;动态 per-token 复用 xpu_ops.dynamic_per_token_int8_quant_ref
  5. 测试配套tests/kernels/quantization/test_triton_scaled_mm.py 新增 test_scaled_mm_td_matches_plain,在 CUDA-alike 和 XPU 上对比 use_td=True/False 的输出,要求位级一致(rtol=0, atol=0),并覆盖 M=1/64/256K=N∈{4096,2048}、两种 scale_a 布局和是否含 bias 的组合。
文件 模块 状态 重要度
vllm/model_executor/layers/quantization/compressed_tensors/triton_scaled_mm.py 量化内核 modified 7.38
vllm/_custom_ops.py 自定义算子 modified 6.37
tests/kernels/quantization/test_triton_scaled_mm.py 量化测试 modified 5.83
vllm/model_executor/kernels/linear/scaled_mm/triton.py 线性内核 modified 5.46
vllm/model_executor/kernels/linear/__init__.py 内核注册 modified 4.93

关键符号

scaled_mm_kernel triton_scaled_mm scaled_int8_quant TritonInt8ScaledMMLinearKernel.is_supported

关键源码片段

vllm/model_executor/layers/quantization/compressed_tensors/triton_scaled_mm.py data-contract

核心内核实现:为 scaled_mm_kernel 增加 USE_TD/B_T 分支,用 tl.make_tensor_descriptor 替代掩码加载,并在 triton_scaled_mm 中实现 TD 启用 gate 与分配器初始化。

# 文件 : vllm/model_executor/layers/quantization/compressed_tensors/triton_scaled_mm.py
# scaled_mm_kernel 中新增 USE_TD / B_T 两个 constexpr 分支。
# USE_TD 开启时,用 tl.make_tensor_descriptor 描述 A、B 的张量布局,
# 在 K 循环内直接 block load,避免每元素 int64 偏移计算。if USE_TD:
    # 描述 A:shape [M, K],stride [stride_am, 1],
    # block_shape [BLOCK_SIZE_M, BLOCK_SIZE_K]
    a_desc = tl.make_tensor_descriptor(
        a_ptr,
        shape=[M, K],
        strides=[stride_am, 1],
        block_shape=[BLOCK_SIZE_M, BLOCK_SIZE_K],
    )
    if B_T:
        # checkpoint 权重以转置 [N, K] 视图存放(w_q.t()),
        # 这里描述底层 N 主序缓冲,并在每个 tile 加载后转置。
        b_desc = tl.make_tensor_descriptor(
            b_ptr,
            shape=[N, K],
            strides=[stride_bn, 1],
            block_shape=[BLOCK_SIZE_N, BLOCK_SIZE_K],
        )
    else:
        b_desc = tl.make_tensor_descriptor(
            b_ptr,
            shape=[K, N],
            strides=[stride_bk, 1],
            block_shape=[BLOCK_SIZE_K, BLOCK_SIZE_N],
        )for k in range(0, tl.cdiv(K, BLOCK_SIZE_K)):
    if USE_TD:
        a = a_desc.load([pid_m * BLOCK_SIZE_M, k * BLOCK_SIZE_K])
        if B_T:
            b = tl.trans(b_desc.load([pid_n * BLOCK_SIZE_N, k * BLOCK_SIZE_K]))
        else:
            b = b_desc.load([k * BLOCK_SIZE_K, pid_n * BLOCK_SIZE_N])
    else:
        # 普通掩码加载路径(原有逻辑),mask 由 offsets_k 生成
        masks_k = offsets_k < K
        masks_a = masks_am[:, None] & masks_k[None, :]
        a = tl.load(a_ptrs, mask=masks_a)
        masks_b = masks_k[:, None] & masks_bn[None, :]
        b = tl.load(b_ptrs, mask=masks_b)
    accumulator = tl.dot(a, b, accumulator, out_dtype=accumulator_dtype)
# 外层 triton_scaled_mm 的 TD 启用判断。
# 条件:use_tensor_descriptor(use_td) 允许(tri-state 环境变量),
# 且内维连续、16 字节对齐、tile 为 2 的幂。
b_t = weight.stride(1) != 1 and weight.stride(0) == 1
b_inner = K if b_t else N
use_td = (
    use_tensor_descriptor(use_td)
    and input.stride(1) == 1
    and (weight.stride(1) == 1 or b_t)
    and (K * input.element_size()) % 16 == 0
    and (b_inner * weight.element_size()) % 16 == 0
    and (block_size_m & (block_size_m - 1)) == 0
    and (block_size_n & (block_size_n - 1)) == 0
    and (block_size_k & (block_size_k - 1)) == 0
)
# TD 需要专用分配器,每个设备只初始化一次。
if use_td and input.device not in _TD_ALLOCATOR_DEVICES:
    set_triton_allocator(input.device)
    _TD_ALLOCATOR_DEVICES.add(input.device)
vllm/_custom_ops.py dependency-wiring

为缺少原生 _C 量化算子的 XPU 设备补充 scaled_int8_quant 的 fallback 路径,使 W8A8 推理链路在 XPU 上可完整运行。

# 文件 : vllm/_custom_ops.py
# scaled_int8_quant 在 XPU 上走 torch.compile 参考实现,
# 因为 XPU 没有对应的 _C 原生 int8 量化算子。
if current_platform.is_xpu():
    # XPU 仅支持对称量化,asymmetric 直接报错,避免静默错误结果。
    if not symmetric:
        raise NotImplementedError(
            "asymmetric int8 activation quantization is unsupported on XPU"
        )
    # 静态 per-tensor 量化:用 float32 除法 + round + clamp 模拟。
    if scale is not None:
        q = (input.to(torch.float32) / scale).round().clamp(-128, 127)
        return q.to(torch.int8), scale, None
​
    # 动态 per-token 量化:复用 xpu_ops 的参考实现。
    from vllm._xpu_ops import xpu_ops
    q, scales, _ = xpu_ops.dynamic_per_token_int8_quant_ref(
        input.contiguous(), True, 8
    )
    return q, scales.reshape(-1, 1).to(torch.float32), None

评论区精华

XPU 相关 CI 失败是否阻塞合入 other

jikunshang 在 Issue 评论中指出 main 上 XPU sleep model 测试已失败,与本次变更无关,仍决定合并。

结论:确认 XPU sleep model 失败属于 main 已存在问题,不阻塞本 PR。 · 已解决

风险与影响

TD 门控条件(内维连续、16 字节对齐、2 的幂 tile)若未覆盖某些合法布局会回退到普通路径,逻辑上是安全的;但若 b_t 检测不准确可能产生错误结果,测试仅覆盖有限形状。XPU 上新启用的 scaled_int8_quant fallback 依赖 torch.compile 参考实现,不支持 asymmetric 量化,遇到不对称权重量化会直接抛 NotImplementedError;且动态量化走 xpu_ops 参考路径,性能未充分验证。此外,XPU kernel 注册顺序将 TritonInt8ScaledMMLinearKernel 列为唯一候选,可能导致此前可用的其他 XPU int8 路径被替代,需确认无回归。

对 XPU 用户:首次获得 compressed-tensors W8A8 INT8 模型支持,并默认启用 TD,显著提升吞吐、降低 TTFT/TPOT;对 CUDA/ROCm 用户:功能默认关闭,行为不变;对团队:新增一条平台专用量化内核路径,需要为 XPU 维护 TD 特性和 fallback 参考实现。改动集中在 kernel 层、量化层和线性内核注册,影响面有限但跨模块。

新平台路径启用 TD 门控条件严格 XPU 量化 fallback 依赖 torch.compile 参考实现 测试覆盖形状有限

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论