Prhub

#44324 [CPU][RISC-V] Add RVV micro GEMM for WNA16

原始 PR 作者 wcynb1023 合并时间 2026-06-22 20:53 文件变更 4 提交数 6 评论 2 代码增减 +273 / -2

执行摘要

为 CPU WNA16 添加 RVV 微 GEMM 内核,加速 2.4-3.2x

RISC-V CPU 上现有 VEC 路径使用通用向量抽象,寄存器压力大,性能不理想。本 PR 旨在通过 RVV 专用微 GEMM 提升 WNA16 量化模型的推理速度,满足国产 RISC-V 硬件的部署需求。PR 描述中给出加速比和数值匹配结果。

值得精读。设计上清晰地分离了 ISA 枚举、微 GEMM 内核、C++ 分发、Python 前端,层次分明。RVV 内核实现中 Mx8 tile 和 K 展开的策略可推广到其他量化场景。代码注释丰富,适合作为为特定 ISA 添加微内核的参考。

讨论亮点

本 PR 无 Review 讨论,maintainer @bigPYJ1151 直接批准合并。

实现拆解

实现分为四步:

  1. ISA 枚举扩展:在 csrc/cpu/utils.hpp 中新增 ISA::RVV 枚举值,并在 get_isa() 中添加对 "rvv" 字符串的解析,为后端分发做准备。
  2. RVV 微 GEMM 内核:新增 csrc/cpu/micro_gemm/cpu_micro_gemm_rvv.hpp,实现 MicroGemm<ISA::RVV, scalar_t>。核心内核函数 gemm_micro_rvv_fma_mx8_ku4 使用 Mx8 内部 tile,保持外部 N=32 的 packed weight 布局兼容;通过标量-向量 FMA 利用激活广播模式;K 循环按 4 展开以减少指令开销。同时提供 load_row8_b_as_f32 模板特化,支持 float/Half/BFloat16 到 float 的高效加载,并利用可选的 zvfh/zvfbfmin 扩展。
  3. C++ 连接:在 csrc/cpu/cpu_wna16.cpp 中通过 #if defined(__riscv_v) 条件包含新头文件,在 cpu_gemm_wna16 的 ISA 解析分支中添加 "rvv" 映射,并实例化 MicroGemm<ISA::RVV, scalar_t> 及对应的 Dequantizer4b,复用相同的反量化逻辑。
  4. Python 前端:在 vllm/model_executor/kernels/linear/mixed_precision/cpu.py 中导入 CpuArchEnum,在 _get_isa_hint() 中检测当前 CPU 架构是否为 RISC-V,若是则返回 "rvv",确保 ops.cpu_gemm_wna16 接收到正确的 ISA 提示。
文件 模块 状态 重要度
csrc/cpu/micro_gemm/cpu_micro_gemm_rvv.hpp CPU 内核 added 8.6
csrc/cpu/cpu_wna16.cpp CPU 推理 modified 6.65
csrc/cpu/utils.hpp CPU 工具 modified 5.46
vllm/model_executor/kernels/linear/mixed_precision/cpu.py 量化驱动 modified 5.44

关键符号

gemm_micro_rvv_fma_mx8_ku4 load_row8_b_as_f32 cpu_gemm_wna16 _get_isa_hint get_isa

关键源码片段

csrc/cpu/micro_gemm/cpu_micro_gemm_rvv.hpp core-logic

核心新增文件,实现 RVV 微 GEMM 内核,包含模板化的 load 和 FMA 内核。

