优化C ++ 2-D阵列

时间:2008-09-30 12:05:36

标签: c++ linux optimization gcc stl

我需要一种方法来表示C ++中的双精度二维数组(密集矩阵),具有绝对最小的访问开销。

我已经在各种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。在没有STL的情况下安排二维数组的其他方法 - 指向每一行开头的n个指针数组,或者将堆栈中的整个事物定义为常量matrix[n][n] - 运行速度几乎与索引函数方法。

最近的GCC版本(&gt; 4.0)似乎能够在启用优化时将STL向量矢量编译为与非STL代码几乎相同的效率,但这在某种程度上与机器有关。 / p>

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

10 个答案:

答案 0 :(得分:8)

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

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

valarray接受数字类型作为参数:

valarray<double> a;

然后,您可以使用切片,间接数组......当然,您可以继承valarray并为2D数组定义自己的operator()(int i,int j)...

答案 1 :(得分:8)

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

-fipa-matrix-reorg
  

执行矩阵展平和   移调。矩阵展平尝试   用...替换m维矩阵   它等效的n维矩阵,   其中n <米这降低了水平   访问所需的间接方向   矩阵的元素。第二   优化是矩阵转置   试图改变秩序   矩阵的尺寸以便   改善缓存局部性。都   优化需要fwhole-program   旗。只有在启用转置时才会启用   分析信息是可用的。

请注意,-O2或-O3未启用此选项。你必须自己通过它。

答案 2 :(得分:7)

很可能这是一个参考地点问题。 vector使用new来分配其内部数组,因此由于每个块的头部,每行在内存中至少会有一点距离;如果分配它们时内存已经碎片化,它可能相距很远。阵列的不同行可能至少会导致缓存线故障,并可能导致页面错误;如果你真的不走运,那么两个相邻的行可能位于共享一个TLB插槽的内存行上,而访问一个就会驱逐另一个。

相比之下,您的其他解决方案可确保所有数据都相邻。如果您对齐结构使其跨越尽可能少的缓存行,它可以帮助您提高性能。

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

确保以正确的方向走数组,即跨越(连续的内存地址)而不是向下。这将减少缓存故障。

答案 3 :(得分:6)

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

答案 4 :(得分:1)

公平取决于你在矩阵上使用的算法。

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

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

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

答案 5 :(得分:1)

你可以很容易地做矢量&lt; double&gt;(n * m);

答案 6 :(得分:1)

您可能需要查看http://eigen.tuxfamily.org/处的Eigen C ++模板库。它生成AltiVec或sse2代码以优化矢量/矩阵计算。

答案 7 :(得分:0)

答案 8 :(得分:0)

另一个相关的库是Blitz ++:http://www.oonumerics.org/blitz/docs/blitz.html

Blitz ++旨在优化阵列操作。

答案 9 :(得分:0)

通过声明我自己的二维数组类,我已经为原始图像做了一些时间。

在普通的2D数组中,您可以访问以下元素:

阵列[2] [3]。现在要获得该效果,您将拥有一个带有重载的类数组 []数组访问器。但是,这基本上会返回另一个数组,从而给出 你是第二个维度。

这种方法的问题在于它具有双重函数调用开销。

我这样做的方法是使用()样式重载。

所以而不是 array [2] [3],改变我做了这个样式数组(2,3)。

那个()函数非常小,我确定它是内联的。

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

如果需要,您可以为模板添加模板。
我的差异是我的阵列是动态的。我有一块我记得的焦炭记忆。我使用了一个列缓存,所以我知道下一行的字节序列在哪里开始。 Access已针对访问相邻值进行了优化,因为我将其用于图像处理。

没有代码就很难解释,但基本上结果和C一样快,而且更容易理解和使用。