目录
1. GPU+VisualStudio+Matlab版本适配性查看
总结一下之前做的工作,想到哪里写到哪里,以后遇到问题会继续更新~~~~
———————————————————以下为正式内容—————————————————
一. 环境配置
1. GPU+VisualStudio+Matlab版本适配性查看
查看本机matlab支持的vs版本信息:Matlab安装路径\bin\win64\mexopts
查看本机Matlab支持哪个版本的GPU:GPU Support by Release- MATLAB & Simulink- MathWorks 中国https://ww2.mathworks.cn/help/parallel-computing/gpu-support-by-release.html
2. Matlab环境配置
首先在matlab命令行输入
mex -setup C++
使用适合本机的Vs编译Cuda程序
二. 使用Matlab编译CUDA工程
1. 建立CUDA工程并编写GPU代码
1.1 点击VisualStudio图标,点击创建新项目
1.2. 选择CUDA 11.3 Runtime(11.3为本机安装的CUDA ToolKit版本)
1.3. 编写GPU代码。
1.4 几点建议
(1)为了便于确定代码执行的正确性,建议先用一般的c语言标准编写代码并验证正确性 。
(2)编写头文件时,规范使用#ifdef、#define、#endif防止头文件嵌套包含时的重复调用,不建议使用#pragma once,因为#ifdef、#define、#endif受C/C++语言标准支持,不受编译器的限制,而#pragma once受编译器的限制,mexcuda编译器不一定支持#pragma once。
#ifndef _HeadFileName_H // _HeadFileName为当前头文件的名称
#define _HeadFileName_H
/*** 头文件内容 ***/
#endif
(3)在核函数中使用pow函数时,最好使用powf(CUDA内置的加速函数)代替,防止后续Matlab端编译错误
(4)如果在代码中使用了动态并行,需要将属性-> CUDA C/C++ -> Generate Relocatable Device Code设置为 是(-rdc=true)
2. 编写可供Matlab编译的CUDA代码
2.1 包含的头文件
在项目中添加新建项 mexFunction.h,头文件内容如下
#ifndef _mexFunction_H
#define _mexFunction_H
#include "MatlabPath\extern\include\mex.h"
#include "MatlabPath\toolbox\parallel\gpu\extern\include\gpu\mxGPUArray.h"
#endif
MatlabPath是本机Matlab的安装目录,需要注意的是,因为matlab版本的更新,mex和mxGPUArray的文件路径可能与上述不符,此时在Matlab安装目录中搜索mex.h和mxGPUArray.h,将搜索结果所在的路径复制过来即可。
2.2 程序入口函数mexFunction
与c语言不同,使用Matlab编译代码时的程序入口函数为mexFunction函数,函数定义规范为
void mexFunction(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[])
{
/**** 程序内容 ****/
}
文件中需要包含2.1中定义的“mexFunction.h”头文件。
1.接口参数定义
参数名称 | 参数含义 |
nlhs | 输出变量个数 |
plhs | 输入变量指针数组 |
nrhs | 输入变量个数 |
prhs | 输入变量指针数组 |
2.参数传递方法为
(1)获取实数输入变量
//获取实数指针
AfterRvpDataReal = (float*)mxGetPr(prhs[0]);// float或int或double
(2)获取复数输入变量
//获取实部指针
double* real = (double*)mxGetPr(prhs[0]);
//获取虚部指针:
double* imag = (double*)mxGetPi(prhs[0]);
(3)获取字符串输入变量
需要使用mxArrayToString对输入变量类型进行转化。
char* path = mxArrayToString(prhs[5]);
(4)获取矩阵大小
//输入矩阵的行数
row = mxGetM(prhs[0]);
//输入矩阵的列数
column = mxGetN(prhs[0]);
(5)初始化实数输出变量
plhs[0] = mxCreateNumericMatrix(DistanceNum*AzimuthNum, 1, mxSINGLE_CLASS, mxREAL);
real = (float*)mxGetPr(plhs[0]);
plhs[1] = mxCreateNumericMatrix(DistanceNum*AzimuthNum, 1, mxSINGLE_CLASS, mxREAL);
imag = (float*)mxGetPr(plhs[1]);
(6)初始化复数输出变量
plhs[0] = mxCreateNumericMatrix(rawNum, columnNum, mxSINGLE_CLASS, mxCOMPLEX);
real = (float*)mxGetPr(plhs[0]);
imag = (float*)mxGetPi(plhs[0]);
假设是2*5矩阵(rawNum = 2,columnNum = 5)
real[6] = {1,2,3,4,5,6,7,8,9,10};imag[6] = {1,3,5,7,9,11,13,15,17,19}
则传回matlab的矩阵是:
(7)一个例程:
void mexFunction(int nlhs, mxArray *plhs[],
int nrhs, mxArray const *prhs[])
{
double* a = (double*)mxGetPr(prhs[0]);
double* b = (double*)mxGetPi(prhs[0]);
int c = mxGetM(prhs[0]);
double* real;
double* image;
plhs[0] = mxCreateNumericMatrix(2, 5, mxDOUBLE_CLASS, mxCOMPLEX);
real = (double*)mxGetPr(plhs[0]);
image = (double*)mxGetPi(plhs[0]);
for (int i = 0; i < 10; i++)
{
real[i] = i;
image[i] = 2 * i + 1;
}
printf("------------------\n");
printf("%f,%f,%f,%f\n", a[0],a[1],a[2],a[3]);
printf("%f,%f,%f,%f\n", b[0], b[1], b[2], b[3]);
printf("%d\n",c);
printf("------------------");
}
3. 使用Matlab编译CUDA工程并调用
3.1 mexcuda编译指令
Matlab编译CUDA工程使用mexcuda指令,该指令的说明文档在
Compile MEX-function for GPU computation - MATLAB mexcuda - MathWorks 中国https://ww2.mathworks.cn/help/parallel-computing/mexcuda.html接下来对该指令的应用进行简单的说明。
(1)如果工程中有多个源文件option1,option2,……,optionN,则需要输出使用到的所有源文件的路径,此处建议输入文件的完整路径(绝对路径),而非相对路径,例如:
mexcuda E:\Work\cudaSource1.cu E:\Work\cudaSource2.cu E:\Work\cSource1.cpp...
E:\Work\cSource2.cpp
(2)如果文件中使用了动态并行,则需要在对应的文件前面加上-dynamic,例如:
mexcuda -dynamic E:\Work\cudaDynamicSource1.cu ...
E:\Work\cudaNoDynamicSource2.cu ...
-dynamic E:\Work\cudaDynamicSource3.cu ...
E:\Work\cSource1.cpp ...
(3) 如果文件中使用了CUDA库函数(cufft、cublas等),则需要加入库函数路径,格式是
‘库函数完整文件路径’ -l+库函数名称,例如:
mexcuda 'C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.3\lib\x64\cufft.lib' -lcufft ...
-dynamic E:\Work\cudaDynamicSource1.cu ...
E:\Work\cudaNoDynamicSource2.cu ...
-dynamic E:\Work\cudaDynamicSource3.cu ...
E:\Work\cSource1.cpp ...
(4)按回车后,出现MEX已成功完成证明编译成功,此时文件夹中应该出现.mexw64执行文件,该文件名称与mexcuda指令后的第一个文件名保持一致,例如,(3)中生成的文件名称为cudaDynamicSource1.mewx64。
(5) 可能出现的问题以及修正方法
- 调用的子函数中的printf函数无法正常打印内容
解决方法:将printf函数更换为mexPrintf函数,使用时同样需要包含头文件"mex.h"和"mxGPUArray.h"两个头文件,即2.1中定义的“mexFunction.h”头文件。
- 错误信息:无法解析的外部符号 mexFunction
错误使用 mex LINK : error LNK2001: 无法解析的外部符号 mexFunction ConfidencePixelSelection.lib : fatal error LNK1120: 1 个无法解析的外部命令
出现原因:没有编写mexFunction程序入口
改正方法:按照 2. 编写可供Matlab编译的CUDA代码 中的方法编写mexFucntion函数
- 错误信息:undefine reference to '_Z3powfi'
nvlink error : Undefined reference to '_Z3powfi' in 'C:/Users/lenovo/AppData/Local/Temp/mex_374451651535793_20720/MatrixOperation.obj' (target: sm_35)
出现原因:在核函数中使用了cpu函数pow()
改正方法:将核函数中的pow改为powf
- 错误信息:redefine/无法重载仅以返回类型区分的函数
出现原因:头文件重复定义
改正方法:在头文件开头和结尾加入:#ifndef,#define,#endif
- 错误信息:
F:\HSCdownload\Matlab\R2020b\extern\include\matrix.h(756): error C2143: 语法错误: 缺少“)”(在“常数”的前面)
F:\HSCdownload\Matlab\R2020b\extern\include\matrix.h(756): error C2143: 语法错误: 缺少“;”(在“常数”的前面)
F:\HSCdownload\Matlab\R2020b\extern\include\matrix.h(756): error C2059: 语法错误:“常数”
F:\HSCdownload\Matlab\R2020b\extern\include\matrix.h(757): error C2059: 语法错误:“)”
出现原因:具体的咱也不清楚
改正方法:把“mex.h”和“mxGPUArray.h”的头文件包含放在自定义头文件的前面,例如:将下方代码段
-
//标准库部分 #include <stdio.h> #include <stdlib.h> //自定义头文件 #include "a.h" //mexFuntion头文件 #include "F:\HSCdownload\Matlab\R2020b\extern\include\mex.h" #include "F:\HSCdownload\Matlab\R2020b\toolbox\parallel\gpu\extern\include\gpu\mxGPUArray.h"
改为:
//标准库部分 #include <stdio.h> #include <stdlib.h> //mexFuntion头文件 #include "F:\HSCdownload\Matlab\R2020b\extern\include\mex.h" #include "F:\HSCdownload\Matlab\R2020b\toolbox\parallel\gpu\extern\include\gpu\mxGPUArray.h" //自定义头文件 #include "a.h"
-
错误信息
error C2059: 语法错误:“<”
出现原因:出现错误的位置是核函数调用时候的<<< >>>,所以是因为在c/c++文件中调用了核函数
改正方法:对应的文件由.cpp改为.cu即可
正在创建库 preprocess.lib 和对象 preprocess.exp
kernel_function.obj : error LNK2019: 无法解析的外部符号 cufftPlan1d,函数 "void __cdecl double2_signalFFT(struct double2 *,struct
double2 *,int)" (?double2_signalFFT@@YAXPEAUdouble2@@0H@Z) 中引用了该符号
kernel_function.obj : error LNK2019: 无法解析的外部符号 cufftExecZ2Z,函数 "void __cdecl double2_signalFFT(struct double2 *,struct
double2 *,int)" (?double2_signalFFT@@YAXPEAUdouble2@@0H@Z) 中引用了该符号
kernel_function.obj : error LNK2019: 无法解析的外部符号 cufftExecD2Z,函数 "void __cdecl signalFFT(double *,struct double2 *,int)"
(?signalFFT@@YAXPEANPEAUdouble2@@H@Z) 中引用了该符号
kernel_function.obj : error LNK2019: 无法解析的外部符号 cufftExecZ2D,函数 "void __cdecl signalIFFT(struct double2 *,double *,int)"
(?signalIFFT@@YAXPEAUdouble2@@PEANH@Z) 中引用了该符号
kernel_function.obj : error LNK2019: 无法解析的外部符号 cufftDestroy,函数 "void __cdecl double2_signalFFT(struct double2 *,struct
double2 *,int)" (?double2_signalFFT@@YAXPEAUdouble2@@0H@Z) 中引用了该符号
preprocess.mexw64 : fatal error LNK1120: 5 个无法解析的外部命令
出现原因:调用cufft等库时,需要在编译命令里添加库文件
改正方法:以cufft为例,添加如下命令
'C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.3\lib\x64\cufft.lib' -lcufft
3.2 调用方法
调用mexw64文件的方法与函数调用方法相同,但是要注意,如果cuda代码中的参数精度为float、int等,需要先在matlab中进行类型转化,转化方法为:
单精度参数:single(var)
整型参数:int32(var)
将参数传回matlab端后,需要使用gather()函数将结果转化为matlab中的数据类型以便进行后续处理。
% 调用cudaDynamicSource1.mmexw64程序
% cuda代码中matInPutVar1为双精度类型,matInPutVar2为单精度类型,matInPutVar3为整型
cudaInPutVar1 = matInPutVar1;
cudaInPutVar2 = single(matInPutVar2);
cudaInPutVar3 = int32(matInPutVar3);
[cudaOut1, cudaOut2] = cudaDynamicSource1(cudaInPutVar1 , cudaInPutVar2 , cudaInPutVar2 );
% 将cuda输出转化为Matlab的数据类型
matOut1 = double(gather(cudaOut1);
matOut2 = double(gather(cudaOut2);