2008-09-30 98 views
7

我需要一种方法来代表用C双打的2 d阵列(致密基质)++,具有绝对最小访问开销。优化C++ 2-d阵列

我已经做了各种Linux/UNIX机器和gcc版本的一些时间。向量的STL向量,声明为:

vector<vector<double> > matrix(n,vector<double>(n)); 

并通过matrix[i][j]访问是访问不是数组声明为5%和更慢的100%之间:

double *matrix = new double[n*n]; 

通过内联指数函数matrix[index(i,j)]访问,其中index(i,j)评估为i + n * j。 n个指针的数组的每一行的开始处,或在堆栈上限定整个事情作为恒定大小matrix[n][n] - - 运行在几乎完全相同的速度指数函数法排列2-d阵列而不STL的其它方式。

当开启优化时,最近的GCC版本(> 4.0)似乎能够将STL向量向量编译成几乎与非STL代码相同的效率,但这与机器相关。

我想如果可以使用STL,但必须选择最快的解决方案。有没有人有使用GCC优化STL的经验?

回答

8

如果您使用GCC,编译器可以分析您的矩阵访问并在某些情况下更改内存中的顺序。魔术编译标记被定义为:

-fipa-matrix-reorg 

执行矩阵平整和 移调。矩阵展平尝试 用其等价的n维矩阵替换m维矩阵,其中n为<米。这降低了访问矩阵元素所需的间接寻址级别。第二个 优化是矩阵转置 ,试图改变矩阵的维度的顺序,以便 改善缓存局部性。两个 优化都需要编程 标志。仅当 分析信息可用时才能启用转置。

请注意,此选项不由-O2或-O3启用。你必须自己传递它。

8

对于矩阵,我的猜测是最快的是使用1D STL数组并重写()运算符以将其用作2D矩阵。

但是,STL还定义了一种专门用于不可调整大小的数值数组的类型:valarray。您对就地操作也有各种优化。

的valarray接受作为参数数值类型:

valarray<double> a; 

然后,您可以用切片的,间接的阵列,...当然,你可以继承的valarray和定义自己的操作符()(INT i,int j)for 2D arrays ...

+0

我给予好评是的valarray,不一定要做出一个自定义的矩阵类型。那么,自定义矩阵类型可以工作,但仍然应该基于valarray而不是矢量(valarray支持切片,这使得获得一列就像获得一行一样简单)。 – 2008-09-30 12:12:56

+0

小心继承std :: valarray;它不是为继承而设计的,因为大部分的“STL”。 – 2008-09-30 13:15:16

+0

只要不向其中添加数据,就可以继承任何类的STL,因为构造函数不会被调用。虽然没有pb添加方法。 – PierreBdR 2008-09-30 13:33:52

6

我的建议是使用Boost.UBLAS,它提供了快速矩阵/向量类。

7

很可能这是局部性的,参考的问题。 vector使用new来分配它的内部数组,所以每行在内存中至少会因为每个数据块的头部而分开;如果在分配内存时内存已经碎片化,它可能会有很长的距离。阵列的不同行可能至少会导致缓存行故障,并可能导致页面错误;如果你真的不走运,两条相邻的行可能在共享一个TLB槽的存储器行上,而访问它们将会驱逐另一行。

相反的其他解决方案保证所有的数据是相邻的。如果您调整结构以便尽可能少地使用缓存行,它可以帮助您提高性能。

vector是专为调整大小的阵列。如果您不需要调整数组大小,请使用常规C++数组。 STL操作通常可以在C++数组上运行。

确保以正确的方向走过阵列,即跨过(连续的存储器地址)而不是向下走。这将减少缓存故障。

1

公平地依赖于你在矩阵上使用的算法。

当您按行访问数据时,双名[n * m]格式非常快,因为除了乘法和加法之外几乎没有开销,并且因为您的行是打包的数据,将在缓存中保持一致。

如果您的算法访问列顺序数据,则其他布局可能具有更好的缓存一致性。如果您的算法访问矩阵的象限中的数据,甚至其他布局可能会更好。

尝试针对您正在使用的使用类型和算法进行一些研究。如果矩阵非常大,这一点特别重要,因为缓存未命中可能会损害您的性能,而不是需要1或2次额外的数学运算来访问每个地址。

1

您可以轻松地做矢量< double>(n * m);

0

我已经通过声明自己的2维数组类来完成这一段时间的原始图像。

在一个正常的2D阵列,则可以访问的元素,如:

阵列[2] [3]。现在为了得到这个效果,你会有一个带有重载 []数组访问器的类数组。但是,这基本上会返回另一个数组,从而使您成为第二个维度。

这种方法的问题是,它具有双重功能调用的开销。

我做的是使用()风格的过载的方式。

因此,而不是 array [2] [3],改变我做它这样的风格数组(2,3)。

即()函数是非常微小的,我确信它是内联。

请参阅此链接的,一般的概念: http://www.learncpp.com/cpp-tutorial/99-overloading-the-parenthesis-operator/

如果需要,您可以模板类型。
我的区别是我的数组是动态的。我有一块char声明,我会声明。我使用了一个列缓存,所以我知道下一行开始的位置。访问已针对访问邻近值进行了优化,因为我将它用于图像处理。

没有代码就很难解释,但实质上结果与C一样快,并且更容易理解和使用。