为什么矩阵加法比本征矩阵向量乘法慢?

复合滤波器

为什么矩阵加法要比矩阵矢量乘法花费更多的时间?

矩阵相加仅花费n ^ 2相加,而矩阵矢量乘法则需要n *(n-1)相加和n ^ 2相乘。

但是,在Eigen中,Matrix Add的时间是Matrix-Vector乘法的两倍。是否有任何选项可以加快Eigen中的Matrix Add操作?

#include <eigen3/Eigen/Eigen>
#include <iostream>
#include <ctime>
#include <string>
#include <chrono>
#include <fstream>
#include <random>
#include <iomanip>

using namespace Eigen;
using namespace std;

int main()
{
const int l=100;
MatrixXf m=MatrixXf::Random(l,l);
MatrixXf n=MatrixXf::Random(l,l);
VectorXf v=VectorXf::Random(l,1);

MatrixXf qq=MatrixXf::Random(l,1);
MatrixXf pp=MatrixXf::Random(l,l);

auto start = chrono::steady_clock::now();
for(int j=0;j<10000;j++)
qq=m*v;
auto end = chrono::steady_clock::now();
double time_duration=chrono::duration_cast<chrono::milliseconds>(end - start).count();
std::cout << setprecision(6) << "Elapsed time in seconds : "<< time_duration/1000<< "s" << std::endl;
auto start1 = chrono::steady_clock::now();
for(int j=0;j<10000;j++)
pp=m+n;
auto end1 = chrono::steady_clock::now();
double time_duration1=chrono::duration_cast<chrono::milliseconds>(end1 - start1).count();
std::cout << setprecision(6) << "Elapsed time in seconds : "<< time_duration1/1000<< "s" << std::endl;
}

测试1:未经任何优化:

编译命令:g ++-8 -test.cpp -o test

运行命令:./ test

经过的时间(秒):0.323s

经过的时间(秒):0.635s

测试2:使用-march = native优化:

g ++-8 test.cpp -march =本机-o测试

运行命令:./ test

经过的时间(秒):0.21s

经过的时间(秒):0.372s

测试3:使用-O3优化:

编译命令:g ++-8 -test.cpp -O3 -o test

运行命令:./ test

经过的时间(秒):0.009s

经过的时间(秒):0.016s

测试4:-march = native,-O3优化:

编译命令:g ++-8 -test.cpp -march = native -O3 -o test

运行命令:./ test

经过的时间(秒):0.008s

经过的时间(秒):0.016s

==============

我注意到,由于我没有使用上一次迭代的结果,因此编译器可能会作弊。为了解决此问题,我改为进行一次迭代,并使用较大的大小来获得稳定的时间统计信息。

#include <eigen3/Eigen/Eigen>
#include <iostream>
#include <ctime>
#include <string>
#include <chrono>
#include <fstream>
#include <random>
#include <iomanip>

using namespace Eigen;
using namespace std;

int main()
{
const int l=1000;
MatrixXf m=MatrixXf::Random(l,l);
MatrixXf n=MatrixXf::Random(l,l);
VectorXf v=VectorXf::Random(l,1);

MatrixXf qq=MatrixXf::Random(l,1);
MatrixXf pp=MatrixXf::Random(l,l);

auto start = chrono::steady_clock::now();
qq=m*v;
auto end = chrono::steady_clock::now();
double time_duration=chrono::duration_cast<chrono::microseconds>(end - start).count();

auto start1 = chrono::steady_clock::now();
pp=m+n;
auto end1 = chrono::steady_clock::now();
double time_duration1=chrono::duration_cast<chrono::microseconds>(end1 - start1).count();
std::cout << setprecision(6) << "Elapsed time in microseconds : "<< time_duration<< "us" << std::endl;
std::cout << setprecision(6) << "Elapsed time in microseconds : "<< time_duration1<< "us" << std::endl;
}

测试1:未经任何优化:

编译命令:g ++-8 -test.cpp -o test

运行命令:./ test

经过的时间(以微秒为单位):3125us

经过的时间(以微秒为单位):6849us

测试2:使用-march = native优化:

g ++-8 test.cpp -march =本机-o测试

运行命令:./ test

经过的时间(以微秒为单位):1776us

经过的时间(以微秒为单位):3815us

测试3:使用-O3优化:

编译命令:g ++-8 -test.cpp -O3 -o test

运行命令:./ test

经过的时间(以微秒为单位):449us

经过的时间(以微秒为单位):760us

测试4:-march = native,-O3优化:

编译命令:g ++-8 -test.cpp -march = native -O3 -o test

运行命令:./ test

经过的时间(以微秒为单位):351us

经过的时间(以微秒为单位):871us

加格尔

简短的答案:您计算了操作数,但忽略了对内存访问的计数,在加法情况下,内存访问的开销增加了近2倍。详细信息如下。

首先,两种操作的实际操作次数相同,因为现代CPU能够同时执行一个独立的加法和乘法。x*y+z甚至可以将两个顺序的mul / add类似作为一个单独的操作进行融合,其成本与1加法或1乘法相同。如果您的CPU支持FMA,这就是发生的事情-march=native,但是我怀疑FMA在这里起什么作用。

其次,在您的计算中,您忘记了测量内存访问的数量。回想一下,除非数据已经存在于L1高速缓存中,否则一种内存负载要比一种添加或一倍多要昂贵得多。

除此之外,它很容易:我们的2*n^2负载中有很多缓存未命中以及n^2存储。

对于具有以列为主的矩阵的矩阵向量乘积,输入向量仅读取一次,因此n^2+n会加载输入,并且由于列是由4列的块一次处理的,因此我们n^2/4对输出向量进行了读写操作,但是高速缓存未命中几乎为零,因为它适合L1高速缓存。因此,总的来说,相加的开销要比矩阵向量乘积的开销大将近x2,因此x2的速度因子并不是异常。

此外,矩阵向量代码通过显式循环剥离得到了更积极的优化,尽管我怀疑这对基准测试是否会有所不同,因为您的矩阵根本不适合L1缓存。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章