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

目录
  • 生成随机矩阵
  • 计算矩阵点积
    • 使用显式循环计算
    • 使用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才是真正的用武之地

总结

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

(0)

相关推荐

  • Visual Studio下Eigen库环境配置方式

    目录 引言 一.Eigen下载地址 二.Visual Studio环境配置 三.代码测试 总结 引言 Eigen是一款C++开源库,里面包含线性代数.矩阵.矢量运算.数据分析等相关算法库. 本文主要介绍如何使用Visual Studio编译安装Eigen3. 一.Eigen下载地址 Eigen库地址下载 下载后解压,得到源文件. 二.Visual Studio环境配置 VS新建一个空项目 打开属性管理窗口(“视图->其他窗口->属性管理”) 在对应的调试模式下,添加新的项目属性表,并编辑属性

  • C++ Eigen库计算矩阵特征值及特征向量

    本文主要讲解利用Eigen库计算矩阵的特征值及特征向量并与Matlab计算结果进行比较. C++Eigen库代码 #include <iostream> #include <Eigen/Dense> #include <Eigen/Eigenvalues> using namespace Eigen; using namespace std; void Eig() { Matrix3d A; A << 1, 2, 3, 4, 5, 6, 7, 8, 9; c

  • C++读入"N,X,Y,Z"格式文本文件到Eigen3 Matrix

    C++读入"N,X,Y,Z"格式文本文件到Eigen3 Matrix,以及相同格式输出方法 很多数据资料的格式类似这样: 1,-2085738.7757,5503702.8697,2892977.6829 2,-2071267.5135,5520926.7235,2883341.8135 3,-2079412.5535,5512450.8800,2879771.2119 4,-2093693.1744,5511218.2651,2869861.8947 5,-2113681.5062,

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

    目录 生成随机矩阵 计算矩阵点积 使用显式循环计算 使用Eigen库 使用GPU 结果分析 总结 生成随机矩阵 生成随机矩阵有多种方式,直接了当的方式是使用显式循环的方式为矩阵的每个元素赋随机值. #include <iostream> #include <random> using namespace std; // 生成随机数 double GenerateRandomRealValue() {     std::random_device rd;     std::defau

  • 小白入门篇使用Python搭建点击率预估模型

    点击率预估模型 0.前言 本篇是一个基础机器学习入门篇文章,帮助我们熟悉机器学习中的神经网络结构与使用. 日常中习惯于使用Python各种成熟的机器学习工具包,例如sklearn.TensorFlow等等,来快速搭建各种各样的机器学习模型来解决各种业务问题. 本文将从零开始,仅仅利用基础的numpy库,使用Python实现一个最简单的神经网络(或者说是简易的LR,因为LR就是一个单层的神经网络),解决一个点击率预估的问题. 1.假设一个业务场景 声明:为了简单起见,下面的一切设定从简-. 定义需

  • pytorch中Tensor.to(device)和model.to(device)的区别及说明

    目录 Tensor.to(device)和model.to(device)的区别 区别所在 举例 pytorch学习笔记--to(device)用法 这段代码到底有什么用呢? 为什么要在GPU上做运算呢? .cuda()和.to(device)的效果一样吗?为什么后者更好? 如果你有多个GPU Tensor.to(device)和model.to(device)的区别 区别所在 使用GPU训练的时候,需要将Module对象和Tensor类型的数据送入到device.通常会使用 to.(devic

  • 关于pytorch多GPU训练实例与性能对比分析

    以下实验是我在百度公司实习的时候做的,记录下来留个小经验. 多GPU训练 cifar10_97.23 使用 run.sh 文件开始训练 cifar10_97.50 使用 run.4GPU.sh 开始训练 在集群中改变GPU调用个数修改 run.sh 文件 nohup srun --job-name=cf23 $pt --gres=gpu:2 -n1 bash cluster_run.sh $cmd 2>&1 1>>log.cf50_2GPU & 修改 –gres=gpu:

  • 详解python中GPU版本的opencv常用方法介绍

    引言 本篇是以python的视角介绍相关的函数还有自我使用中的一些问题,本想在这篇之前总结一下opencv编译的全过程,但遇到了太多坑,暂时不太想回看做过的笔记,所以这里主要总结python下GPU版本的opencv. 主要函数说明 threshold():二值化,但要指定设定阈值 blendLinear():两幅图片的线形混合 calcHist() createBoxFilter ():创建一个规范化的2D框过滤器 canny边缘检测 createGaussianFilter():创建一个Ga

  • CPU,GPU,DPU,TPU,NPU,BPU等处理器的性能及概念

    目录 1.CPU 2.GPU 3.TPU 4.NPU 5.ASIC 6.BPU(Brain Processing Unit,大脑处理器) 7. DPU(Deep learning Processing Unit, 即深度学习处理器) 1.CPU CPU( Central Processing Unit, 中央处理器)就是机器的"大脑",也是布局谋略.发号施令.控制行动的"总司令官". CPU的结构主要包括运算器(ALU, Arithmetic and Logic U

  • 关于Python的GPU编程实例近邻表计算的讲解

    目录 技术背景 加速场景 基于Numba的GPU加速 总结概要 技术背景 GPU加速是现代工业各种场景中非常常用的一种技术,这得益于GPU计算的高度并行化.在Python中存在有多种GPU并行优化的解决方案,包括之前的博客中提到的cupy.pycuda和numba.cuda,都是GPU加速的标志性Python库.这里我们重点推numba.cuda这一解决方案,因为cupy的优势在于实现好了的众多的函数,在算法实现的灵活性上还比较欠缺:而pycuda虽然提供了很好的灵活性和相当高的性能,但是这要求

  • Python3实现打格点算法的GPU加速实例详解

    目录 技术背景 打格点算法实现 打格点算法加速 总结概要 技术背景 在数学和物理学领域,总是充满了各种连续的函数模型.而当我们用现代计算机的技术去处理这些问题的时候,事实上是无法直接处理连续模型的,绝大多数的情况下都要转化成一个离散的模型再进行数值的计算.比如计算数值的积分,计算数值的二阶导数(海森矩阵)等等.这里我们所介绍的打格点的算法,正是一种典型的离散化方法.这个对空间做离散化的方法,可以在很大程度上简化运算量.比如在分子动力学模拟中,计算近邻表的时候,如果不采用打格点的方法,那么就要针对

  • C++实现矩阵对称正交化的示例代码

    1.python代码 import numpy as np import pandas as pd df=pd.DataFrame() df['fac_01']=(34, 45, 65) df['fac_02']=(56, 25, 94) print(df) print('------------------矩阵的特征跟D.和特征向量U-----------------------') D,U=np.linalg.eig(np.dot(df.T, df)) # 求矩阵的特征跟D.和特征向量U p

随机推荐