Matlab调用Cuda程序

目录

一. 环境配置

1. GPU+VisualStudio+Matlab版本适配性查看

2. Matlab环境配置

二. 使用Matlab编译CUDA工程

1. 建立CUDA工程并编写GPU代码

2. 编写可供Matlab编译的CUDA代码

2.1 包含的头文件

2.2 程序入口函数mexFunction

3. 使用Matlab编译CUDA工程并调用

3.1 mexcuda编译指令 

3.2 调用方法


总结一下之前做的工作,想到哪里写到哪里,以后遇到问题会继续更新~~~~

———————————————————以下为正式内容—————————————————

一. 环境配置

1. GPU+VisualStudio+Matlab版本适配性查看

        查看本机matlab支持的vs版本信息:Matlab安装路径\bin\win64\mexopts

        查看本机Matlab支持哪个版本的GPU:GPU Support by Release- MATLAB & Simulink- MathWorks 中国icon-default.png?t=N7T8https://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 中国icon-default.png?t=N7T8https://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);

  • 6
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值