目前,当我使用重载括号运算符(//first multiplication
)时,我想弄清楚为什么我的朴素矩阵 - 矩阵乘法更慢(0.7秒)。如果我不使用它们(//second multiplication
)并且乘法直接访问类成员数组data_
,它大约快两倍(0.35秒)。我使用我自己的Matrix.h
中定义的矩阵类。
为什么速度有这么大的差异?我的复制构造函数有问题吗?在调用重载的运算符函数时是否存在这么多“开销”,以至于它证明了这种性能损失是合理的呢?
还有一个问题/奇怪的行为:当你互相交换两个最内层的循环(x
和inner
)时,乘法(当然)变得非常慢,但两者都是现在,乘法几乎占用了相同的时间(7秒)。在这种情况下,为什么它们需要相同的时间,但在性能差异达到约50%之前。
编辑:程序按以下方式编译:{{1}}
非常感谢你的帮助!
我的主要功能如下:
g++ -c -std=c++0x -O3 -DNDEBUG
以下是#include "Matrix.h"
int main(){
Matrix m1(1024,1024, 2.0);
Matrix m2(1024,1024, 2.5);
Matrix m3(1024,1024);
//first multiplication
for(int y = 0; y < 1024; ++y){
for(int inner = 0; inner < 1024; ++inner){
for(int x = 0; x < 1024; ++x){
m3(y,x) += m1(y, inner) * m2(inner, x);
}
}
}
//second multiplication
for(int y = 0; y < 1024; ++y){
for(int inner = 0; inner < 1024; ++inner){
for(int x = 0; x < 1024; ++x){
m3.data_[y*1024+x] += m1.data_[y*1024+inner]*m2.data_[inner*1024+inner];
}
}
}
}
的一部分:
Matrix.h
这里是class Matrix{
public:
Matrix();
Matrix(int sizeY, int sizeX);
Matrix(int sizeY, int sizeX, double init);
Matrix(const Matrix & orig);
~Matrix(){delete[] data_;}
double & operator() (int y, int x);
double operator() (int y, int x) const;
double * data_;
private:
int sizeX_;
int sizeY_;
}
Matrix.h
EDIT2:结果我对Matrix::Matrix()
: sizeX_(0),
sizeY_(0),
data_(nullptr)
{ }
Matrix::Matrix(int sizeY, int sizeX)
: sizeX_(sizeX),
sizeY_(sizeY),
data_(new double[sizeX*sizeY]())
{
assert( sizeX > 0 );
assert( sizeY > 0 );
}
Matrix::Matrix(int sizeY, int sizeX, double init)
: sizeX_(sizeX),
sizeY_(sizeY)
{
assert( sizeX > 0 );
assert( sizeY > 0 );
data_ = new double[sizeX*sizeY];
std::fill(data_, data_+(sizeX_*sizeY_), init);
}
Matrix::Matrix(const Matrix & orig)
: sizeX_(orig.sizeX_),
sizeY_(orig.sizeY_)
{
data_ = new double[orig.sizeY_*orig.sizeX_];
std::copy(orig.data_, orig.data_+(sizeX_*sizeY_), data_);
}
double & Matrix::operator() (int y, int x){
assert( x >= 0 && x < sizeX_);
assert( y >= 0 && y < sizeY_);
return data_[y*sizeX_ + x];
}
double Matrix::operator() (int y, int x) const {
assert( x >= 0 && x < sizeX_);
assert( y >= 0 && y < sizeY_);
return data_[y*sizeX_ + x];
}
使用了错误的数组访问权限。我将其更改为//second multiplication
,现在两次乘法都需要相同的时间。
非常感谢你的帮助!
答案 0 :(得分:1)
我认为你的两个版本并没有计算同样的东西:
首先你有:
m3(y,x)+ = m1(y,内部)* m2(内部, x );
但是在第二个你有
m3.data_ [y * 1024 + x] + = m1.data_ [y * 1024 + inner] * m2.data_ [inner * 1024 + inner ];
第二个可以考虑内在因素,而不是内部*(1024 + 1),可以通过多种方式优化内部*(<+ p>)。
两个版本的输出是什么?他们匹配吗?
编辑:另一个回答者非常正确地建议类中的维度不是常数将会从表中进行一些优化;在第一个版本中,编译器不知道大小是2的幂,因此它使用通用乘法但在第二个版本中它知道其中一个操作数是1024(不仅仅是常数而是编译时间常量)所以它可以使用快速乘法(左移2次方)。
(对于我之前关于NDEBUG的回答道歉:我打开了一段时间的页面,所以没有看到编辑行的编辑。)
答案 1 :(得分:0)
我怀疑不同之处在于在operator()版本中,sizeX_不是const,这可能会阻止编译器优化某些内容,即重复将sizeX_加载到寄存器中。尝试在类定义中声明sizeX_和sizeY_ const。
那就是你应该在标题中内联函数,正如评论中所建议的那样。