C++ 利用硬件加速矩陣乘法的實(shí)現(xiàn)
一、矩陣乘法定義
矩陣 A x × y 和 矩陣 B u × v 相乘的前提條件是 y = = u ,并且相乘后得到的矩陣為 C x × v(即 A 的行和 B 的列構(gòu)成了矩陣 C的行列);
二、矩陣類封裝
我們用 C++ 封裝了一個(gè) n × m 的矩陣類,用二維數(shù)組來(lái)存儲(chǔ)數(shù)據(jù),定義如下:
#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 可以認(rèn)為是一個(gè)二維數(shù)組( p k D a t a [ i ] [ j ]就是矩陣第 i 行,第 j 列的數(shù)據(jù)),之所以這里用了二維指針,是因?yàn)楫?dāng) MAXN 很大時(shí),棧上分配不了這么多空間,容易導(dǎo)致棧溢出,所以通過(guò) new 把空間分配在了堆上;2)釋放空間的時(shí)候,首先釋放低維空間,再釋放高維空間;
三、矩陣乘法實(shí)現(xiàn)
1、ijk式
最簡(jiǎn)單的矩陣乘法實(shí)現(xiàn)如下:
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 式,對(duì)矩陣乘法 A × B = C ,枚舉 A 的每一行,再枚舉 B的每一列,分別對(duì)應(yīng)相乘后放入矩陣 C的對(duì)應(yīng)位置中,如下圖所示;
2、 ikj 式
對(duì)上述算法進(jìn)行一些改進(jìn),交換兩個(gè)內(nèi)層循環(huán)的位置,得到如下算法:
class Matrix { ... public: void Multiply_ikj(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 (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 式,對(duì)矩陣乘法 A × B = C A \times B = C A×B=C,行優(yōu)先枚舉 A A A 的每一個(gè)格子,再枚舉 B B B 的每一行,分別對(duì)應(yīng)相乘后放入矩陣 C C C 的對(duì)應(yīng)位置中,每次相乘得到的 C C C 都是部分積,如下圖所示,用綠色的深淺來(lái)表示這個(gè)值是否已經(jīng)完整求得;
3、kij 式
對(duì)上述算法再進(jìn)行一些改進(jìn),交換兩個(gè)外層循環(huán)的位置,得到如下算法:
class Matrix { ... public: void Multiply_kij(const Matrix& other, Matrix& ret) { // assert(m == other.n); ret.Reset(n, other.m); int i, j, 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 式,對(duì)矩陣乘法 A × B = C A \times B = C A×B=C,列優(yōu)先枚舉 A A A 的每一個(gè)格子,再枚舉 B B B 的每一行,分別對(duì)應(yīng)相乘后放入矩陣 C C C 的對(duì)應(yīng)位置中,每次相乘得到的 C C C 都是部分積,如下圖所示,用綠色的深淺來(lái)表示這個(gè)值是否已經(jīng)完整求得;
四、時(shí)間測(cè)試
矩陣階數(shù) | 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 |
由于矩陣乘法本身的時(shí)間復(fù)雜度是 O(N3) 的,所以數(shù)據(jù)量越大,越能看出實(shí)際效果;
五、原理分析
原因是因?yàn)?CPU 訪問(wèn)內(nèi)存的速度比 CPU 計(jì)算速度慢得多,為了解決速度不匹配的問(wèn)題,在 CPU 與 內(nèi)存 之間加了高速緩存cache。高速緩存 cache 的存在大大提高了 CPU 訪問(wèn)數(shù)據(jù)的速度。但是當(dāng)內(nèi)存訪問(wèn)不連續(xù)的時(shí)候,就會(huì)導(dǎo)致 cache 命中率降低,所以為了加速,就要盡可能使內(nèi)存訪問(wèn)連續(xù),即不要跳來(lái)跳去。矩陣
六、最后結(jié)論
運(yùn)行速度: ikj ≈ kij > ijk
模板地址:矩陣乘法模板
到此這篇關(guān)于C++ 利用硬件加速矩陣乘法的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)C++ 矩陣乘法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
淺析C語(yǔ)言中的setjmp與longjmp函數(shù)
以下是對(duì)C語(yǔ)言中的setjmp與longjmp函數(shù)進(jìn)行了詳細(xì)的介紹,需要的朋友可以過(guò)來(lái)參考下2013-09-09Visual Studio2000系列版本安裝OpenGL的圖文教程
這篇文章主要介紹了Visual Studio2000系列版本安裝OpenGL的圖文教程,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-04-04C++中運(yùn)算符 &和&&、|和|| 的詳解及區(qū)別
這篇文章主要介紹了C++中運(yùn)算符 &和&&、|和|| 的詳解及區(qū)別的相關(guān)資料,這里舉例說(shuō)明該如何區(qū)別他們的不同,需要的朋友可以參考下2016-11-11一波C語(yǔ)言二元查找樹(shù)算法題目解答實(shí)例匯總
這篇文章主要介紹了一波C語(yǔ)言二元查找樹(shù)算法題目解答實(shí)例匯總,包括按層次遍歷和轉(zhuǎn)換為鏡像等基本算法題目,需要的朋友可以參考下2016-03-03