2008-09-30 96 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一樣快,並且更容易理解和使用。