为何Scipy稀疏矩阵转数组后np.matmul运算速度大幅降低?
稀疏矩阵转数组后导致np.matmul运算变慢的原因解析
问题背景
为优化Python代码并节省内存,尝试使用Scipy稀疏矩阵,却导致代码运行变慢。排查后发现,Scipy稀疏矩阵转数组的操作会拖慢np.matmul的计算速度。
复现代码
import numpy as np import scipy import time space_size = 50 delta = 1 S = 20 Nb_seed = np.arange(200) Distance_effect = np.random.uniform(0,1,(space_size**2, space_size**2)) Distance_effect_ = scipy.sparse.csr_matrix(Distance_effect) loc = np.random.choice(a = space_size**2, size = S * max(Nb_seed), replace = True).reshape((max(Nb_seed), S)) dist_effect_ = Distance_effect_[:, loc.reshape(max(Nb_seed) * S,)].toarray() dist_effect_ = dist_effect_.reshape((space_size**2, max(Nb_seed), S)) dist_effect_ = np.transpose(dist_effect_,(2,0,1)) dist_effect = Distance_effect[:, loc] dist_effect = np.transpose(dist_effect,(2,0,1)) a = np.random.uniform(0,1,(S , S,space_size**2)) start_time = time.time() for i in range(10): e = np.sum(np.matmul(a, dist_effect_), axis = 1).T print("--- %s seconds ---" % (time.time() - start_time)) start_time = time.time() for i in range(10): c = np.sum(np.matmul(a, dist_effect), axis = 1).T print("--- %s seconds ---" % (time.time() - start_time))
运行耗时
- 稀疏矩阵转数组后运算耗时:
--- 12.80036735534668 seconds --- - 直接使用numpy数组运算耗时:
--- 0.05399775505065918 seconds ---
原因解析
1. 内存布局的本质差异
- 原生
numpy数组切片得到的dist_effect是连续内存块,np.matmul可以充分利用CPU的向量化指令(如AVX、SSE)和缓存机制,内存访问效率极高,计算速度快。 - 稀疏矩阵调用
.toarray()生成的dist_effect_,内存布局是非连续的。因为稀疏矩阵本身是按非零元素的索引存储,转成稠密数组时无法保证内存的连续性,np.matmul计算时需要频繁跳转内存地址,缓存命中率极低,直接拉低计算效率。
2. 无意义的稀疏转换开销
你的Distance_effect是全随机稠密矩阵(没有大量零元素),完全不适合用稀疏矩阵存储。将稠密矩阵转成稀疏矩阵再转回稠密数组,不仅没节省内存,还额外增加了转换的计算成本,同时破坏了原数组的连续内存结构。
3. 维度变换加剧内存碎片化
对dist_effect_进行reshape和transpose后,进一步加剧了内存不连续的问题。numpy对非连续数组做转置时不会主动复制内存来保证连续性,后续matmul只能在碎片化内存上操作,性能自然远不如原生连续数组。
优化建议
- 若原始矩阵是稠密矩阵(无大量零元素),直接使用
numpy数组即可,无需引入稀疏矩阵,既省内存又保证计算效率。 - 若确实需要处理稀疏场景,避免将稀疏矩阵转成稠密数组计算,应直接使用Scipy稀疏矩阵的内置运算接口(如
scipy.sparse.linalg中的矩阵乘法方法),利用稀疏存储特性减少计算量。
内容的提问来源于stack exchange,提问作者Silnae
相关产品推荐
相关产品推荐

