C++ 利用硬體加速矩陣乘法的實現
一、矩陣乘法定義
矩陣 A x × y 和 矩陣 B u × v 相乘的前提條件是 y = = u ,並且相乘後得到的矩陣為 C x × v(即 A 的行和 B 的列構成了矩陣 C的行列);
二、矩陣類封裝
我們用 C++ 封裝了一個 n × m 的矩陣類,用二維陣列來儲存資料,定義如下:
#define MAXN 1000 #define LL __int64 class Matrix { private: int n,m; LL** pkData; public: Matrix() : n(0),m(0) { pkData = NULL; } void Alloc() { pkData = new LL *[MAXN]; // 1) for (int i = 0; i < MAXN; ++i) { pkData[i] = new LL[MAXN]; } } void Dealloc() { if (pkData) { for (int i = 0; i < MAXN; ++i) { // 2) delete [] pkData[i]; } delete[] pkData; pkData = NULL; } } };
1) p k D a t a 可以認為是一個二維陣列( p k D a t a [ i ] [ j ]就是矩陣第 i 行,第 j 列的資料),之所以這裡用了二維指標,是因為當 MAXN 很大時,棧上分配不了這麼多空間,容易導致棧溢位,所以通過 new 把空間分配在了堆上;2)釋放空間的時候,首先釋放低維空間,再釋放高維空間;
三、矩陣乘法實現
1、ijk式
最簡單的矩陣乘法實現如下:
class Matrix { ... public: void Multiply_ijk(const Matrix& other,Matrix& ret) { // assert(m == other.n); ret.Reset(n,other.m); int i,j,k; for (i = 0; i < n; i++) { for (j = 0; j < other.m; j++) { for (k = 0; k < m; k++) { ret.pkData[i][j] += pkData[i][k] * other.pkData[k][j]; } } } } };
這種方法被稱為ijk 式,對矩陣乘法 A × B = C ,列舉 A 的每一行,再列舉 B的每一列,分別對應相乘後放入矩陣 C的對應位置中,如下圖所示;
2、 ikj 式
對上述演算法進行一些改進,交換兩個內層迴圈的位置,得到如下演算法:
class Matrix { ... public: void Multiply_ikj(const Matrix& other,k; for (i = 0; i < n; i++) { for (k = 0; k < m; k++) { LL v = pkData[i][k]; for (j = 0; j < other.m; j++) { ret.pkData[i][j] += v * other.pkData[k][j]; } } } } };
這種方法被稱為 ikj 式,對矩陣乘法 A × B = C A \times B = C A×B=C,行優先列舉 A A A 的每一個格子,再列舉 B B B 的每一行,分別對應相乘後放入矩陣 C C C 的對應位置中,每次相乘得到的 C C C 都是部分積,如下圖所示,用綠色的深淺來表示這個值是否已經完整求得;
3、kij 式
對上述演算法再進行一些改進,交換兩個外層迴圈的位置,得到如下演算法:
class Matrix { ... public: void Multiply_kij(const Matrix& other,k; for (k = 0; k < m; k++) { for (i = 0; i < n; i++) { LL v = pkData[i][k]; for (j = 0; j < other.m; j++) { ret.pkData[i][j] += v * other.pkData[k][j]; } } } } };
這種方法被稱為 k i j kij kij 式,對矩陣乘法 A × B = C A \times B = C A×B=C,列優先列舉 A A A 的每一個格子,再列舉 B B B 的每一行,分別對應相乘後放入矩陣 C C C 的對應位置中,每次相乘得到的 C C C 都是部分積,如下圖所示,用綠色的深淺來表示這個值是否已經完整求得;
四、時間測試
矩陣階數 | i j k ijkijk | i k j ikjikj | k i j kijkij |
---|---|---|---|
200 | 47 ms | 31 ms | 16 ms |
500 | 781 ms | 438 ms | 453 ms |
1000 | 8657 ms | 3687 ms | 3688 ms |
2000 | 69547 ms | 28000 ms | 29672 ms |
由於矩陣乘法本身的時間複雜度是 O(N3) 的,所以資料量越大,越能看出實際效果;
五、原理分析
原因是因為 CPU 訪問記憶體的速度比 CPU 計算速度慢得多,為了解決速度不匹配的問題,在 CPU 與 記憶體 之間加了快取記憶體cache。快取記憶體 cache 的存在大大提高了 CPU 訪問資料的速度。但是當記憶體訪問不連續的時候,就會導致 cache 命中率降低,所以為了加速,就要儘可能使記憶體訪問連續,即不要跳來跳去。矩陣
六、最後結論
執行速度: ikj ≈ kij > ijk
模板地址:矩陣乘法模板
到此這篇關於C++ 利用硬體加速矩陣乘法的實現的文章就介紹到這了,更多相關C++ 矩陣乘法內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!