在 RISC-V RVV 0.7.1 中屏蔽 CSR SpMV 的单个行

Masking individual rows for CSR SpMV in RISC-V RVV 0.7.1

提问人:Alex 提问时间:11/7/2023 最后编辑:Alex 更新时间:11/9/2023 访问量:92

问:

编辑:我已经将我的问题重新表述为更有成效的东西,并将在下面提供答案。这个问题的旧版本仍然在下面。

我正在RVV 0.7.1中为CSR格式实现优化的SpMV内核。在 C 语言中,SpMV 可以这样实现:

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

矢量化最初很简单,通过加载 ,使用它来执行 的索引加载,加载和乘以两者。在此之后,必须在每一行中执行约简和 (),将其中的所有乘积相加并将它们存储在 的相应索引中。挑战在于从 创建掩码,以便只有属于给定行的元素在每次缩减时处于活动状态。coldIdxxvalv{f}redsum.vsyrowPtr

我最初的想法(应该提供有关原始问题的上下文)是使用 中的索引将 1 放在向量寄存器的相应元素中,然后使用 和 元素移位创建一个包含每个元素所属行的寄存器。然而,这显然是不可行的。rowPtrviota

如何构建所需的蒙版?


老问题

我正在尝试使用 RISC-V 矢量扩展 (0.7.1) 来创建基于索引数组的掩码寄存器。例如,采用以下索引数组 idx array。目标是使用其中存在的索引将 1 放入相应的掩码元素中,如下面的掩码所示:

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 指令,但它并没有完全按照我想要的方式进行,因为它填充了所有元素。我希望代码也非常高性能,所以如果可能的话,它主要由向量指令组成,那将是理想的。如果有人能为我指出正确的方向,我将不胜感激。

矢量化 稀疏 矩阵乘法 riscv

评论

1赞 camel-cdr 11/7/2023
我认为这是不合理的。您能否分享有关您想要矢量化的更多细节,也许还有另一个角度可以解决这个问题?
0赞 Alex 11/7/2023
@camel-cdr 总体目标是以 CSR 格式对 SpMV 进行矢量化。我需要屏蔽每行的元素以获得减少总和 ()。这个想法是在创建掩码寄存器后使用,他们使用与递增索引的比较来创建最终的掩码。redsumviota
0赞 camel-cdr 11/7/2023
我必须承认我以前没有写过 SpMV。但是从上面看,你说的那部分就是这个吗? 难道你不需要屏蔽连续的元素吗?如果这完全是基础,我可能帮不上什么忙。for i from rOff[row] to rOff[row+1]: sum += vals[i] * x[cols[i]];rOff[row] to rOff[row+1]
1赞 camel-cdr 11/7/2023
这应该很容易,假设你想将元素遮罩到包容性,那么你可以用以下命令创建这样的遮罩: . 设置在第一个位之前,并且设置包括第一个位。但是你为什么不直接开始阅读 from 和 setvl to 呢?顺便说一句,我建议在循环完成后减少,因为这通常更快。这是累积到向量寄存器的所有元素中,尾巴不受干扰,然后减少一次。begin=rOff[row]end=rOff[row+1]vmxor(vmsbf(vmseq(vid,begin)), vmsif(vmseq(vid,end)))vmsbfvmsifbeginend - begin
1赞 Alex 11/7/2023
小校正,您希望遮罩元素,但不包括结束元素(属于下一行)。我相信这是一个简单的解决方法,您只需同时使用两者而不是vmsbfvmsif

答:

1赞 Alex 11/8/2023 #1

解决方案的功劳归功于骆驼-cdr!

采用以下数组:rowPtr

0 2 3 5 7

让我们考虑第三次迭代 (),它应该更具说明性。目标是实现以下掩码,启用元素 3 和 4:[3,5[

00011000

第一步是将每个元素的索引写入向量。这样,我们可以使用两次(如果相等,则设置掩码),带有元素和 of,这将产生两个寄存器,其中一个在元素中或:vidvmseqii+1rowPtrrowPtr[i]rowPtr[i+1]

idx | 01234567
i   | 00010000
i+1 | 00000100

然后,我们可以在两者上使用 (set-before-first),它将所有位设置在第一个活动掩码位之前。通过对两者进行异或运算,我们得到最终所需的掩码:vmsbf.m

sbf i   | 11100000
sbf i+1 | 11111000
xor     | 00011000

通过遍历和重复此操作,可以为每一行的元素生成一个掩码。简而言之,我们可以将此操作编写为:rowPtr

vmxor(vmsbf(vmseq(vid,rowPtr[i])), vmsbf(vmseq(vid,rowPtr[i+1])))