我有一些scipy稀疏矩阵(目前在CSR格式中),需要与密集的numpy 1D向量相乘。
这个向量叫做G
:
print G.shape, G.dtype
(2097152,) complex64
每个稀疏矩阵的形状为
(16384,2097152)
,非常稀疏。密度约为4.0e-6。
我有一个名为spmats
的100个这样的稀疏矩阵列表。我可以像这样轻松地将每个矩阵与
G
相乘:res = [spmat.dot(G) for spmat in spmats]
这将得到一个期望形状为
(16384,)
的密集向量列表。我的应用程序相当注重性能,因此我尝试了一种替代方法,即首先将所有稀疏矩阵连接成一个大的稀疏矩阵,然后只使用一次
dot()
调用,如下所示:import scipy.sparse as sp
SPMAT = sp.vstack(spmats, format='csr')
RES = SPMAT.dot(G)
这将导致一个名为
RES
的长向量,形状为(1638400,)
,是上述所有结果向量res
的串联版本,如预期所示。我已经检查过结果是相同的。也许我完全错了,但我希望第二种情况比第一种情况更快,因为numpy调用、内存分配、python对象创建、python循环等都要少得多。我不关心连接稀疏矩阵所需的时间,只关心计算结果所需的时间。然而,根据
%timeit
的结果:%timeit res = [spmat.dot(G) for spmat in spmats]
10 loops, best of 3: 91.5 ms per loop
%timeit RES = SPMAT.dot(G)
1 loops, best of 3: 389 ms per loop
我已经检查过,无论是在任何操作中,我都没有内存不足的情况,并且似乎没有任何可疑的事情发生。这是真的奇怪吗?这是否意味着所有的稀疏矩阵向量积都应该分块进行,每次只处理几行,以使它们更快?
就我所知,稀疏矩阵与密集向量相乘的时间应该是非零元素个数的线性函数,在上述两种情况下未发生改变。是什么导致了这样的差异?
我正在单核Linux机器上运行,拥有4GB的RAM,使用EPD7.3。
编辑:
以下是一个可以重现问题的小例子。
import scipy.sparse as sp
import numpy as n
G = n.random.rand(128**3) + 1.0j*n.random.rand(128**3)
spmats = [sp.rand (128**2, 128**3, density = 4e-6, format = 'csr', dtype=float64) for i in range(100)]
SPMAT = sp.vstack(spmats, format='csr')
%timeit res = [spmat.dot(G) for spmat in spmats]
%timeit RES = SPMAT.dot(G)
我得到:
1 loops, best of 3: 704 ms per loop
1 loops, best of 3: 1.34 s per loop
在这种情况下,性能差异并不像我的稀疏矩阵那样大,因为它们具有一定的结构(可能是因为缓存),但是将矩阵连接起来仍然更慢。我已经尝试过scipy 10.1和12.0。
G
具有dtype = np.complex64
时,列表推导式要慢5倍(!),而当G
具有dtype = np.complex128
时,两种方法的速度都相同。 - jorgeca