2012-12-29 61 views
2

我正在編寫模板矩陣類,並且當從運算符返回值時,得到堆棧溢出:+, - ,*適用於較大的矩陣。我寧願以某種方式通過參考返回緩解堆棧,以避免額外的複製,但後來,我還得回去與構造一個對象,並打破「使用刪除爲每」的一般規則。由於複製開銷和堆棧限制問題,我無法按值返回,而且由於內存泄漏,我也無法通過引用返回,所以我應該怎麼做?通過引用返回的C++矩陣類重載運算符

這是我的產品功能(矩陣包含二維數組elems的):

template<typename T, unsigned int n, unsigned int m> template<unsigned int m2> 
Matrix<T,n,m2> Matrix<T,n,m>::operator*(Matrix<T,m,m2>& M) { 
    T prod[n][m2]; 
    if(n*m < GPUAccelerationThreshold) 
     for(int i = 0; i < n; i++) 
      for(int j = 0; j < m2; j++) { 
       prod[i][j] = elems[i][0] * M(0, j); 
       for(int p = 1; p < m; p++) 
        prod[i][j] += elems[i][p] * M(p, j); 
      } 
    else { 
     array_view<T, 2> product(n, m2, *prod); 
     array_view<T, 2> a(n, m, *elems); 
     array_view<T, 2> b(m, m2, M.elems[0]); 

     parallel_for_each(
      product.extent, 
      [=](index<2> idx) restrict(amp) { 
       int row = idx[0]; 
       int col = idx[1]; 
       for (int inner = 0; inner < m; inner++) { 
        product[idx] += a(row, inner) * b(inner, col); 
       } 
      } 
     ); 
     product.synchronize(); 
    } 


    return Matrix<T,n,m2>(prod); 
} 

我在寫這個類,因爲我想提高GPU的一些矩陣運算(與MS AMP)。我搜索了一個現有的解決方案,發現GPU加速的線性代數庫,但是我找不到它們是一個帶有+, - ,*運算符的簡單矩陣類。也許有人可以推薦我嗎?

+0

你能返回一個共享指針嗎? – andre

+0

你有沒有聽說過'pImpl'模式? – Yakk

回答

3

三篇快評論:

  • 傳統,Matrix類使用了動態分配 。你不顯示您的Matrix類,但如果你的 的數據是:
     
    T myData[n][m]; 
    
    你可能想將其更改爲:
     
        std::vector myData; 
    
    ,在構造函數初始化它的大小n * m,並 計算在operator[]單指數(如果你想進行任何邊界檢查,應該返回一個代理 )。 或者,您可以使用operator()(int i, int j)來訪問一個元素:myMatrix(i, j)myMatrix[i][j]是首選訪問取決於您問的是誰。 雖然此解決方案略微增加了總內存使用量(但非常輕微),但它將堆棧佔用空間減少到幾十個字節,而不考慮矩陣的大小。
  • 傳統上,矩陣類沒有尺寸作爲其模板參數的一部分 。是否這是一件好事 是否有爭議。您的解決方案 可以更好地進行類型檢查(編譯時爲 錯誤,而不是運行時),但如果維度是構造函數的參數,而不是模板參數的 ,則可以從命令行 或配置文件或其他。這是經典的安全性 與靈活性的折衷。 關於您的問題,沒有尺寸爲 模板參數表示類型爲T的所有矩陣的相同類型爲 。因此,您可以訪問您的成員函數返回的矩陣的內部結構,並且您不再需要中間T prod[n][m2]中的 。當然,你可以使Matrix朋友的所有 實例化,或者簡單地使用訪問 函數來設置值。無論如何,你做不是想要一個 中間T prod[n][m2];這不僅需要大量的 堆棧內存,這意味着你將不得不復制結果。
  • 最後,這是較爲先進的:在最好的矩陣 類,operator*不返回一個矩陣,而是一個輔助 類,沿着線: 模板 類MatrixMultiply函數 { L恆定* myLhs; R const * myRhs; public: typedef T value_type; MatrixMultiply函數(L常量&LHS,R常量&右) :myLhs(左軸) ,myRhs(右軸) { } INT的getX()const的 { 返回myLhs->的getX(); } int getY()const { return myRhs-> getY(); } T get(int i,int j)const { return calculateIJ(myLhs,myRhs); } }; 然後,您將提供一個使用getX(),getY()get(i, j)的模板化構造函數和賦值運算符 。您的 operator*也是一個模板,它返回一個 MatrixMultiply: 模板 MatrixMultiply函數 運算符*(L常量&LHS,R常量&右) { 返回MatrixMultiply函數(左,右); } (請注意,如果L::value_typeR::value_type不 相同,這不會編譯。這是你想要什麼,除了 的錯誤信息會很不明朗。) 的結果是,你永遠實際上建立了中間體,臨時矩陣。 正如你所想象的,上述解決方案大大簡化了。 您需要額外的錯誤處理代碼,我不認爲並行化是微不足道的。但它避免了所有中間矩陣的構造,即使在複雜的 表達式中。 (同樣的技術可以用一個抽象基類一起使用, 說MatrixAccessor,與純虛吸氣劑,並導出 Matrix和從 它。IMHO所有像MatrixMultiply的助手,這是很多的可讀性,並且從編譯器錯誤消息 肯定會更容易理解。結果將是 一樣,只要編譯器實際上是內聯的所有成員 功能。但是,這是一個很大的如果,因爲可以有 顯著函數嵌套。 )
