我需要一种方法来表示C ++中的双精度二维数组(密集矩阵),具有绝对最小的访问开销。
我已经在各种linux / unix机器和gcc版本上做了一些时间。向量的STL向量,声明为:
vector<vector<double> > matrix(n,vector<double>(n));
并通过matrix[i][j]
访问的访问速度比声明为:
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的经验?
答案 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)
Boost中有uBLAS实现。值得一看。
http://www.boost.org/doc/libs/1_36_0/libs/numeric/ublas/doc/matrix.htm
答案 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一样快,而且更容易理解和使用。