Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/135.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 为什么在本征矩阵中矩阵加法比矩阵向量乘法慢?_C++_Performance_Matrix_Eigen - Fatal编程技术网

C++ 为什么在本征矩阵中矩阵加法比矩阵向量乘法慢?

C++ 为什么在本征矩阵中矩阵加法比矩阵向量乘法慢?,c++,performance,matrix,eigen,C++,Performance,Matrix,Eigen,为什么矩阵加法比矩阵向量乘法耗时更长 矩阵加法只需要n^2加法,而矩阵向量乘法需要n*(n-1)加法和n^2乘法 然而,在Eigen中,矩阵加法所需的时间是 矩阵向量乘法不需要。在Eigen中是否存在加速矩阵加法运算的选项 #include <eigen3/Eigen/Eigen> #include <iostream> #include <ctime> #include <string> #include <chrono> #incl

为什么矩阵加法比矩阵向量乘法耗时更长

矩阵加法只需要n^2加法,而矩阵向量乘法需要n*(n-1)加法和n^2乘法

然而,在Eigen中,矩阵加法所需的时间是 矩阵向量乘法不需要。在Eigen中是否存在加速矩阵加法运算的选项

#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;
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
使用名称空间特征;
使用名称空间std;
int main()
{
常数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);
自动启动=计时::稳定时钟::现在();

对于(int j=0;j简短回答:您计算了操作的数量,但忽略了计算内存访问的数量,对于加法情况,内存访问的成本几乎要高出2倍。详细信息如下

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

第二,在你的计算中,你忘记了测量内存访问的次数。回想一下,除非数据已经在一级缓存中,否则一次内存加载要比一次add或一次mul昂贵得多

添加起来很简单:我们有
2*n^2
加载和大量缓存未命中,再加上
n^2
存储

对于具有列主矩阵的矩阵向量积,输入向量只读取一次,因此
n^2+n
加载输入,并且由于列一次由4列块处理,因此我们对输出向量进行
n^2/4
读写,但几乎没有缓存未命中,因为它适合一级缓存加法的内存负载几乎比矩阵向量积的内存负载高出x2,因此x2速度因子并不异常


此外,矩阵向量代码通过显式循环剥离进行了更积极的优化,尽管我怀疑这会对该基准测试产生任何影响,因为您的矩阵根本不适合一级缓存。

您是否验证了没有优化循环?编译器可能会发现,运行循环10000次的效果是最好的与运行一次相同…是的,我做了。请查看新更新的描述。它仍然需要更长的时间。@MaxLanghoYou更改了编译标志,但您仍然没有使用结果!您可以在每次加法或乘法后执行类似于静态浮点x=0.0;x=x+qq[0][0];x=x+pp[0]的操作,以便编译器必须使用结果。(非常感谢你的建议@Jeffrey,我用一次迭代来演示。这不是你的确切问题,而是关于缓存在总体上如何/为什么重要的书面说明,请参阅感谢你的回答。在eigen中是否存在任何简单的方法来加速矩阵加法?或者这只是其性能的上限?我在Mat中注意到了这一点。)lab 2018a,矩阵加法所需的时间与矩阵向量乘法所需的时间大致相同。在单个核上,这实际上取决于矩阵的大小和标量类型。在基准测试中,您使用float,而matlab使用double。这可能会改变缓存行为。对于基准测试中足够大的矩阵,多线程可能会有所帮助我只有matlab2016a,在我的haswell计算机上,矩阵向量积几乎比矩阵加法快3倍(对于1000x1000个矩阵)。
#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;
}