c++矩阵计算性能对比:Eigen和GPU解读

 更新时间:2022年12月15日 14:55:01   作者:guotianqing  
这篇文章主要介绍了c++矩阵计算性能对比:Eigen和GPU解读,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

生成随机矩阵

生成随机矩阵有多种方式,直接了当的方式是使用显式循环的方式为矩阵的每个元素赋随机值。

#include <iostream>
#include <random>

using namespace std;

// 生成随机数
double GenerateRandomRealValue()
{
    std::random_device rd;
    std::default_random_engine eng(rd());
    std::uniform_real_distribution<double> distr(1, 10);
    return distr(eng);
}

int main()
{
        // 3d矩阵
    double a[3][3];
    for (int i = 0; i < 3; ++i) {
        for (int j = 0;  j < 3; ++j) {
            a[i][j] = GenerateRandomRealValue();
        }
    }

    return 0;
}

另一种方式是使用Eigen库,它提供了矩阵运算的库。

生成随机矩阵:

#include "Eigen/Dense"
#include <functional>

using namespace std;
using namespace Eigen;

MatrixXd Generate2DMatrixByEigen()
{
        // 直接使用内置的Random,产生均匀分布随机矩阵
    MatrixXd m = MatrixXd::Random(3,3);
    
    // 也可以调用自定义的随机数生成函数填充数据
    // MatrixXd m = MatrixXd::Zero(3,3).unaryExpr(std::bind(GenerateRandomRealValue));
    return m;
}

计算矩阵点积

使用显式循环计算

直接上代码:

void CalcMatrixDotForLoop(const vector<vector<double>>& a, const vector<vector<double>>& b)
{
    std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();
    if (a[0].size() != b.size()) {
        cout << "error:" << a.size() << "," << b[0].size() << endl;
        return;
    }

    vector<vector<double>> c;
    vector<double> c_row(b[0].size());
    for (int i = 0; i < a.size(); ++i) {
        for (int j = 0; j < b[0].size(); ++j) {
            for (int k = 0; k < b.size(); ++k) {
                c_row[j] += a[i][k] * b[k][j];
            }
        }
        c.emplace_back(c_row);
    }
    std::chrono::high_resolution_clock::time_point t2 = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double, std::milli> time_span = t2 - t1;
    std::cout << "Loop takes " << time_span.count() << " ms\n";

    // cout << "matrix c:\n";
    // for (int i = 0; i < c.size(); ++i) {
    //     for (int j = 0; j < c[0].size(); ++j) {
    //         cout << c[i][j] << ",";
    //     }
    //     cout << endl;
    // }
}

使用Eigen库

代码:

void ModeEigen(const int a_row, const int a_col, const int b_row, const int b_col)
{
    std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();
    auto c = a * b;
    std::chrono::high_resolution_clock::time_point t2 = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double, std::milli> time_span = t2 - t1;
    std::cout << "Eigen takes " << time_span.count() << " ms\n";
    // cout << "matrix c:\n" << c << endl;
}

使用GPU

代码片断:

auto t_begin = std::chrono::high_resolution_clock::now();

t1 = std::chrono::high_resolution_clock::now();
cudaMalloc((void**)&da,size);
cudaMalloc((void**)&db,size);
cudaMalloc((void**)&dc,size);
t2 = std::chrono::high_resolution_clock::now();
time_span = t2 - t1;
std::cout << "GPU malloc takes " << time_span.count() << " ms\n";

t1 = std::chrono::high_resolution_clock::now();
cudaMemcpy(da,a,size,cudaMemcpyHostToDevice);
cudaMemcpy(db,b,size,cudaMemcpyHostToDevice);
t2 = std::chrono::high_resolution_clock::now();
time_span = t2 - t1;
std::cout << "cudaMemcpy takes " << time_span.count() << " ms\n";

