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照样能够编译 - -