1.说明
关于MEX文件,官方的解释是:
A MEX file is a function, created in MATLAB, that calls a C/C++ program or a Fortran subroutine. A MEX function behaves just like a MATLAB script or function. To call a MEX function, use the name of the MEX file, without the file extension. The MEX file contains only one function or subroutine. The calling syntax depends on the input and output arguments defined by the MEX function. The MEX file must be on your MATLAB path.
本质上MEX文件是一个动态链接库文件,在MATLAB中调用该方法时,MATLAB会加载该动态链接库文件,然后调用指定入口方法,然后按照既定的格式去处理输入和输出。
2.API说明
2.1 MEX C
在C中,入口方法为
1
2
3
4
5
| // nls: 输出数组的数量
// plhs:输出数组
// nrhs: 输入数组的数量
// prhs: 输入数组
void mexFunction(int nlhs, mxArray *plhs[],int nrhs, const mxArray *prhs[]);
|
使用MEX C时,实测MATLAB的开销比较小,大概在十几个微秒,猜测是MATLAB的矩阵底层数据结构和mxArray基本一致,所以在转换上不会花费太大的开销。
对于传进来的矩阵首先第一步当然是获取它的维度信息了,使用mxGetDimensions可以获取每个维度的大小,不过这个函数有个小瑕疵无法获取具体有多少个维度,要想获取具体维度信息就只能曲线救国,先使用mxGetNumberOfElements获取矩阵中元素总数,然后根据每个维度的大小间接算出有多少个维度。对于常用的二维矩阵,使用mxGetM函数获取行数量,使用mxGetN获取列数量就足矣。
有了矩阵的维度信息,现在就可以开始处理数据了。除非特别声明不然MATLAB传到MEX C中的矩阵都是double类型,使用mxGetDoubles函数即可获取内存指针,此时虽然获取的是一个double类型的指针,但它其实是可以处理多维数据的,因为多维数据在内存中的分布是顺序的,按列存储,行号低的排列在前,高的在后,列号低的排在列号高的前面。由于该数据是MATLAB以共享内存的方式传进来,所以在C函数内修改该数据,MATLAB中的数据也会被修改,除非特别需要,否则不建议直接对该数据进行修改操作,而是使用mxMalloc函数重新分配一块内存,将数据复制到新内存中后进行修改操作。修改完数据后如果需要将数据传回到MATLAB中时,则需要使用mxCreate系列函数先给plhs[x]先创建指针,然后使用mxSet系列函数将数据指针赋给plhs[x],此时绝对不可将数据指针使用mexFree释放掉,否则MATLAB运行会出错,因为它将访问已经释放的内存。
对于mxMalloc等动态分配内存的函数分配的动态内存,MATLAB会统一释放,所以无需担心内存会泄漏。
常用的API就以上几个,详细参考 MEX C API
2.2 MEX C++
在C++中,入口方法为:
1
2
3
4
5
6
7
8
9
10
| class MexFunction:public matlab::mex::Function
{
public:
// outputs: 输出数组
// inputs: 输入数组
void operator()(matlab::mex::ArgumentList outputs,matlab::mex::ArgumentList inputs)
{
}
}
|
在MATLAB环境中调用使用MEX C++ API编写的函数时,基本开销大约是50几个微秒,底层数据转换开销相比MEX C 来说比较大,大概是MATLAB中矩阵数据结构和ArgumentList数据结构相差较大,故转换开销也比较大。
ArgumentList其实是Array的一个子类,但是ArgumentsList类可进行的操作太少(仅限于获取维度信息,迭代器),不适合常规使用,所以一般将ArgumentList类型的输入参数转换成Array的另外一个子类TypeArray,这样可以使用各个维度索引来获取具体元素。当需要将矩阵数据输出回MATLAB时,此时需要一个名为ArrayFactory的工厂类,其中的createArray方法可以根据数据集及其维度信息,创建指定维度的 Array 变量。
数据类型说明参考 类型说明
其他API说明参考 API说明
3.例子
3.1 MEX c 获取数据 操作完后返回
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| #include "mex.h"
#include "matrix.h"
#include <stdlib.h>
void mexFunction(int nlhs, mxArray *plhs[],int nrhs, const mxArray *prhs[])
{
// get number of rows
size_t size = mxGetM(prhs[0]);
// get data pointer of array
mxComplexDouble *input = mxGetComplexDoubles(prhs[0]);
// new some memory, it is n-by-1 Matrix
mxComplexDouble *temp = (mxComplexDouble*)mxMalloc(size * sizeof(mxComplexDouble));
memcpy(temp,input,size * sizeof(mxComplexDouble));
// real = real * 2, image = -1 * image
for(size_t i = 0; i < size; ++i)
{
temp[i].real = temp[i].real * 2;
temp[i].imag = -1 * temp[i].imag;
}
//create the pointer
plhs[0] = mxCreateDoubleMatrix(0,0,mxCOMPLEX);
//set the pointer of outputting
mxSetComplexDoubles(plhs[0],temp);
mxSetM(plhs[0],size);
mxSetN(plhs[0],1);
}
|
3.2 MEX c++ 获取数据 操作并返回
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| #include "mex.hpp"
#include "mexAdapter.hpp"
#include <complex>
using namespace std;
using namespace matlab::data;
using matlab::mex::ArgumentList;
typedef std::complex<double> Complex;
class MexFunction:public matlab::mex::Function
{
public:
void operator()(matlab::mex::ArgumentList outputs,matlab::mex::ArgumentList inputs)
{
ArrayFactory factory;
// create TypedArray
TypedArray<Complex> input = std::move(inputs[0]);
// get dimensions
ArrayDimensions dims = input.getDimensions();
// cache the data of inputting
std::vector<Complex> items(input.begin(),input.end());
// real = real * 2 , image = image * -1
for(auto &item: items)
{
item.real(item.real() * 2 );
item.imag(item.imag() * -1);
}
// create the output Array from cache vector
outputs[0] = factory.createArray(dims,items.begin(),items.end());
}
};
|