t1 = std::chrono::high_resolution_clock::now();
dim3 dg(32,32);
dim3 dbs((n+dg.x-1)/dg.x,(n+dg.y-1)/dg.y);
mextix<<<dbs,dg>>>(da,db,dc,n);
t2 = std::chrono::high_resolution_clock::now();
time_span = t2 - t1;
std::cout << "gpu takes " << time_span.count() << " ms\n";

t1 = std::chrono::high_resolution_clock::now();
cudaMemcpy(c,dc,size,cudaMemcpyDeviceToHost);
t2 = std::chrono::high_resolution_clock::now();
time_span = t2 - t1;
std::cout << "cudaMemcpy back takes " << time_span.count() << " ms\n";

cudaFree(da);
cudaFree(db);
cudaFree(dc);

auto t_end = std::chrono::high_resolution_clock::now();
time_span = t_end - t_begin;
std::cout << "GPU total takes " << time_span.count() << " ms\n";

结果分析

经过测试,得到以下结论:

  • 对于CPU上矩阵运算来说,使用Eigen远远优于显式循环(我只使用了单线程,你当然可以尝试多线程,但程度复杂度会明显上升)
  • 对于小规模矩阵来说,Eigen库要快于GPU(数据在host和device之间的拷贝消耗了大量的时间)
  • 对于较大规模矩阵来说,GPU的优势才显现出来(数据运算时间超过了拷贝耗时,运算量越大,GPU并行的优势也越明显)

总之:

  • 绝对避免使用显式循环,使用Eigen库
  • 对于一般的应用来说,使用Eigen库足够应付大多数场景,毕竟CPU机器要比GPU机器廉价且普遍
  • 对于涉及大量的矩阵运算,包括机器学习等,GPU才是真正的用武之地

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Matlab实现绘制玫瑰线的示例代码

    Matlab实现绘制玫瑰线的示例代码

    这篇文章主要为大家介绍了如何利用Matlab绘制3好看的玫瑰线,文中的示例代码讲解详细,对我们学习Matlab有一定帮助,感兴趣的小伙伴可以动手试一试
    2022-08-08
  • C++基于Floyd算法实现校园导航系统

    C++基于Floyd算法实现校园导航系统

    这篇文章主要为大家详细介绍了C++基于Floyd算法实现校园导航系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03
  • C++中的Switch 语句详情

    C++中的Switch 语句详情

    在日常的开发当中,我们经常会遇到一种情况,我们用一个变量表示状态。比如关闭-激活-完成,当我们需要判断状态的时候,就需要罗列if-else语句。今天这篇文章我们就来介绍一下C++ Switch 语句,需要的朋友可以参考一下
    2021-11-11
  • 用C语言实现扫雷小游戏

    用C语言实现扫雷小游戏

    这篇文章主要为大家详细介绍了用C语言实现扫雷小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-06-06
  • 详解C++ 动态内存分配与命名空间

    详解C++ 动态内存分配与命名空间

    这篇文章主要介绍了详解C++ 动态内存分配与命名空间,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-08-08
  • C语言版停车位管理系统

    C语言版停车位管理系统

    这篇文章主要为大家详细介绍了C语言版停车位管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03
  • C语言实现飞机游戏(2)

    C语言实现飞机游戏(2)

    这篇文章主要介绍了C语言实现飞机游戏的第二部分,进行功能完善,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-05-05
  • 深入理解C++的动态绑定与静态绑定的应用详解

    深入理解C++的动态绑定与静态绑定的应用详解

    本篇文章是对C++中的动态绑定与静态绑定进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • 解析C++类内存分布

    解析C++类内存分布

    本篇文章介绍了C++类内存分布结构,我们来看看编译器是怎么处理类成员内存分布的,特别是在继承、虚函数存在的情况下
    2021-06-06
  • c语言求余数的实例讲解

    c语言求余数的实例讲解

    在本篇文章里小编给大家整理的是关于c语言如何求余数的相关知识点内容,有需要的朋友们可以学习下。
    2020-02-02

最新评论