我经常看到与稀疏矩阵运算相关的术语“低算术强度”和“内存受限”。但是,我的直觉是稀疏矩阵运算应该更少的内存限制,如果有的话。毕竟,对于一个高度稀疏的矩阵,您只需要传输密集矩阵运算所需的一小部分数据(即,只需存储和传输矩阵的非零、实际有意义的条目)。
所以我的问题是:为什么稀疏矩阵运算通常被认为是受内存限制的?
我想必须存在某种程度的“密集度”,在这种情况下,稀疏矩阵计算变得受计算限制。
我经常看到与稀疏矩阵运算相关的术语“低算术强度”和“内存受限”。但是,我的直觉是稀疏矩阵运算应该更少的内存限制,如果有的话。毕竟,对于一个高度稀疏的矩阵,您只需要传输密集矩阵运算所需的一小部分数据(即,只需存储和传输矩阵的非零、实际有意义的条目)。
所以我的问题是:为什么稀疏矩阵运算通常被认为是受内存限制的?
我想必须存在某种程度的“密集度”,在这种情况下,稀疏矩阵计算变得受计算限制。
BLAS1 操作、BLAS2 操作和稀疏操作具有相同的低算术强度诅咒,它们执行每次内存读取都会失败(与 BLAS3 操作类似gemm
,它执行翻牌读取并且只会变得越来越多的算术密集/计算密集型)。
然而,由于间接寻址,稀疏的 matvecs 更加复杂。这增加了(至少)额外的内存压力来加载整数索引。更糟糕的是,它会导致缓存利用率低得多,因为缓存通常会在任何一次读取附近获取大量数据,但稀疏 matvec 通常只会使用一个值。(BLAS1/BLAS2 运算的算术强度较低,但至少对缓存友好)。
有一些稀疏矩阵格式试图减轻这种缓存不友好性,通常它们就像传统格式(CSR/CSC/等)的阻塞变体,其中矩阵的每个“非零”都是一个小的密集矩阵。这使您可以减少一些间接寻址并改善访问的局部性。它们非常适合高阶 FEM,其中许多自由度通常配置在相同的几何实体上并共享相同的图案。但是它们不适合低阶方法(这样的矩阵中没有足够的局部相似性来有效地“建立”一个大块)。
综上所述,“低算术强度”的结果是“低算术”.. 稀疏利用方法通常很快,即使它们在硬件级别上并不是特别有效。他们只是天生就在做“更少的工作”……从用户的角度来看,这不一定是件坏事。
这实际上取决于您在问题中包含的操作。如果您采用任何 1 级 BLAS 或 2 级 BLAS 算法的稀疏等效项,那么是的,它们是内存限制的(不是计算限制的),但对于密集矩阵来说,1 级 BLAS 和 2 级 BLAS 也是如此。要回答这个问题,我们需要了解是什么让算法计算受限。
计算绑定算法是那些其性能在很大程度上依赖于底层系统执行算术的能力的算法,通常以 FLOPS 来衡量。
对于具有此属性的算法,它必须执行比内存操作更多的算术运算,因为内存操作具有非常高的延迟。在线性代数算法的上下文中,这意味着在算法过程中必须有大量的数据重用。数据重用意味着我们可以避免诉诸主存。3 级 BLAS 操作通常具有此属性。
例如,要进行稀疏矩阵向量乘积,您只需扫描其非零列表一次。无法以任何有助于提高性能的方式缓存任何值。就此而言,稠密矩阵向量积也是如此。这些不是计算绑定算法的示例。
不幸的是,当今的大多数计算架构都非常擅长算术,并且仅在内存性能方面表现不错。因此,许多稀疏线性代数算法专门设计用于在其最苛刻的点从第 3 级 BLAS 减少到密集线性代数算法。多面分解就是这样的算法的一个例子。
所以给你的总体答案是:稀疏线性代数是许多操作的内存限制,但密集线性代数也是如此。然而,我们可以选择/设计我们的算法来匹配手头系统的能力,所以这不一定是一件坏事。