矩阵相乘_重排序优化算法的C++实现

都知道计算机执行矩阵相乘运算很麻烦,使用传统数学算法的时间复杂度是O(n^3)。

这里不讨论数学上的优化算法。在计算机计算时有个cache-miss问题,这里讨论一下。

C++里的矩阵,其实是二维数组。在存储的时候是按行存的,cache在读取的时候,也是按行取的(下面的代码可以证明)。如果按照正常算法执行矩阵相乘,依次计算新矩阵每个位置的结果,用第一个矩阵的行去乘第二个矩阵的列,然后累加求和,那第二个矩阵每次运算都跨行了,这涉及到3层循环,第1层表示行,第2层表示列,第3层表示累加。那第3层循环里,每次都会有cache-miss。

如果能设计一个算法,让第3层循环的不要跨行,而是一直在执行某一行的计算,该行的所有元素都取完了再去执行下一行,那就可以避免cache-miss了。

这里给出3组方法,第1种是原始的方法。第2和第3种方法调整了循环顺序。

第二个方法是重排序V1版本,第三种方法是重排序的V2版本。

2个版本的区别是:最内层循环的读取矩阵的顺序,V1是逐行读取,V2是逐列读取。

cpp 复制代码
#include <iostream>
#include <vector>
#include <chrono>

using namespace std;

void matrixMultiplyReorderedV2(const vector<vector<int>>& A, const vector<vector<int>>& B, vector<vector<int>>& C) {
    int m = A.size();
    int n = B.size();
    int p = B[0].size();
    
    for (int k = 0; k < n; k++) {
        for (int j = 0; j < p; j++) {
            for (int i = 0; i < m; i++) {
                C[i][j] += A[i][k] * B[k][j];
            }
        }
    }
}

void matrixMultiplyReorderedV1(const vector<vector<int>>& A, const vector<vector<int>>& B, vector<vector<int>>& C) {
    int m = A.size();
    int n = B.size();
    int p = B[0].size();
    
    for (int k = 0; k < n; k++) {
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < p; j++) {
                C[i][j] += A[i][k] * B[k][j];
            }
        }
    }
}

void clearVecC(std::vector< vector<int> >& C)
{
    for (int i = 0; i < C.size(); i++) {
        for (int j = 0; j < C[0].size(); j++) {
            C[i][j] = 0;
        }
    }
}

void matrixMultiplyOriginal(const vector<vector<int>>& A, const vector<vector<int>>& B, vector<vector<int>>& C) {
    int m = A.size();
    int n = B.size();
    int p = B[0].size();
    
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < p; j++) {
            for (int k = 0; k < n; k++) {
                C[i][j] += A[i][k] * B[k][j];
            }
        }
    }
}

int main() {
    int sz = 1000;
    int m = sz, n = sz, p = sz; // 矩阵大小
    vector<vector<int>> A(m, vector<int>(n, 1));
    vector<vector<int>> B(n, vector<int>(p, 1));
    vector<vector<int>> C(m, vector<int>(p, 0));

    auto start = chrono::high_resolution_clock::now();
    matrixMultiplyOriginal(A, B, C);
    auto end = chrono::high_resolution_clock::now();

    chrono::duration<double> duration = end - start;
    cout << "Original version time: " << duration.count() << " seconds" << endl;

    clearVecC(C);
    start = chrono::high_resolution_clock::now();
    matrixMultiplyReorderedV1(A, B, C);
    end = chrono::high_resolution_clock::now();

    duration = end - start;
    cout << "matrixMultiplyReorderedV1 version time: " << duration.count() << " seconds" << endl;

    clearVecC(C);
    start = chrono::high_resolution_clock::now();
    matrixMultiplyReorderedV2(A, B, C);
    end = chrono::high_resolution_clock::now();


    duration = end - start;
    cout << "matrixMultiplyReorderedV1 version time: " << duration.count() << " seconds" << endl;

    return 0;
}

运行结果是:

bash 复制代码
Original version time: 22.9585 seconds
matrixMultiplyReorderedV1 version time: 12.499 seconds
matrixMultiplyReorderedV1 version time: 25.3616 seconds

可以看到,V1版本明显缩短了运算时间,减少了cache-miss。V2版本没效果,反而比原始版本的还差一点。这可以说明:cache是按行读取内存的矩阵的,而不是列。

另外,使用多线程可以缩短计算时间,我在另一篇文章(多线程实现矩阵相乘_C++)里有详细说明。

相关推荐
YLCHUP8 小时前
【思维&图论建模】题解:债权(debt)_图论_建模_思路转化_思维题目_C++算法竞赛
c++·笔记·算法·数学建模·贪心算法·深度优先·图论
xiaoye-duck8 小时前
《C++ 项目:手写大模型接入 SDK》会话模块整合:内存 SessionManager 与 DataManager 持久化联动
开发语言·数据库·c++·语言模型
思麟呀8 小时前
背包DP(01背包+完全背包)
c++·算法
笑容掩饰我的失落10 小时前
区块链从小白到精通(七):Solidity 语法精讲——数据类型、函数、修饰器与事件
算法·区块链·php·智能合约·哈希算法
旖旎夜光10 小时前
【C++ 基础入门】C++11 新特性(中):右值引用、移动语义与完美转发
开发语言·c++·笔记·学习
ShineWinsu10 小时前
对于Qt:QTabWidget常见属性的解析
开发语言·数据库·c++·qt·浏览器·qtabwidget·标签页
Pniubi10 小时前
力扣24. 两两交换链表中的节点(递归)
算法·leetcode·链表
꧁细听勿语情꧂11 小时前
直接选择排序,堆排序,冒泡排序,递归快速排序、hoare版本、挖坑法、lomuto前后指针,非递归快速排序
c语言·开发语言·数据结构·算法
俊俊几年的12 小时前
动态规划(Dynamic Programming)详解:从入门到 LeetCode Hot 100
linux·c++·算法·leetcode·嵌入式·动态规划·c
小雉系统21 小时前
BcoreOS安全通信架构:FsSession 抗算法后门安全设计
算法·安全·架构