本文共 1863 字,大约阅读时间需要 6 分钟。
矩阵乘法是线性代数中的一个重要操作,常用于解决实际问题。以下是关于矩阵乘法的实现、优化及性能分析的详细内容。
矩阵乘法是两个矩阵的操作,假设矩阵A(行数为x,列数为y)和矩阵B(行数为u,列数为v),则它们的乘积C的行数为x,列数为v。乘积矩阵C的元素C_{x,v}是A的第x行和B的第v列的元素点积。
在C++中,我们通过二维数组存储矩阵元素。矩阵类包含以下功能:
malloc或new)分配内存,避免栈溢出。矩阵乘法的核心是元素的点积,实现方式各不相同:
ijk方法:循环顺序为i、j、k。
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]; } } }}优点:直观,缺点:内存访问较为分散。
ikj方法:循环顺序为i、k、j。
void Multiply_ikj(const Matrix& other, Matrix& ret) { // assert(m == other.n); ret.Reset(n, other.m); int i, k, j; 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]; } } }}优点:内存访问较为局部,性能较好。
kij方法:循环顺序为k、i、j。
void Multiply_kij(const Matrix& other, Matrix& ret) { // assert(m == other.n); ret.Reset(n, other.m); int k, i, j; 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]; } } }}优点:优化了部分内存访问,缺点:整体性能较低。
通过实际测试,ikj方法的性能优于ijk和kij方法,尤其是在较大的矩阵规模下表现显著。具体测试结果如下:
| 矩阵阶数 | ijk方法 | ikj方法 | kij方法 |
|---|---|---|---|
| 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 |
内存访问的全面性直接影响性能。CPU缓存的缓存层次结构决定了访问局部内存的高效性。当矩阵规模较大时,不同的循环顺序会显著影响内存的访问模式,而影响其是否命中缓存。ikj方法通过调整访问顺序,提高了缓存的利用率。
在实际应用中:
通过合理选择循环顺序,可以显著提升计算效率。
转载地址:http://lijdz.baihongyu.com/