c ++重载括号运算符比直接访问数组成员慢
c++ overloaded parentheses operator slower than direct access to array member
目前,我正在尝试弄清楚为什么当我使用重载的括号运算符 (//first multiplication
) 时,我的原始矩阵-矩阵-乘法速度较慢(0.7 秒)。如果我不使用它们 (//second multiplication
) 并且乘法直接访问 class 成员数组 data_
它大约快两倍(0.35 秒)。我使用 Matrix.h
中定义的自己的矩阵 class。
为什么速度会有如此显着的差异?我的复制构造函数有问题吗?在调用重载运算符函数时是否有太多 "overhead" 可以证明这种性能损失是合理的?
还有一个问题/奇怪的行为:当你交换最里面的两个循环(x
和 inner
)时,乘法(当然)非常慢,但是现在这两个乘法几乎花费相同的时间(7 秒)。为什么在这种情况下他们需要相同的时间,但之前有 ~50% 的性能差异。
编辑:程序编译如下: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
的实现
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];
}
EDIT2: 原来我对 //second multiplication
使用了错误的数组访问。我将其更改为 m3.data_[y*1024+x] += m1.data_[y*1024+inner]*m2.data_[inner*1024+x];
,现在两个乘法运算时间相同。
非常感谢您的帮助!
我怀疑不同之处在于在 operator() 版本中,sizeX_ 不是 const,这可能会阻止编译器优化某些东西,即将 sizeX_ 重复加载到寄存器中。尝试在 class 定义中声明 sizeX_ 和 sizeY_ const。
您应该按照评论中的建议在 header 中内联函数。
我认为你的两个版本计算的不是同一件事:
在第一个你有:
m3(y,x) += m1(y, 内部) * m2(内部, x);
但在第二个你有
m3.data_[y*1024+x] += m1.data_[y*1024+inner]*m2.data_[inner*1024+inner];
第二个可以分解 inner out 而不是 inner * (1024 + 1) 可以优化第一个不能的多种方式。
两个版本的输出是什么?它们匹配吗?
编辑:另一位回答者非常正确地建议 class 中的维度不是常量将对 table 进行一些优化;在第一个版本中,编译器不知道大小是 2 的幂,因此它使用通用乘法,但在第二个版本中,它知道其中一个操作数是 1024(不只是一个常数,而是一个 编译时间常量)所以它可以使用快速乘法(左移2的幂)。
(对我之前关于 NDEBUG 的回答表示歉意:我打开了该页面一段时间,所以没有看到您对编译行的编辑。)
目前,我正在尝试弄清楚为什么当我使用重载的括号运算符 (//first multiplication
) 时,我的原始矩阵-矩阵-乘法速度较慢(0.7 秒)。如果我不使用它们 (//second multiplication
) 并且乘法直接访问 class 成员数组 data_
它大约快两倍(0.35 秒)。我使用 Matrix.h
中定义的自己的矩阵 class。
为什么速度会有如此显着的差异?我的复制构造函数有问题吗?在调用重载运算符函数时是否有太多 "overhead" 可以证明这种性能损失是合理的?
还有一个问题/奇怪的行为:当你交换最里面的两个循环(x
和 inner
)时,乘法(当然)非常慢,但是现在这两个乘法几乎花费相同的时间(7 秒)。为什么在这种情况下他们需要相同的时间,但之前有 ~50% 的性能差异。
编辑:程序编译如下: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
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];
}
EDIT2: 原来我对 //second multiplication
使用了错误的数组访问。我将其更改为 m3.data_[y*1024+x] += m1.data_[y*1024+inner]*m2.data_[inner*1024+x];
,现在两个乘法运算时间相同。
非常感谢您的帮助!
我怀疑不同之处在于在 operator() 版本中,sizeX_ 不是 const,这可能会阻止编译器优化某些东西,即将 sizeX_ 重复加载到寄存器中。尝试在 class 定义中声明 sizeX_ 和 sizeY_ const。
您应该按照评论中的建议在 header 中内联函数。
我认为你的两个版本计算的不是同一件事:
在第一个你有:
m3(y,x) += m1(y, 内部) * m2(内部, x);
但在第二个你有
m3.data_[y*1024+x] += m1.data_[y*1024+inner]*m2.data_[inner*1024+inner];
第二个可以分解 inner out 而不是 inner * (1024 + 1) 可以优化第一个不能的多种方式。
两个版本的输出是什么?它们匹配吗?
编辑:另一位回答者非常正确地建议 class 中的维度不是常量将对 table 进行一些优化;在第一个版本中,编译器不知道大小是 2 的幂,因此它使用通用乘法,但在第二个版本中,它知道其中一个操作数是 1024(不只是一个常数,而是一个 编译时间常量)所以它可以使用快速乘法(左移2的幂)。
(对我之前关于 NDEBUG 的回答表示歉意:我打开了该页面一段时间,所以没有看到您对编译行的编辑。)