MATLAB+C+Openmp混合编程(3)-mex.h基础

mex.h基础
mex.h是库文件,刚接触混编的童鞋要清楚C/C++如果引入了mex.h文件将会导致C/C++项目无法直接运行,必须通过mex文件的编译命令将引入了mex.h的C/C++项目编译为mex文件,然后通过MATLAB调用。
题主推荐先在code::blocks中完成纯C/C++的项目编写,测试无误后,MATLAB中直接打开编写的纯C/C++项目文件,然后将mex.h库文件引入,进一步为项目添加接口函数。
案例代码1-将MATLAB矩阵传递给C/C++,观察MATLAB的矩阵在C/C++中的排布方式

#include “mex.h”
#include “matrix.h”
void mexFunction(int nlhs, mxArray plhs[], int nrhs, mxArray* prhs[])
{
double* input;
input = mxGetPr(prhs[0]);
printf(“矩阵的第一行第一列元素的地址%p\n”, input);
size_t M,N;
int i,j;
M = mxGetM(prhs[0]);
N = mxGetN(prhs[0]);
printf(“矩阵的行数%d\n”, M);
printf(“矩阵的列数%d\n”, N);
printf(“第一行第一列值%f\n”, *input);
printf(“第二行第一列值%f\n”, *(input +1));
printf(“第三行第一列值%f\n”, *(input + 2));
printf(“第四行第一列值%f\n”, *(input + 3));
printf(“第一行第二列值%f\n”, (input + 4));
printf(“第二行第二列值%f\n”, (input + 5));
printf(“第三行第二列值%f\n”, (input + 6));
printf(“第四行第二列值%f\n”, (input + 7));
i=2;
j=3;
printf(“第%d行第%d列值为%f\n”,i,j,(input+(M(j-1))+(i-1)));
printf(“第一行第一列值可以表示为f\n”, i, j, (input + (M * (j - 1)) + (i - 1)));
}

matrix.h本身是基础矩阵运算库,但是MATLAB中有些mex函数功能也涉及到这个库了,所以推荐也引入。
完成如上代码编写后,进行mex文件编译即可,因为上面代码引入的头文件没有第三方库,因此编译时候直接在MATLAB中输入
mex main.cpp
这里main.cpp是指接口函数所在的项目文件名
如果有的人编写的时候采用了自编的头文件
那么编译命令变为
mex header.cpp main.cpp
注意header.cpp中已经引入了header.h文件,在mex编译时不需要输入header.h,只要确保main.cpp中引入了header.cpp和header.h,header.cpp中引入了header.h即可。
mex编译完成后会在MATLAB当前工作路径下生产一个main.mex文件,接下来直接在MATLAB中调用mex文件即可
MATLAB中生成一个随机数矩阵
A=rand(5,5)
main(A)
运行后可以看到MATLAB工作窗口中输入了上面代码的相关内容。
题主花了一定篇幅去分析MATLAB矩阵在C/C++中的索引排布方式,因为这个真的很基础且重要,其实包括Python和MATLAB在内的多维矩阵都可以看做一维数组,将一个多维数组排布为一维数组,通过索引计算公式就能够实现定位。
代码分析-输入参数获取
double input;
input = mxGetPr(prhs[0]);
注意mxGetPr获取的值全部默认为double类型,如果想获取整型数据应该采用如下命令
int k;
k = (int)mxGetScalar(prhs[1]);
prhs[0]代表MATLAB输入的参数,0代表第一个输入参数,以此类推
prhs[i]代表MATLAB输入的第i+1个参数
代码分析-printf与mexPrintf
值的注意的是printf在mex编译后不一定能够正常运行,推荐采用mexPrintf进行debug,当然mexPrintf啥的来debug确实很低端,但是printf大法最为直接!
mexPrintf(“M_ZBD IS %d\n”,M_zbd);
最后补充一句,如果后续采用并行编程,例如OPENMP进行CPU并行编程,printf和mexPrintf就不可以用于debug了,在并行区内printf会导致MATLAB报错!
代码分析-矩阵维度获取
二维矩阵获取的代码如下
M = mxGetM(prhs[0]);
N = mxGetN(prhs[0]);
这里获取了第一个输参数的行数和列数。
三维或者更高维度矩阵维度

#define E_IN prhs[0]
const mwSize *dim_array;
dim_array = mxGetDimensions(E_IN);
int X = *dim_array;
int Y = *(dim_array+1);
int Z = *(dim_array+2);

dim_array本质上是存储了多个维度值的指针
案例代码2-输入一个矩阵判断每个元素的值返回新矩阵

