[英]Matrix Multiplication optimization via matrix transpose
我正在進行一項任務,我轉換矩陣以減少矩陣乘法運算的緩存未命中。 根據我對幾個同學的理解,我應該得到8倍的提升。 但是,我只得到2倍......我可能做錯了什么?
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
而不是class
, malloc
而不是new
, typedef 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.