【发布时间】:2016-09-17 12:55:04
【问题描述】:
上下文:我将 Eigen 用于人工神经网络,其典型尺寸约为每层 1000 个节点。因此,大多数操作是将大小为 ~(1000,1000) 的矩阵 M 与大小为 1000 的向量或一批 B 向量相乘,它们表示为大小为 Bx1000 的矩阵。
训练神经网络后,我正在使用修剪 - 这是一种常见的压缩技术,最终得到稀疏矩阵(非空参数的密度在 10% 到 50% 之间)。
目标:我想将稀疏矩阵用于压缩目的,其次用于性能优化,但这不是主要目标
问题: 我正在比较不同批量大小的稀疏和密集矩阵乘法(仅计算乘法时间)的性能,并且我观察到以下内容(使用 Eigen 3.2.8、MacBook Pro 64 位、没有 open_mp 并使用标准 g++):
- 当 B=1(矩阵 x 向量)时 - 密度为 10% 或 30% 的稀疏矩阵运算比密集矩阵运算更有效 - 这似乎是预期的结果:执行的运算要少得多
- 对于 B=32:
- 密集矩阵运算所需的时间仅为 B=1 所需时间的 10 倍左右 - 这很酷 - 是否显示了一些矢量化效果?
- 稀疏矩阵运算所需的时间是 B=1 所需时间的 67 倍 - 这意味着它的效率低于独立处理 32 个向量
MxN multiplication time (ms) for M sparse/dense, and N of size 1000xB
代码: 我将以下类型用于稀疏和密集矩阵:
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,batch):我有兴趣比较M sparse/dense 和batch 何时增长的性能.
我测试了 3 种配置:
- M 密集,B 密集
- M 稀疏,B 密集
- M 稀疏,B 密集,但 M*B 的乘法是逐列手动完成的
结果如下 - 其中数字是 B=32 时每列时间/B=1 时每列时间的比率,矩阵 M 密度为 0.3:
最初报告的问题是最坏的情况(M ColMajor,B RowMajor)。对于 (M RowMajor, B ColMajor),B=32 和 B=1 之间有 5 倍的加速,稀疏矩阵的性能几乎等同于密集矩阵。
【问题讨论】:
-
你在使用编译器优化吗?
-
是的,使用
-O2 -msse4