You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于RISC-V RVV 0.7.1的CSR SpMV行掩码构建方法问询

基于RISC-V RVV 0.7.1构建CSR SpMV内核的掩码生成方案

重构后的问题

我正在基于RISC-V RVV 0.7.1实现CSR格式的优化SpMV内核。SpMV的C语言实现如下:

for (int i = 0; i < numRows; i++)
    for (int j = rowPtr[i]; j < rowPtr[i+1]; j++)
       y[i] += val[j] * x[colIdx[j]];

初始向量化步骤较为简单:加载colIdx,以此对x进行索引加载,再加载val并将二者相乘。之后需对每行执行归约求和(v{f}redsum.vs),将行内所有乘积相加后存入y的对应位置。当前的挑战是基于rowPtr创建掩码,确保仅目标行内的元素在归约时处于激活状态。

我的初步思路是利用rowPtr中的索引在向量寄存器对应位置设置1,再通过viota和元素移位生成记录每个元素所属行的寄存器,但该方案不可行。请问应如何构建所需掩码?


旧版问题

我尝试使用RISC-V向量扩展(0.7.1)基于索引数组创建掩码寄存器。例如,给定如下idx array索引数组,目标是利用其中的索引在掩码对应位置设置1,如下方mask所示:

idx array | 0 2 3 4 4 6 8
elem idx  | 0 1 2 3 4 5 6 7 8
mask      | 1 0 1 1 1 0 1 0 1

起初我错误地关注vrgather.vx指令,但它并非我所需,因为它会填充所有元素。我希望代码具备较高性能,因此理想情况下应尽量使用向量指令。恳请指点方向。


解决方案

针对CSR SpMV行掩码的生成方案

结合RVV 0.7.1指令集,推荐两种高效实现思路:

  • 动态生成掩码(无预处理开销)

    1. 用viota.v生成覆盖所有非零元素的连续索引向量(对应C代码中的j值)。
    2. 将当前行的rowPtr[i]和rowPtr[i+1]分别广播到向量寄存器vstart和vend。
    3. 用vmslt.vx判断元素索引是否≥rowPtr[i],再用另一条vmslt.vx判断是否<rowPtr[i+1],最后通过vmand.mm合并两个掩码,得到当前行的有效元素掩码。
      这种方法无需预处理,适合行长度波动大的稀疏矩阵。
  • 预处理生成行归属向量(适合重复执行场景)
    如果内核会被多次调用,可预先生成每个非零元素的行号向量row_id:

    1. 加载rowPtr到向量寄存器,用vslideup.vi或vslidedown.vi计算相邻元素差值,得到每行的非零元素个数。
    2. 用vrepeat.vv根据行元素个数广播对应行号,拼接成完整的row_id向量。
      后续处理每行时,只需广播当前行号,用vmseq.vx对比row_id即可直接得到该行的有效掩码,把开销分摊到预处理阶段,单次计算性能更高。

针对旧版索引数组生成掩码的补充

如果是直接从索引数组生成目标掩码,可按以下步骤优化:

  1. 初始化全0的向量掩码寄存器。
  2. 将索引数组加载到向量寄存器vidx,用viota.v生成目标掩码长度的索引向量velem_idx。
  3. 对vidx做广播扩展后,用vmseq.vv对比velem_idx得到每个索引的匹配位,再用vmor.mm累加到掩码寄存器中。注意处理向量长度对齐问题,可通过分段完成。

内容的提问来源于stack exchange,提问作者Alex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 01:27:41