[英]sparse x dense matrix multiplication performance under-efficient
背景 :我使用Eigen进行人工神经网络,其中典型的维度是每层大约1000个节点。 因此,大多数操作是将大小为〜(1000,1000)的矩阵M
与大小为1000的向量或一批B向量相乘, 其被表示为大小为Bx1000的矩阵 。
在训练神经网络之后,我正在使用修剪 - 这是一种常见的压缩技术,最终得到稀疏矩阵(非空参数的密度在10%到50%之间)。
目标 :我想使用稀疏矩阵进行压缩,其次是性能优化,但这不是主要目标
问题 :我正在比较不同批量大小的稀疏和密集矩阵乘法(仅计算乘法时间)的性能,我观察以下(使用Eigen 3.2.8,MacBook Pro 64位,没有open_mp,并使用标准g ++):
M稀疏/密集的MxN乘法时间(ms),以及大小为1000xB的N.
相同的数字,但显示稀疏和密集矩阵的一批不同大小的每个矢量的时间。 当批量大小增加时,我们清楚地看到密集矩阵的时间减少,并且稀疏矩阵的增强显示出一些错误。 B = 1时标准化
代码 :我对稀疏和密集矩阵使用以下类型:
typedef SparseMatrix<float> spMatFloat;
typedef Matrix<float, Dynamic, Dynamic, RowMajor> deMatRowFloat;
我正在进行基准测试的操作如下:
o.noalias()=m*in.transpose();
其中o
是密集矩阵(1000xB), m
是密集矩阵(1000x1000)或使用m.sparseView()
获得的相应稀疏矩阵, in
是密集矩阵(Bx1000)
完整代码如下(20个不同随机矩阵的平均时间,并且每次乘法运行50次) - B = 32且B = 1的时间低于。
欢迎任何反馈/直觉!
batch 1 ratio 0.3 dense 0.32 sparse 0.29
batch 32 ratio 0.3 dense 2.75 sparse 15.01
#include <Eigen/Sparse>
#include <Eigen/Dense>
#include <stdlib.h>
#include <boost/timer/timer.hpp>
using namespace Eigen;
using namespace boost::timer;
typedef SparseMatrix<float> spMatFloat;
typedef Matrix<float, Dynamic, Dynamic, RowMajor> deMatRowFloat;
void bench_Sparse(const spMatFloat &m, const deMatRowFloat &in, deMatRowFloat &o) {
o.noalias()=m*in.transpose();
}
void bench_Dense(const deMatRowFloat &m, const deMatRowFloat &in, deMatRowFloat &o) {
o.noalias()=m*in.transpose();
}
int main(int argc, const char **argv) {
float ratio=0.3;
int iter=20;
int batch=32;
float t_dense=0;
float t_sparse=0;
deMatRowFloat d_o1(batch,1000);
deMatRowFloat d_o2(batch,1000);
for(int k=0; k<iter; k++) {
deMatRowFloat d_m=deMatRowFloat::Zero(1000,1000);
deMatRowFloat d_b=deMatRowFloat::Random(batch,1000);
for(int h=0;h<ratio*1000000;h++) {
int i=rand()%1000;
int j=rand()%1000;
d_m(i,j)=(rand()%1000)/500.-1;
}
spMatFloat s_m=d_m.sparseView();
{
cpu_timer timer;
for(int k=0;k<50;k++) bench_Dense(d_m,d_b,d_o1);
cpu_times const elapsed_times(timer.elapsed());
nanosecond_type const elapsed(elapsed_times.system+elapsed_times.user);
t_dense+=elapsed/1000000.;
}
{
cpu_timer timer;
for(int k=0;k<50;k++) bench_Sparse(s_m,d_b,d_o2);
cpu_times const elapsed_times(timer.elapsed());
nanosecond_type const elapsed(elapsed_times.system+elapsed_times.user);
t_sparse+=elapsed/1000000.;
}
}
std::cout<<"batch\t"<<batch<<"\tratio\t"<<ratio<<"\tdense\t"<<t_dense/50/iter<<"\tsparse\t"<<t_sparse/50/iter<<std::endl;
}
ggael建议之后的新结果 :我尝试了不同的可能组合,发现在更改M
和B
RowMajor / ColMajor时确实存在巨大的性能差异。
总结一下,我感兴趣做M*B
,其中M
是(1000,1000), B
是(1000,批处理):我有兴趣比较M稀疏/密集的性能和批量增长时的性能。
我测试了3种配置:
结果如下 - 其中数字是每列的比率时间,B = 32 /时间,B = 1,矩阵M,密度为0.3:
最初报告的问题是更糟糕的情况(M ColMajor,B RowMajor)。 对于(M RowMajor,B ColMajor),在B = 32和B = 1之间有5倍的加速,并且稀疏矩阵的性能几乎等于密集矩阵。
在Eigen中,对于密集代数,矩阵矢量和矩阵矩阵产品都经过高度优化,并充分利用了矢量化。 正如您所观察到的,基质矩阵产品具有更高的效率。 这是因为通过增加算术运算次数和存储器访问次数之间的比率,以及利用存储器高速缓存,可以进一步优化矩阵矩阵产品。
然后关于稀疏密集型产品,有两种策略:
Matrix<float,Dynamic,32,RowMajor>
)。 最后,在任何一种情况下,您都可以尝试使用稀疏矩阵的行主要和列主要存储,并确定稀疏矩阵的策略和存储顺序的哪种组合在您的情况下最有效。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.