这是我刚遇到的事情。出于某种原因,在犰狳中乘以密集的稀疏矩阵要比稀疏和密集矩阵的乘法慢得多(即,颠倒顺序)。
RcppArmadillo.h
我正在使用RcppArmadillo软件包将Arma与R接口; armadillo
包括set.seed(98765)
# 10000 x 10000 sparse matrices, 99% sparse
a <- rsparsematrix(1e4, 1e4, 0.01, rand.x=function(n) rpois(n, 1) + 1)
b <- rsparsematrix(1e4, 1e4, 0.01, rand.x=function(n) rpois(n, 1) + 1)
# dense copies
a_den <- as.matrix(a)
b_den <- as.matrix(b)
system.time(mult_sp_den_to_sp(a, b_den))
# user system elapsed
# 508.66 0.79 509.95
system.time(mult_den_sp_to_sp(a_den, b))
# user system elapsed
# 13.52 0.74 14.29
。这是R中的一些时间,在几个相当大的垫子上:
// [[Rcpp::export]]
arma::sp_mat mult_sp_den_to_sp2(arma::sp_mat& a, arma::mat& b)
{
// sparse x dense -> sparse
// copy dense to sparse, then multiply
arma::sp_mat temp(b);
arma::sp_mat result(a * temp);
return result;
}
所以第一次乘法比第二次乘以大约35倍(所有时间都以秒为单位)。
有趣的是,如果我只是制作密集矩阵的临时稀疏副本,性能会大大提高:
system.time(mult_sp_den_to_sp2(a, b_den))
# user system elapsed
# 5.45 0.41 5.86
this.apiSerivce.userlist({'offset':'0','limit':'10'})
.subscribe( resultObj => {
console.log(resultObj)
this.userlist = resultObj.response
//or
//this.userlist = resultObj['response']
this.status = resultObj.status
})
这是预期的行为吗?我知道,对于稀疏矩阵,您执行操作的确切方式会对代码的效率产生很大影响,远远超过密集。然而,35倍的速度差异似乎相当大。
答案 0 :(得分:2)
稀疏和密集矩阵以非常不同的方式存储。 Armadillo使用CMS(列主存储)用于密集矩阵,而CSC(压缩稀疏列)用于稀疏矩阵。来自Armadillo的文档:
<强>垫强>
的垫强>
的 cx_mat 强>
密集矩阵的类,其中元素以列主要顺序存储(即逐列)<强> SpMat
sp_mat
sp_cx_mat 强>
稀疏矩阵的类,其中元素以压缩稀疏列(CSC)格式存储
我们必须首先了解每种格式需要多少存储空间:
给定数量element_size
(单精度为4个字节,双精度为8个字节),index_size
(如果使用32位整数则为4个字节,如果使用64位整数则为8个字节) ,num_rows
(矩阵的行数),num_cols
(矩阵的列数)和num_nnz
(矩阵的非零元素数),以下公式给我们每种格式的存储空间:
storage_cms = num_rows * num_cols * element_size
storage_csc = num_nnz * element_size + num_nnz * index_size + num_cols * index_size
有关存储格式的详细信息,请参阅wikipedia或netlib。
假设双精度和32位indeces,在你的情况下意味着:
storage_cms = 800MB
storage_csc = 12.04MB
因此,当您将稀疏x密集(或密集x稀疏)矩阵相乘时,您正在访问~812MB的内存,而在乘以稀疏x稀疏矩阵时,您只能访问~24MB的内存。
请注意,这不包括你编写结果的内存,这可能是一个很重要的部分(在这两种情况下都高达~800MB),但我对Armadillo不是很熟悉,以及它用于矩阵的算法乘法,所以不能确切地说它如何存储中间结果。
无论算法是什么,它肯定需要多次访问两个输入矩阵,这解释了为什么将密集矩阵转换为稀疏(只需要访问800MB密集矩阵),然后执行稀疏x稀疏产品(这需要多次访问24MB内存)比密集x稀疏和稀疏x密集产品更有效。
这里还有各种各样的缓存效果,这需要知道算法的精确实现和硬件(以及大量时间)才能正确解释,但上面是一般的想法。
至于为什么dense x sparse
比sparse x dense
更快,这是因为稀疏矩阵的CSC存储格式。如scipy's documentation中所述,CSC格式对列切片有效,对行切片有效。 dense x sparse
乘法算法需要对稀疏矩阵进行列切片,sparse x dense
需要对稀疏矩阵进行行切片。请注意,如果armadillo使用CSR而不是CSC,则sparse x dense
会有效,而dense x sparse
则不会。
我知道这并不是你所看到的所有性能影响的完整答案,但应该让你大致了解正在发生的事情。正确的分析需要花费更多的时间和精力,并且必须包括算法的具体实现,以及有关运行它的硬件的信息。
答案 1 :(得分:2)
这应该在即将到来的Armadillo 8.500中修复,这将包含在RcppArmadillo 0.8.5 Real Soon Now中。具体做法是:
(sparse x dense)
重新实现为((dense^T) x (sparse^T))^T
,利用相对快速的(dense x sparse)
代码当我测试它时,所花费的时间从约500秒减少到约18秒,这与其他时间相当。