簡體   English   中英

矩陣乘法的矩陣乘法優化

[英]Matrix Multiplication optimization via matrix transpose

我正在進行一項任務,我轉換矩陣以減少矩陣乘法運算的緩存未命中。 根據我對幾個同學的理解,我應該得到8倍的提升。 但是,我只得到2倍......我可能做錯了什么?

GitHub上的完整資源

void transpose(int size, matrix m) {
    int i, j;
    for (i = 0; i < size; i++) 
        for (j = 0; j < size; j++) 
            std::swap(m.element[i][j], m.element[j][i]);
}

void mm(matrix a, matrix b, matrix result) {
    int i, j, k;
    int size = a.size;
    long long before, after;

    before = wall_clock_time();
    // Do the multiplication
    transpose(size, b); // transpose the matrix to reduce cache miss
    for (i = 0; i < size; i++)
        for (j = 0; j < size; j++) {
            int tmp = 0; // save memory writes
            for(k = 0; k < size; k++)
                tmp += a.element[i][k] * b.element[j][k];
            result.element[i][j] = tmp;
        }
    after = wall_clock_time();
    fprintf(stderr, "Matrix multiplication took %1.2f seconds\n", ((float)(after - before))/1000000000);
}

我到目前為止做得對嗎?

僅供參考:我需要做的下一個優化是使用SIMD / Intel SSE3

我到目前為止做得對嗎?

不,你的轉置有問題。 在開始擔心性能之前,您應該已經看過這個問題。 當你正在做的任何一種黑客周圍的優化它總是一個好主意,用天真的,但不理想的實現作為一個測試。 如果沒有得到正確的答案,那么實現100倍加速的優化是沒有價值的。

另一個有用的優化是通過引用傳遞。 你正在傳遞副本。 實際上,您的matrix result可能永遠不會消失,因為您正在傳遞副本。 再一次,你應該測試一下。

另一個有助於加速的優化是緩存一些指針。 這仍然很慢:

for(k = 0; k < size; k++)
    tmp += a.element[i][k] * b.element[j][k];
result.element[i][j] = tmp;

優化器可能會看到指針問題的方法,但可能沒有。 如果不使用非標准__restrict__關鍵字告訴編譯器您的矩陣不重疊,至少不會。 緩存指針,因此您不必執行a.element[i]b.element[j]result.element[i] 它仍然可能有助於告訴編譯器這些數組與__restrict__關鍵字不重疊。

附錄
查看代碼后,需要幫助。 首先是一個小評論。 你不是在寫C ++。 你的代碼是C,帶有一絲C ++的暗示。 您使用的是struct而不是classmalloc而不是newtypedef struct而不僅僅是struct ,C頭而不是C ++頭。

由於您的struct matrix ,我對復制構造函數導致的緩慢的評論是不正確的。 它不正確甚至更糟! 使用隱式定義的復制構造函數與包含裸指針的類或結構一起使用火。 如果有人調用m(a, a, a_squared)來獲得矩陣a的平方,你將會非常嚴重地被燒毀。 你會引火燒身更糟糕,如果一些希望m(a, a, a)做的就地計算a 2。

在數學上,您的代碼僅涵蓋矩陣乘法問題的一小部分。 如果有人想將100x1000矩陣乘以1000x200矩陣怎么辦? 這是完全有效的,但是您的代碼無法處理它,因為您的代碼僅適用於方形矩陣。 另一方面,你的代碼會讓某人將100x100矩陣乘以200x200矩陣,這沒有多大意義。

從結構上講,由於您使用了不規則的數組,因此您的代碼幾乎可以100%保證它會很慢。 malloc可以在malloc中噴灑矩陣的行。 如果矩陣在內部表示為連續數組但是被訪問就好像它是NxM矩陣,那么你將獲得更好的性能。 C ++為此提供了一些很好的機制。

如果你的作業暗示你必須轉置,那么,當然,你應該糾正你的轉置程序。 就目前而言,它會進行兩次轉置,完全沒有轉置。 不應讀取j =循環

j=0; j<size; j++

j=0; j<i; j++

不必轉置以避免以“錯誤”順序處理一個因子矩陣的元素。 只需交換j循環和k循環。 暫且不談任何(其他)性能調整,基本的循環結構應該是:

  for (int i=0; i<size; i++)
  {
    for (int k=0; k<size; k++)
    {
      double tmp = a[i][k];
      for (int j=0; j<size; j++)
      {
        result[i][j] += tmp * b[k][j];
      }
    }
  }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM