c++矩陣計(jì)算性能對(duì)比:Eigen和GPU解讀
生成隨機(jī)矩陣
生成隨機(jī)矩陣有多種方式,直接了當(dāng)?shù)姆绞绞鞘褂蔑@式循環(huán)的方式為矩陣的每個(gè)元素賦隨機(jī)值。
#include <iostream>
#include <random>
using namespace std;
// 生成隨機(jī)數(shù)
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庫,它提供了矩陣運(yùn)算的庫。
生成隨機(jī)矩陣:
#include "Eigen/Dense"
#include <functional>
using namespace std;
using namespace Eigen;
MatrixXd Generate2DMatrixByEigen()
{
?? ??? ?// 直接使用內(nèi)置的Random,產(chǎn)生均勻分布隨機(jī)矩陣
? ? MatrixXd m = MatrixXd::Random(3,3);
? ??
? ? // 也可以調(diào)用自定義的隨機(jī)數(shù)生成函數(shù)填充數(shù)據(jù)
? ? // MatrixXd m = MatrixXd::Zero(3,3).unaryExpr(std::bind(GenerateRandomRealValue));
? ? return m;
}計(jì)算矩陣點(diǎn)積
使用顯式循環(huán)計(jì)算
直接上代碼:
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";
結(jié)果分析
經(jīng)過測(cè)試,得到以下結(jié)論:
- 對(duì)于CPU上矩陣運(yùn)算來說,使用Eigen遠(yuǎn)遠(yuǎn)優(yōu)于顯式循環(huán)(我只使用了單線程,你當(dāng)然可以嘗試多線程,但程度復(fù)雜度會(huì)明顯上升)
- 對(duì)于小規(guī)模矩陣來說,Eigen庫要快于GPU(數(shù)據(jù)在host和device之間的拷貝消耗了大量的時(shí)間)
- 對(duì)于較大規(guī)模矩陣來說,GPU的優(yōu)勢(shì)才顯現(xiàn)出來(數(shù)據(jù)運(yùn)算時(shí)間超過了拷貝耗時(shí),運(yùn)算量越大,GPU并行的優(yōu)勢(shì)也越明顯)
總之:
- 絕對(duì)避免使用顯式循環(huán),使用Eigen庫
- 對(duì)于一般的應(yīng)用來說,使用Eigen庫足夠應(yīng)付大多數(shù)場(chǎng)景,畢竟CPU機(jī)器要比GPU機(jī)器廉價(jià)且普遍
- 對(duì)于涉及大量的矩陣運(yùn)算,包括機(jī)器學(xué)習(xí)等,GPU才是真正的用武之地
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
visual studio2019的安裝以及使用圖文步驟詳解
這篇文章主要介紹了visual studio2019的安裝以及使用圖文步驟詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03
C語言拓展實(shí)現(xiàn)Lua sleep函數(shù)
這篇文章主要介紹了C語言拓展實(shí)現(xiàn)Lua sleep函數(shù),本文使用C語言寫出sleep函數(shù),編譯后在Lua中調(diào)用,需要的朋友可以參考下2015-04-04
一文詳解C++子類函數(shù)為什么不能重載父類函數(shù)
這篇文章主要介紹了一文詳解C++子類函數(shù)為什么不能重載父類函數(shù),文章圍繞主題展開詳細(xì)的內(nèi)容戒殺,具有一定的參考價(jià)值,需要的朋友可以參考一下2022-09-09
動(dòng)態(tài)數(shù)組C++實(shí)現(xiàn)方法(分享)
下面小編就為大家?guī)硪黄獎(jiǎng)討B(tài)數(shù)組C++實(shí)現(xiàn)方法(分享)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-05-05
C++新特性詳細(xì)分析基于范圍的for循環(huán)
C++11這次的更新帶來了令很多C++程序員期待已久的for?range循環(huán),每次看到j(luò)avascript,?lua里的for?range,心想要是C++能有多好,心里別提多酸了。這次C++11不負(fù)眾望,再也不用羨慕別家人的for?range了。下面看下C++11的for循環(huán)的新用法2022-04-04

