基于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指令集,推荐两种高效实现思路:
动态生成掩码(无预处理开销)
- 用
viota.v生成覆盖所有非零元素的连续索引向量(对应C代码中的j值)。 - 将当前行的
rowPtr[i]和rowPtr[i+1]分别广播到向量寄存器vstart和vend。 - 用
vmslt.vx判断元素索引是否≥rowPtr[i],再用另一条vmslt.vx判断是否<rowPtr[i+1],最后通过vmand.mm合并两个掩码,得到当前行的有效元素掩码。
这种方法无需预处理,适合行长度波动大的稀疏矩阵。
- 用
预处理生成行归属向量(适合重复执行场景)
如果内核会被多次调用,可预先生成每个非零元素的行号向量row_id:- 加载
rowPtr到向量寄存器,用vslideup.vi或vslidedown.vi计算相邻元素差值,得到每行的非零元素个数。 - 用
vrepeat.vv根据行元素个数广播对应行号,拼接成完整的row_id向量。
后续处理每行时,只需广播当前行号,用vmseq.vx对比row_id即可直接得到该行的有效掩码,把开销分摊到预处理阶段,单次计算性能更高。
- 加载
针对旧版索引数组生成掩码的补充
如果是直接从索引数组生成目标掩码,可按以下步骤优化:
- 初始化全0的向量掩码寄存器。
- 将索引数组加载到向量寄存器
vidx,用viota.v生成目标掩码长度的索引向量velem_idx。 - 对
vidx做广播扩展后,用vmseq.vv对比velem_idx得到每个索引的匹配位,再用vmor.mm累加到掩码寄存器中。注意处理向量长度对齐问题,可通过分段完成。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