+0

謝謝你的回答(以及其他問題),我現在看到解決方案不會像我想象的那麼簡單。我認爲這將需要很多進一步的閱讀才能繼續.. – deFenestra

+0

@deFenestra這一切都取決於你想達到什麼,以及性能問題。只需將數據移動到一個'std :: vector',並將結果直接放入返回的矩陣,而不是中間的'T prod [n] [m]',就可以解決堆棧溢出問題。我的其他建議實際上只是您可能想要長期思考的想法。 (如果你正在處理大型矩陣和複雜的表達式,這些中間臨時對象可能會用完所有的堆,在這種情況下,你需要我最後的建議。) –

1

有沒有簡單的方法來解決這個問題。您不能將堆棧局部變量作爲參考返回,因爲當您返回時,變量「後面」的內存將消失。所以你必須有一些專用的存儲。它不一定來自新建/刪除,但在複製數據時確實需要有某種存儲。

一個解決辦法當然有三操作數的操作,這樣反而:

a = b + c; 

您使用的功能:

加(A,B,C);

其中a,b和c是參考。

它確實使代碼變得更加混亂,但我想不出更明顯的方式來解決問題 - 如果你不想編寫自己的分配器/刪除函數(或垃圾收集器)。

1

其實我不能完全理解你的想法......二元運算符有兩個參數並創建結果。事實上,你可以看到你正在返回一個新創建的對象。所以這是編寫程序的唯一方法:分配內存,使用它,然後刪除它。事實上,我甚至不明白你的構造函數的作用。如果它簡單地將指針複製到「prod」,那麼當您從函數返回結果矩陣時,結果矩陣會被破壞,因爲只要函數返回(因爲它創建在堆棧上),「prod」內存將被刪除。所以你不能通過引用返回它。

我看到解決方案在矩陣構造函數中分配內存的方式。如果根據矩陣大小將它作爲模板,矩陣的大小可以從模板參數中得知(我發現製作矩陣大小作爲參數的模板是非常奇怪的。這是什麼意思?)。所以你在構造函數中用「new」分配內存,並在「delete」中使用析構函數刪除它。所以這個內存將根據在OOP中工作得很好的RAII方法進行分配。然後你實現諸如setElement(i,j,value)之類的方法,並在你的二元運算符中設置新創建的矩陣中的元素並返回它。

但是,我希望你照顧一些問題。複製構造函數必須真正複製矩陣,而不僅僅是一個指針(或者幾個析構函數將試圖銷燬相同的內存),或者你可以編程實際上覆制矩陣的「懶惰複製」模型(請參閱wiki)。或者你可以在不實現的情況下使拷貝構造函數保持私有狀態(以防止拷貝)。 如果您不允許創建諸如「setElement」之類的方法,因爲您不希望庫的用戶更改矩陣值,則可以訪問私有數據和方法(即使在作爲參數或新獲得的對象中創建)在這樣的操作符中,因爲你在類方法中。

如果您需要將原始指針傳遞給其他計算函數,因爲它在「其他」部分完成,您可以從一個只複製一個指針的指針構造一個構造函數(但這是危險的方式。你不能在任何地方訪問它,因爲矩陣類現在是老闆),或者完全複製數據(雖然速度較慢,但​​你可以根據需要傳遞堆棧指針或需要控制的指針),析構函數會清除它矩陣破壞時。或者你甚至可以創建諸如「getRawMatrix()」之類的私有方法,該方法會將原始指針返回給矩陣中的數據,並將此指針傳遞給您的計算函數,或者甚至只是獲取原始數據指針,因爲您位於矩陣類的一個方法中。

通常你在一個構造函數中分配內存,並製作「懶惰的複製」模型,因爲矩陣可能很大。允許在課程內部訪問私人數據和成員,這就是課程的制定。我希望它會有用..