为何稀疏矩阵与全矩阵相加比全矩阵间相加更慢?
首先来看你提供的测试代码片段:
I_FULL = 600; J_FULL = 10000; FULL_COUNT = I_FULL*J_FULL; NON_ZERO_ELEMENT_COUNT = 1000; nonZeroIdxs = randsample(FULL_COUNT, NON_ZERO_ELEMENT_COUNT); mat_Sp = spalloc(I_FULL, J_FULL, NON_ZERO_ELEMENT_COUNT); mat_Sp(nonZeroIdxs) = 0.5; mat_Full = full(mat_Sp); otherMat_Full = rand(I_FULL, J_FULL); hFullAddSp = @()otherMat_Full+mat_Sp; hFullAddFull = @()otherMat_Full+mat_Full;
你观察到hFullAddFull的性能始终更优,核心原因在于稀疏矩阵的存储特性和加法运算的底层实现逻辑,具体可以拆解为以下几点:
存储结构带来的遍历与缓存差异
全矩阵是连续的内存块存储,CPU可以按顺序批量读取数据,缓存命中率极高,还能充分利用SIMD(单指令多数据)指令集一次性处理多个元素,效率拉满。而稀疏矩阵仅存储非零元素的位置和值,这些数据在内存中是离散分布的。当稀疏矩阵和全矩阵相加时,程序需要逐个遍历稀疏矩阵的非零元素,定位到全矩阵的对应位置执行加法,剩下的全矩阵元素还要原样保留——这种“零散更新+批量保留”的操作,远不如两个全矩阵相加的“连续批量运算”高效。底层运算库的优化差异
两个全矩阵相加的操作,Matlab会直接调用高度优化的BLAS(基础线性代数子程序)库(比如MKL或OpenBLAS)中的矩阵加法函数。这些函数经过多年打磨,充分利用了CPU的多核、缓存层级和指令集,是极致优化的批量运算实现。而稀疏矩阵+全矩阵相加的操作,没有对应的高效BLAS接口,Matlab需要自行实现逻辑:先复制全矩阵作为结果容器,再逐个将稀疏矩阵的非零元素叠加到对应位置。这个过程不仅遍历稀疏元素的开销大,还无法利用SIMD做大规模加速,缓存命中率也低。你的测试场景放大了差异
你的测试中非零元素仅1000个,相对于600×10000=600万的全矩阵来说占比极低。此时稀疏+全矩阵相加的逻辑是:先复制600万元素的全矩阵,再做1000次零散的加法更新;而两个全矩阵相加是直接对两个600万元素的连续内存块做逐元素加法,后者的连续批量操作效率远高于前者的“复制+零散更新”组合。
简单来说,这就像你要给一本书的1000页加批注,和把两本600万页的书逐页内容相加——前者需要翻找指定页码,后者可以批量快速处理,效率差异自然非常明显。
内容的提问来源于stack exchange,提问作者rnoodle