// RVV 微 GEMM 内核:固定 N=8 内部 tile,K 循环展开 4 次,使用标量 - 向量 FMA。
// 该函数被 MicroGemm<ISA::RVV, scalar_t> 调用,完成 Mx8 子矩阵乘法。
template <int32_t M, typename scalar_t>
FORCE_INLINE void gemm_micro_rvv_fma_mx8_ku4(
    const scalar_t* __restrict__ a_ptr, // [M, K] 激活矩阵
    const scalar_t* __restrict__ b_ptr, // [K, 8] 权重矩阵 ( 部分 packed)
    float* __restrict__ c_ptr, // [M, 8] 累加结果
    const int64_t lda, const int64_t ldc, // leading dimensions
    const int32_t k, const bool accum_c) {
  static_assert(0 < M && M <= 8);  // 声明 8 行指针和累加器 ( 使用宏展开,避免重复代码 )
  #define RVV_ROWS_APPLY(OP) OP(0) OP(1) OP(2) OP(3) OP(4) OP(5) OP(6) OP(7)
  #define RVV_IF_M(i) if constexpr (M > (i))  #define RVV_DECL_A(i) const scalar_t* __restrict__ a##i = a_ptr + (i) * lda;
  RVV_ROWS_APPLY(RVV_DECL_A)
  #undef RVV_DECL_A  #define RVV_DECL_ACC(i) fixed_fp32x8_t acc##i;
  RVV_ROWS_APPLY(RVV_DECL_ACC)
  #undef RVV_DECL_ACC  // 初始化累加器:若 accum_c 则加载已有值,否则清零
  #define RVV_INIT_ACC(i) \
    RVV_IF_M(i) { \
      if (accum_c) { \
        acc##i = RVVI(__riscv_vle32_v_f32, LMUL_256)(c_ptr + (i) * ldc, RVV_MGEMM_N8); \
      } else { \
        acc##i = RVVI(__riscv_vfmv_v_f_f32, LMUL_256)(0.0f, RVV_MGEMM_N8); \
      } \
    }
  RVV_ROWS_APPLY(RVV_INIT_ACC)
  #undef RVV_INIT_ACC  // 主循环:每次处理 4 个 K 元素,减少循环开销
  int32_t k_idx = 0;
  for (; k_idx + 3 < k; k_idx += 4) {
    // 加载 b 矩阵的一行(N=8),并转换为 float
    // 实际由 load_row8_b_as_f32 根据 scalar_t 类型特化实现
    #define RVV_STEP_K(K_OFFSET) \
    { \
      fixed_fp32x8_t b = load_row8_b_as_f32<scalar_t>( \
          b_ptr + (k_idx + (K_OFFSET)) * RVV_MGEMM_B_GROUP_STRIDE); \
      RVV_FMA_ROW(0, K_OFFSET) \
      RVV_FMA_ROW(1, K_OFFSET) \
      RVV_FMA_ROW(2, K_OFFSET) \
      RVV_FMA_ROW(3, K_OFFSET) \
      RVV_FMA_ROW(4, K_OFFSET) \
      RVV_FMA_ROW(5, K_OFFSET) \
      RVV_FMA_ROW(6, K_OFFSET) \
      RVV_FMA_ROW(7, K_OFFSET) \
    }
    // RVV_FMA_ROW 使用 vfmacc 执行标量 - 向量融合乘加
    #define RVV_FMA_ROW(i, K_OFFSET) \
      RVV_IF_M(i) { \
        acc##i = RVVI(__riscv_vfmacc_vf_f32, LMUL_256)( \
            acc##i, static_cast<float>(*(a##i + k_idx + (K_OFFSET))), b, RVV_MGEMM_N8); \
      }    RVV_STEP_K(0)
    RVV_STEP_K(1)
    RVV_STEP_K(2)
    RVV_STEP_K(3)
    #undef RVV_STEP_K
    #undef RVV_FMA_ROW
  }
  // ... 处理剩余 K 元素 ( 省略 )
}
csrc/cpu/cpu_wna16.cpp dependency-wiring

桥接文件,通过条件编译包含 RVV 头文件,并在 dispatch 函数中添加 RVV 分支。

// cpu_wna16.cpp 中新增的 RVV 分发分支(位于 cpu_gemm_wna16 函数内)
  } else if (isa == ISA::RVV) {
    // 实例化 MicroGemm<ISA::RVV, scalar_t>
    using gemm_t = cpu_micro_gemm::MicroGemm<ISA::RVV, scalar_t>;
    if (has_zp) {
      using dequantizer_t = Dequantizer4b<scalar_t, ISA::RVV, true, false>;
      cpu_gemm_wna16_impl<scalar_t, dequantizer_t, gemm_t>(
          input.data_ptr<scalar_t>(), q_weight.data_ptr<int32_t>(),
          output.data_ptr<scalar_t>(), scales.data_ptr<scalar_t>(), zeros_ptr,
          g_idx_ptr, bias_ptr, a_m_size, b_n_size, a_k_size, a_m_stride,
          output_m_stride, scales_group_stride, zeros_group_stride, group_num,
          group_size, pack_factor);
      return;
    }
    if (use_desc_act) {
      // 类似,使用 Dequantizer4b<scalar_t, ISA::RVV, false, true>
      // ...
    } else {
      // 一般情况:无 zero-point,无 desc_act
      using dequantizer_t = Dequantizer4b<scalar_t, ISA::RVV, false, false>;
      cpu_gemm_wna16_impl<scalar_t, dequantizer_t, gemm_t>( ... );
      return;
    }
  }
vllm/model_executor/kernels/linear/mixed_precision/cpu.py data-contract

Python 前端检测 RISC-V 架构并传递 "rvv" isa_hint 至 C++ 后端。

# cpu.py 中 _get_isa_hint 函数修改后
def _get_isa_hint(dtype: torch.dtype) -> str:
    supports_amx = torch.cpu._is_amx_tile_supported()
    if supports_amx and dtype in (torch.bfloat16,):
        return "amx"
    elif current_platform.get_cpu_architecture() == CpuArchEnum.RISCV:
        # RISC-V 平台使用 RVV 微 GEMM 后端
        return "rvv"
    else:
        return "vec"

评论区精华

没有提炼出高价值讨论线程

当前评论区没有形成足够清晰的争议点或结论,后续有更多讨论时会体现在这里。

风险与影响

回归风险低:RVV 内核仅在 __riscv_v 编译时生效,不影响 x86/ARM 等其他架构。数值匹配验证(max|rvv-vec| = 0)覆盖常见 shape,反量化路径复用现有代码。
性能风险:当前 tile shape 针对 VLEN=128 调优,更长 VLEN 的 CPU 可能未达最佳,但 PR 在 follow-up 中已提及未来可调。
测试覆盖:PR 缺少与 CI 集成的自动化测试,未来重构可能有退化风险。建议在 RISC-V CI 节点上添加 tests/quantization/test_cpu_wna16.py 的回归测试。

对用户:RISC-V CPU 上运行 W4A16 GPTQ/AWQ 模型的用户将获得 2.4-3.2x GEMM 加速,推理延迟明显降低。对其他用户无影响。对系统:新增约 230 行 C++ 内核代码,无外部依赖。对团队:需维护 RVV 内核,但 kernel 为模板化实现,与现有架构一致。

仅 RISC-V 路径变更 缺少 CI 回归测试 tile shape 针对 VLEN=128 优化

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论