这个问题是这个问题的继续:2-dimensional array on heap, which version is faster?
我已经定义了类似的内容:
class Array
{
double *data;
int X;
int Y;
public:
Array(int X, int Y, double init = 0) : X(X), Y(Y)
{
data = new double [X*Y];
for (int i=0; i<X*Y; i++)
data[i] = init;
}
~Array() { delete[] data; }
double *operator[] (int x) { return (data+x*Y); }
};
我想拥有一个具有二维可读性的连续数组的速度优势。我认为class Array
会这样做,
Array arr(1000,1000);
arr[x][y] = n;
与普通版本(几乎)快速相同
double *arr = new double [1000*1000];
arr[x*1000+y] = n;
因为operator[]
已定义为inline
。
但普通版本要快得多,封装的版本只有一点点快,因为真正的二维版本double **arr; ...; arr[x][y] = n;
不是真的,请参阅Edit2
这是正常的吗?我正在编译VC ++ 2010并进行优化。
使用vector
请不要回答,我知道这种可能性,但我对这种行为的深层原因感兴趣...
修改:
我已经阅读了评论,我的class Array
进行了2次查找,我应该使用直接1次查找并返回对double的引用。我已经尝试了这个并且没有速度改进了,它完全一样。
我真的不明白为什么我的班级会进行2次查找:
Array arr(1000,1000);
arr[x][y] = n;
应该内联到:
(arr.data+x*arr.Y)[y] = n;
并进一步:
*((arr.data+x*arr.Y)+y) = n;
与...完全相同:
arr.data[x*arr.Y+y] = n; // the proposed 1 lookup access
我错了吗?
EDIT2 :
我又一次注意到,double **arr; arr[x][y] = n;
解决方案的时间从1:47分到2:10分不等 - 以随机的方式。
所有其他解决方案:
class Array
,如上所述double &operator() (int x, int y)
double *arr; arr[x*Y+y] = n;
在1:44分钟左右快速相同并且始终保持不变。
答案 0 :(得分:1)
如果我理解正确,你会问为什么使用2D和1D的速度似乎可以忽略不计。
在我看来,进行2D矩阵访问的最佳方法是使用以下内容。
double& operator()(const int row, const int col) inline{
return data[X*row + col];
}
double operator()(const int row, const int col) inline const{
return data[X*row + col];
}
这为您提供了参考和复制方法。
速度问题在于它高度依赖于机器的底层架构。
第一个问题是缓存大小。显然,缓存越大越好,1D版本应该更好地工作,因为2D通常作为连续内存对缓存更好。
同样在您的示例中,无论内存如何排序,第一次访问单个元素都会很慢,因为元素不在缓存中。但是,如果您多次访问该元素,或者同一区域(缓存行)中的元素,则加速应该更加明显。
第二个问题是矢量化。根据您正在进行的操作,特别是如果它们是数学操作(如添加等),它们将决定速度。如果您有一个具有SSE或AVX扩展的较新处理器,请确保编译器正在编译以使用这些功能,通常这在您设置优化时自动完成。您可能希望通过添加-march = native和-msse3或Windows等效项来确保。
另一个小优化是使X,Y const。这将使内联更加有效,但显然伴随着分配变得痛苦的缺点。
最后一句话:简介,看看你花费最多的时间并改进它。
答案 1 :(得分:1)
您无法获得性能优势,因为您仍然需要在包装版本中执行两次内存查找。仅在访问元素x * 1000 + y的1-d情况下的算术运算仅需要一次内存查找。你的包装版本返回一个指针,然后必须再次取消引用,这是缓慢的部分。
尝试将您的包装版本访问重新设为
inline double operator()(int x, int y) const {return data[x*Y + y];}
inline double& operator()(int x, int y) {return data[x*Y + y];}
并呼叫为
arr(x,y) = n;
我很惊讶封装的那个比普通的二维数组快,因为它只能有更多的开销。
编辑:现在更多地关注这个问题,我发现你的解决方案实际上没有进行两次查找,因为你的重载[]运算符行为不同。请参阅我对原帖的评论。
答案 2 :(得分:0)
简单的另一个好处是维度是const,如果你不需要运行时维度,请尝试使用模板:
此外,不用担心内存管理 - 使用std::unique_ptr
而不是double[]
使用std::array
template <class T, size_t X, size_t Y>
class Array
{
using custom_array=std::array<T,X*Y>;
std::unique_ptr<custom_array> data;
public:
Array() : data{new custom_array} {}
Array(const Array& rhs) : data{new custom_array(*(rhs.data))} {}
Array& operator=(const Array& rhs) {
if (&rhs != this)
{
*data=*(rhs.data);
}
return *this;
}
~Array() {}
T& operator() (int x, int y) { return data->at(x*Y+y); }
T operator() (int x, int y) const { return data->at(x*Y+y); }
};
用法:Array<double,1000,1000> A; double b=A(3,4);
对于苹果到苹果 - 将数据分配为std::array<double,X*Y>
,但由于您需要堆分配,请将上述内容与unique_ptr
一起使用。