【问题标题】:Fast accessing elements of Compressed Sparse Row (CSR) sparse matrix压缩稀疏行 (CSR) 稀疏矩阵的快速访问元素
【发布时间】:2023-03-25 01:01:01
【问题描述】:

我想测试一些较新的稀疏线性求解器,我想知道是否有一种快速填充矩阵的方法。我感兴趣的格式是 CSR (http://goo.gl/hLXYd)。假设 CSR 格式的矩阵由下式给出:

values(num non-zero elements)
columns(num non-zero elements)
rowIndex(num rows + 1)

所考虑的稀疏矩阵源自网络。所以,我有数千个节点,其中一些节点通过线连接。因此,矩阵在结构上是对称的。每个连接 (i,j) 向对角项 (i,i) 和 (j,j) 以及非对角项 (i,j) 和 (j,i) 添加一些内容。我可以在相同的节点 (i,j,1)、(i,j,2) 之间有多个连接...所以,我可能需要多次重新访问 2 个对角线和 2 个非对角线元素。

我知道我可以通过执行 rowIndex(i) 来获得行的开头。然后,我必须遍历元素 columns(rowIndex(i):rowIndex(i+1)-1) 以找到 j 所在的位置。

问题:

有没有一种方法可以更快地访问元素,同时采用 CSR 格式,而无需每次我想更新元素时​​都进行搜索?

一些说明: 我只需要从头开始填写矩阵。矩阵在结构上是对称的,并不是真正对称的。保存的值与网络数据(阻抗、电阻等)有关,它们具有真实值。一般来说,价值(i,j)价值(j,i)。我有 (name1,i1,j1,value1), (name2,i2,j2,value2) 等形式的元组。这些元组没有排序,2 个元组可以引用相同的 i,j 值,这意味着它们需要被添加

提前致谢!

【问题讨论】:

    标签: graph fortran sparse-matrix circuit pardiso


    【解决方案1】:

    你所拥有的是所谓的三元组稀疏格式。 CRS的创建,包括删除重复条目和对值求和,可以非常有效地实现。在自己编程之前,请查看SuiteSparse library。它是用 C 编写的,但我相信你会理解其中的原理。您感兴趣的是cholmod_triplet.c 文件,它实现了您需要的功能。

    本质上,转换是在行和列索引上使用两阶段桶排序执行的。该算法具有线性复杂度,如果您对处理大型数据集感兴趣,这一点很重要。

    编辑如果您想一起跳过显式创建三元组格式,您可以通过动态生成(row, col) 连接并将它们添加到动态稀疏结构中来实现。我通常使用插入排序和排序列表来做到这一点,这实际上是最快的。它也比三元组到 CRS 的转换更快,并且使用更少的内存。方法如下:

    • 如果您大致知道,每行中有多少个非零条目,您为每一行预先分配一个(空)列索引数组,以及一个单独的值数组(不是链表,而是那个大小的简单数组)。类似的东西

      static_lists_cols[row] = malloc(sizeof(int)*expected_number_of_non_zeros) static_lists_vals[row] = malloc(sizeof(double)*expected_number_of_non_zeros)

    • 如果您不知道这一点,您可以选择一个初始大小并在行列表已满时根据需要重新分配(使用一些足够大的块大小以避免重新分配开销)。

    • 对于每个(row, col) 对,您使用插入排序将col 插入到与row 对应的排序列表中。对于每行小的(最多几百个)非零,线性搜索是最快的。对于每行更多的非零,您可以使用二分法找到插入col 索引的正确位置。
    • col 被插入到rowth 排序列表中,方法是移动排序列表中具有较高列索引的非零条目。这是缓存友好的,因为实际上这些行小到可以放入现在的任何缓存中。
    • 完成后,您需要将各个行列表复制到最终的columns 中,从而将各个排序列表组装成一个有效的 CRS 结构。值也是如此。
    • 如果您认为某些行可以有零条目,您实际上可以通过预先分配静态“列表数组”来避免最后一步。因此,每行的条目数是恒定的,其中一些可能为零。有时这没关系。

    这种方法比使用三元组到稀疏转换要快,至少对于我使用它的 FEM 模型来说是这样。一般的原因是内存带宽是这里的瓶颈,而上面的方案使用的内存要少得多:

    • 创建三元组格式需要时间,您需要将三元组写入内存
    • 转换为 CRS 需要至少读取和写入三元组一次才能对它们进行排序(实际上不止一次,如果您查看算法的话。您排序两次,并且需要辅助数据结构。)
    • 根据连接结构,您最终可能会拥有大量三元组格式的(row, col) 重复项,这些重复项在组装过程中会通过添加相应的值来删除。上述方法中不存在此开销 - 如果行列表中已存在 col,则只需更新相应的值即可。
    • 如果您将行范围分配给各个工作人员,则可以并行更新排序列表。不需要通信,也不需要同步。确保负载平衡是另一回事...

    查看a performance comparison of using those two methods (Figure 1) 以了解 2D 中的三角形元素。请注意,性能差异取决于 三元组中的条目数与组装稀疏矩阵格式的比率(表 2)。但总的来说,该方法永远不会比三元组到 crs 的转换差,首先需要创建三元组。您还可以下载 MATLAB MEX 函数 sparse_create,它是 mutils 包的一部分(请参阅下载部分)。

    【讨论】:

    • 是的,想法就是这样。但我的问题是,是否有办法跳过以 COO 形式保存,然后转换为 CSR。如果我已经以 COO 形式(行、列、值)制作所有内容,那不是额外的步骤吗?这是绝对必要的步骤吗?
    • @p3tris 哦,当然没有必要。这只是一种方法。我在 FEM 上下文中经常使用的另一种方法是从网格连接动态创建 (row, col) 索引,并将矩阵条目添加到动态稀疏结构中。我通常使用插入排序和排序列表来做到这一点,这实际上是最快的。它也比三元组到 crs 的转换更快,并且使用 更少的内存。有关类似主题的其他答案,请参阅 here
    • 我想我已经接近了:) 那么,你提到的动态稀疏结构是一种带有链表的链表吗?首先,我创建一个包含行的链接列表。然后该列表的每个元素都指向一个包含列和值的列表,并且所有列表都使用插入排序进行排序?当我得到这个动态结构时,我将不得不对其进行处理以获取 csr 矩阵。你能给我更多的解释吗?为什么它会比构建 COO 矩阵更快?谢谢
    【解决方案2】:

    您的问题似乎混淆了 2 个截然不同的问题:

    1. 以 CSR 形式创建矩阵的快速方法是什么?
    2. 是否有更快的方法从已以 CSR 形式存储的矩阵中读取值? (比您描述的直接方法更快)

    所以这里有 2 个答案:

    1. 一般而言,将网络数据从任何形式读取到类似dictionary of keys 的形式(其他中间形式可用,并且可能出于速度或其他原因对您更有吸引力);然后将该中间结构转换为矩阵的 CSR 形式。详情请参阅下文。
    2. 我不相信,不是以 CSR 形式存储的矩阵。这种相对较慢的访问速度是您为节省空间付出的代价的一部分。根据您的观点,您以时间换空间,或以空间换时间。

    您对输入数据的描述表明您应该考虑设计自己的中间形式来编组原始数据。由于您的邻接矩阵是对称的,您只需要以任何形式存储它的一半。此外,您可能不需要沿主对角线存储元素——我猜要么节点 i 始终连接到节点 i,要么永远不会这样网络的性质决定了存储在(i,i)。我有点不确定您要在矩阵的每个节点处存储的信息,是ij 之间的连接数还是其他?

    【讨论】:

    • 很抱歉造成混乱,我不需要 (2)。我只需要从头开始填写矩阵。矩阵在结构上是对称的,并不是真正对称的。保存的值与网络数据(阻抗、电阻等)有关,它们是真实值。一般来说,价值(i,j)价值(j,i)。我有 (name1,i1,j1,value1), (name2,i2,j2,value2) 等形式的元组。这些元组没有排序,2 个元组可以引用相同的 i,j 值,这意味着它们需要被添加。我希望我没有让你更加困惑。
    • 我经常在这里感到困惑,不要担心,但请编辑您的问题,以便下一个徘徊过去的人更清楚地了解您想要什么。
    猜你喜欢
    • 2015-12-05
    • 2017-12-05
    • 1970-01-01
    • 2021-08-03
    • 2021-12-30
    • 1970-01-01
    • 2013-02-13
    • 2023-03-03
    • 2019-03-12
    相关资源
    最近更新 更多