C++ 利用硬件加速矩阵乘法

点击蓝字

752dd79bccd939e66cb1814358655a1b.png

关注我们

来源于网络,侵删

1.矩阵乘法定义

d5cba5128a6c5af51869d13df19ba152.png

2.矩阵类封装

我们用 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;
    }
  }
};

9c9520180444575e6f16d0e528d61cfc.png

3.矩阵乘法实现

(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的对应位置中,如下图所示;

39cdda22c8aaf34ba3e9bec66b8b6574.png

(2)ikj 式

对上述算法进行一些改进,交换两个内层循环的位置,得到如下算法:

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 式,对矩阵乘法 A × B = C ,行优先枚举 A的每一个格子,再枚举 B的每一行,分别对应相乘后放入矩阵 C的对应位置中,每次相乘得到的 C 都是部分积,如下图所示,用绿色的深浅来表示这个值是否已经完整求得;

ef23ee11bf4cbff74edded90773e144d.png

(3)kij 式

对上述算法再进行一些改进,交换两个外层循环的位置,得到如下算法:

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];
        }
      }
    }
  }
};

这种方法被称为kij 式,对矩阵乘法 A × B = C ,列优先枚举 A的每一个格子,再枚举 B的每一行,分别对应相乘后放入矩阵 C 的对应位置中,每次相乘得到的 C 都是部分积,如下图所示,用绿色的深浅来表示这个值是否已经完整求得;

a5c5ce8c900dc551d812e332d3c384dc.png

4.时间测试

e5af0eef9b0680b873837c6ceb9137de.png

5.原理分析

  • 原因是因为 CPU 访问内存的速度比 CPU 计算速度慢得多,为了解决速度不匹配的问题,在 CPU 与 内存 之间加了高速缓存cache。高速缓存 cache 的存在大大提高了 CPU 访问数据的速度。但是当内存访问不连续的时候,就会导致 cache 命中率降低,所以为了加速,就要尽可能使内存访问连续,即不要跳来跳去。

  • 矩阵

6.最后结论

运行速度:

ikj≈kij>ijk

 
 
 
 

0e8c6a185ab99fb6264d95d291410797.gif

如果你年满18周岁以上,又觉得学【C语言】太难?想尝试其他编程语言,那么我推荐你学Python,现有价值499元Python零基础课程限时免费领取,限10个名额!
▲扫描二维码-免费领取

302e93d75213d447823ae0022e4c3112.gif

戳“阅读原文”我们一起进步

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值