#include "mex.h"
// 使用MEX必须包含的头文件
double discrete(double d);
void mexFunction(int nlhs, mxArray* plhs[],
    int nrhs, const mxArray* prhs[]) {
#define A_IN  prhs[0]
#define B_OUT  plhs[0]
    if (nrhs != 1)
        mexErrMsgTxt("Wrong number of input arguments.\n");
    // 检查输入变量数量是否正确,否则报错
    if (nlhs > 1)
        mexErrMsgTxt("Too many output argumnents.\n");
    // 检查输出变量数量是否正确,否则报错
    int M = mxGetM(A_IN);
    int N = mxGetN(A_IN);//mxGETN和mxGETM就是获取矩阵维度
    // 得到输入矩阵A的行数和列数
    B_OUT = mxCreateDoubleMatrix(M, N, mxREAL);
    //为输出矩阵B分配存储空间
    double* A = mxGetPr(A_IN);
    double* B = mxGetPr(B_OUT);
    // 取得输入矩阵A和输出矩阵B的数据指针
    for (int i = 0; i < M * N; ++i)
        B[i] = discrete(A[i]);
    // 调用discrete,根据A(i, j)计算B(i, j)
}
double discrete(double d) {
    if (d < 1.0 / 3.0)
        return 0;
    else if (d < 2.0 / 3.0)
        return 1;
    return 2;
}

代码分析-输出参数
#define B_OUT plhs[0]
B_OUT = mxCreateDoubleMatrix(M, N, mxREAL);
double* B = mxGetPr(B_OUT);
通过上面代码就定义了输出矩阵的指针,然后就可以在C/C++内进行运算了
同理plhs[0]代表第一个输出参数,plhs[i]代表第i+1个输出参数
当MATLAB的矩阵已经赋予了运算用的指针,就可以在C/C++内进行计算了。
代码分析-无输出参数情况
如果想通过mex函数修改MATLAB的输入矩阵(例如上述代表中指针A就指向了MATLAB的输入矩阵),那么直接对A[i]进行修改就可以了,MATLAB运行mex函数就可以发现原本的输入矩阵发生了变化
本节通过两个较为基础的代码示例了mex编程的基础,其实只要将MATLAB的参数传递给C/C++,后续的操作完全遵循C/C++编程即可,补充一句
B_OUT = mxCreateDoubleMatrix(M, N, mxREAL)这个创建的矩阵是MATLAB端的MXN二维矩阵,但是在C/C++中B矩阵还是一维数组,只是MATLAB把一个长度为MXN的一维数组又压缩为M行N列的二维数组展现给用户。
案例代码2的MATLAB版本函数

function [ B ] = matlab_code_version( A )
  [M, N] = size(A);
  B = zeros(M, N);
  for i = 1:M
      for j = 1:N
          if A(i, j) < 1/3
              B(i, j) = 0;
          elseif A(i, j) < 2/3
              B(i, j) = 1;
          else
              B(i, j) = 2;
          end
      end
  end
end

有了上面两个代码,一个mex函数文件,一个MATLAB函数代码,我们就可以进行效率对比了

MATRIX_M = 500;
MATRIX_N = 500;
MATRIX_NUMBER = 1200;
data = rand(MATRIX_M, MATRIX_N, MATRIX_NUMBER);
tic
for i = 1:MATRIX_NUMBER
    change(data(:, :, i));
end
toc
tic
for i = 1:MATRIX_NUMBER
    matlab_code_version(data(:, :, i));
end
toc

//这里面我的mex文件名字是change,这个主要是跟你编译mex文件时候.cpp项目文件的名字有关,编译完成后可以自行修改
总结
说到矩阵当然还包括稀疏矩阵,但是稀疏矩阵相关操作涉及高阶算法和复杂的逻辑问题,这一部分就不展开了,本节所有的代码仅针对稠密矩阵,稀疏矩阵的算法属于高阶内容,逻辑过于复杂,不好描述完整,暂不分享。
注意OPENMP的相关指令还是有点多的,但是难度很低,很好上手,相比于CUDA等GPU编程架构来说已经非常简单了,OPENMP的学习推荐一本教材《多核异构并行计算OpenMP4.5 C/C++篇》
这个书看完基本上多数的函数和用法都能掌握,关于异构编程,虽然OPENMP可以和CUDA混合使用,但是题主不太了解,貌似CUDA的效率高于OPENMP(在GPU端上),如果各位编程能力更强的话,推荐直接上手CUDA,反正mex照样能够编译 - -

  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值