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ù)組來存儲(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)致棧溢出,所以通過 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 都是部分積,如下圖所示,用綠色的深淺來表示這個(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 都是部分積,如下圖所示,用綠色的深淺來表示這個(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 訪問內(nèi)存的速度比 CPU 計(jì)算速度慢得多,為了解決速度不匹配的問題,在 CPU 與 內(nèi)存 之間加了高速緩存cache。高速緩存 cache 的存在大大提高了 CPU 訪問數(shù)據(jù)的速度。但是當(dāng)內(nèi)存訪問不連續(xù)的時(shí)候,就會(huì)導(dǎo)致 cache 命中率降低,所以為了加速,就要盡可能使內(nèi)存訪問連續(xù),即不要跳來跳去。矩陣
六、最后結(jié)論
運(yùn)行速度: ikj ≈ kij > ijk
模板地址:矩陣乘法模板
到此這篇關(guān)于C++ 利用硬件加速矩陣乘法的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)C++ 矩陣乘法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Visual Studio2000系列版本安裝OpenGL的圖文教程
這篇文章主要介紹了Visual Studio2000系列版本安裝OpenGL的圖文教程,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-04-04
C++中運(yùn)算符 &和&&、|和|| 的詳解及區(qū)別
這篇文章主要介紹了C++中運(yùn)算符 &和&&、|和|| 的詳解及區(qū)別的相關(guān)資料,這里舉例說明該如何區(qū)別他們的不同,需要的朋友可以參考下2016-11-11

