Prhub

#47538 [Performance][Hardware][RISC-V] Reduce LMUL pressure in INT4 LUT dequant

原始 PR 作者 I3eg1nner 合并时间 2026-07-06 13:58 文件变更 1 提交数 1 评论 3 代码增减 +23 / -10

执行摘要

RISC-V INT4 LUT 反量化 LMUL 压力降低 75%

RISC-V 向量扩展中,LMUL 寄存器压力随元素宽度增大:原 u64@LMUL_1024 在 VLEN=128 上占用全部 32 个向量寄存器(m8),在 VLEN=256 上占用 16 个(m4),导致大量溢出;改用 u32@LMUL_256 后每变量仅用 4 个寄存器(m1 或 m2),压力降低 75%。

该 PR 值得精读,展示了 RISC-V SIMD 优化中的 LMUL 寄存器压力权衡,是向量化编程中元素宽度选择的重要案例。

讨论亮点

无 review 技术讨论。PR 由 bigPYJ1151 直接批准,仅有 bot 自动评论和一条 CI 相关评论(非本 PR 问题)。

实现拆解

  1. 拆分 64 位值为两个 32 位半字:将 int64_t value 转换为 uint32_t lo(低 32 位)和 uint32_t hi(高 32 位)。
  2. 分别提取低/高半字的 nibble 索引:对每个半字使用 u32@LMUL_256 向量化操作,生成 8 个 4-bit 索引。
  3. 合并索引并查表:用 vcreate 将两个 vuint32m1_t 合并为 vuint32m2_t,最终通过 vrgather 从 LUT 中取得 16 个 float32 值。
  4. 测试配套:仅修改 csrc/cpu/cpu_types_riscv_impl.hpp 文件,新增正确性校验(与标量参考对比)和性能基准(百万次迭代计时)。
文件 模块 状态 重要度
csrc/cpu/cpu_types_riscv_impl.hpp CPU 内核 modified 6.14

关键符号

FP32Vec16::FP32Vec16

关键源码片段

csrc/cpu/cpu_types_riscv_impl.hpp core-logic

核心逻辑变更:FP32Vec16 构造函数中 INT4 LUT 反量化路径的 LMUL 优化。

// FP32Vec16(int64_t, const FP32Vec16&) — INT4 LUT 反量化构造函数
// 原始实现使用 u64 @ LMUL_1024,在 VLEN=128 上注册压力高达 m8(所有 32 个向量寄存器)
// 优化后:先将 64 位值拆为两个 32 位半字,各用 u32 @ LMUL_256 处理,
// 最终通过 vcreate 合并为 u32 @ LMUL_512 用于 vrgather。寄存器压力降至 m1/m2。
explicit FP32Vec16(int64_t value, const FP32Vec16& lut) {
  constexpr int HALF = VEC_ELEM_NUM / 2; // HALF = 8
  const auto q = static_cast<uint64_t>(value);
  const uint32_t lo = static_cast<uint32_t>(q);
  const uint32_t hi = static_cast<uint32_t>(q >> 32);  // 生成 0..7 的 lane ID(u32 @ LMUL_256)
  auto lane_ids = RVVI(__riscv_vid_v_u32, LMUL_256)(HALF);
  // 每个 nibble 偏移 4 bits,故 left shift by 2(乘 4)
  auto shifts = RVVI(__riscv_vsll_vx_u32, LMUL_256)(lane_ids, 2, HALF);  // 提取低 8 个 nibble 索引
  auto packed_lo = RVVI(__riscv_vmv_v_x_u32, LMUL_256)(lo, HALF);
  auto idx_lo = RVVI(__riscv_vand_vx_u32, LMUL_256)(
      RVVI(__riscv_vsrl_vv_u32, LMUL_256)(packed_lo, shifts, HALF),
      0xF, HALF);  // 提取高 8 个 nibble 索引
  auto packed_hi = RVVI(__riscv_vmv_v_x_u32, LMUL_256)(hi, HALF);
  auto idx_hi = RVVI(__riscv_vand_vx_u32, LMUL_256)(
      RVVI(__riscv_vsrl_vv_u32, LMUL_256)(packed_hi, shifts, HALF),
      0xF, HALF);  // 合并为 16 个 u32 索引(LMUL_256 -> LMUL_512),用于 vrgather
  auto idx = RVVI4(__riscv_vcreate_v_u32, LMUL_256, _u32,
                   LMUL_512)(idx_lo, idx_hi);
  reg = RVVI(__riscv_vrgather_vv_f32, LMUL_512)(lut.reg, idx, VEC_ELEM_NUM);
}

评论区精华

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

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

风险与影响

变更仅影响 RISC-V 平台下的 INT4 LUT 反量化构造函数,不涉及其他硬件或逻辑路径。测试验证了正确性和性能,回归风险低。

对使用 RISC-V 平台的 INT4 量化模型有显著性能提升(约 2x),对其他平台无影响。

仅 RISC-V 平台影响 无测试文件跟随变更 单一文件修改

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论