随着计算机软硬件水平的不断提升,64位操作系统逐步开始普及,作为数值计算领域著名的数学计算工具,Matlab也在逐步提高自身的兼容性。另一方面,作为软件编程领域的重要语言,C或者C++在运行性能上有着明显的优势,因此在Matlab中调用C++和C成了聪明的人在实际运行中的通常做法。
Matlab调用C或者C++其实不难,其核心是对于一些mexFunction的理解,这也不是本问关注的内容。这里,笔者只是想讨论如何把32位系统下编译的C或者C++程序移植到64位系统中,首先请看一个例子。
假设我们要快速生成一个给定大小的Hilbert矩阵,要求用C或者C++写,然后在matlab中调用。因此,可以写出该算法的C代码如下:
/*
Create the n*n Hilbert Matrix
*/
# include "mex.h"
void hilb(double *y,int n)
{
int i,j;
for(i=0;i<n;i++)
for(j=0;j<n;j++)
*(y+j+i*n)=1/((double)i+(double)j+1);
}
void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray *prhs[])
{
//x is the input param,y is the pointer to the matrix create in c
double x,*y;
//the size of the matrix
int n;
//only one param for input is allowed
if (nrhs!=1)
mexErrMsgTxt("One inputs required.");
//only one param for output is allowed
if (nlhs != 1)
mexErrMsgTxt("One output required.");
//the param inout must be a scaler which type is double
if (!mxIsDouble(prhs[0])||mxGetN(prhs[0])*mxGetM(prhs[0])!=1)
mexErrMsgTxt("Input must be scalars.");
//get the size of matrix we want to create
x=mxGetScalar(prhs[0]);
//create a x*x matrix,the element's datatype is real double,
//and the matrix's pointer is associated with the firt output param.
plhs[0]=mxCreateDoubleMatrix(x,x,mxREAL);
//get the row numbers of the created matrix
n=mxGetM(plhs[0]);
//get the pointer of the created matrix
y=mxGetPr(plhs[0]);
//call subfunction
hilb(y,n);
}
以上内容命名为hilbertc.c文件,然后保存到matlab的当前目录下,运行指令
>>mex hilbertc.c
在同路径下会生成该函数的mex文件hilbertc.mexw32,然后我们在matlab中调用这个函数,代码如下:
>> A=hilbertc(10)
A =
1.0000 0.5000 0.3333 0.2500 0.2000 0.1667 0.1429 0.1250 0.1111 0.1000
0.5000 0.3333 0.2500 0.2000 0.1667 0.1429 0.1250 0.1111 0.1000 0.0909
0.3333 0.2500 0.2000 0.1667 0.1429 0.1250 0.1111 0.1000 0.0909 0.0833
0.2500 0.2000 0.1667 0.1429 0.1250 0.1111 0.1000 0.0909 0.0833 0.0769
0.2000 0.1667 0.1429 0.1250 0.1111 0.1000 0.0909 0.0833 0.0769 0.0714
0.1667 0.1429 0.1250 0.1111 0.1000 0.0909 0.0833 0.0769 0.0714 0.0667
0.1429 0.1250 0.1111 0.1000 0.0909 0.0833 0.0769 0.0714 0.0667 0.0625
0.1250 0.1111 0.1000 0.0909 0.0833 0.0769 0.0714 0.0667 0.0625 0.0588
0.1111 0.1000 0.0909 0.0833 0.0769 0.0714 0.0667 0.0625 0.0588 0.0556
0.1000 0.0909 0.0833 0.0769 0.0714 0.0667 0.0625 0.0588 0.0556 0.0526
程序执行成功。以上全部是基于32位的matlab.如果把上述程序放在64位版本的matlab下运行,结果会怎么样呢?64位的matlab能兼容32位matlab所编译出的mex程序吗?
实践是回答问题的最好方法,现在我们把hilbertc.mexw32拷贝到64位Matlab的机器下,执行以下代码:
>> A=hilbertc(10)
Undefined function 'hilbertc' for input arguments of type 'double'.
结果是执行出错,其原因是由于32位字长下double类型与64位下double在内存中所占的字节数不同,导致系统在参数传递时发生匹配错误。对于这个问题,我们很好解决,只要在64位平台下重新编译C或者C++源代码文件即可。代码如下
>> mex hilbertc.c
>> A=hilbertc(10)
A =
1.0000 0.5000 0.3333 0.2500 0.2000 0.1667 0.1429 0.1250 0.1111 0.1000
0.5000 0.3333 0.2500 0.2000 0.1667 0.1429 0.1250 0.1111 0.1000 0.0909
0.3333 0.2500 0.2000 0.1667 0.1429 0.1250 0.1111 0.1000 0.0909 0.0833
0.2500 0.2000 0.1667 0.1429 0.1250 0.1111 0.1000 0.0909 0.0833 0.0769
0.2000 0.1667 0.1429 0.1250 0.1111 0.1000 0.0909 0.0833 0.0769 0.0714
0.1667 0.1429 0.1250 0.1111 0.1000 0.0909 0.0833 0.0769 0.0714 0.0667
0.1429 0.1250 0.1111 0.1000 0.0909 0.0833 0.0769 0.0714 0.0667 0.0625
0.1250 0.1111 0.1000 0.0909 0.0833 0.0769 0.0714 0.0667 0.0625 0.0588
0.1111 0.1000 0.0909 0.0833 0.0769 0.0714 0.0667 0.0625 0.0588 0.0556
0.1000 0.0909 0.0833 0.0769 0.0714 0.0667 0.0625 0.0588 0.0556 0.0526
问题如果只进行到这里,那就太肤浅了,我今天的目地在于介绍一种充分利用64位matlab的精度和容量优势,提高其运算量的方法。
众所周知,能安装64位matlab的机器一般配置都是比较高的,特别是内存容量比较大,比较适合处理大容量数据。上述问题的解决方法,只是让64位系统以一种兼容模式运行32位C程序,其本质是以好兼容性牺牲性能。下面介绍一种在保证兼容性的条件下,充分发挥matlab处理大容量数据的方法。总结一下,可以表述为如下问题:
How do I update MEX-files to use the large array handling API (-largeArrayDims)?
如何更改Mex文件以使用处理大容量数组的API?
由于传统的C或者C++语言历史久远,其盛行与16位系统与32位系统期间,当计算机进入64位时,软件支持仍以32位时的标准为模版,这就是为什么我们的入口函数通常是以int作为数组下标和元素个数的索引的原因。在32位系统中,int占4个字节,最多能表示2^32-1个数,可是随着时代的发展,人们处理的问题越来越复杂,处理的数据量越来越大。2^32-1个数肯定在某些行业已经不够用了,然而matlab与C++之间的接口是以32位系统作为标准的,这就导致了人们在处理大容量数据时没办法利用C和C++语言的速度优势,而往往对于大数据量来说,运算速度是很关键的。
因此,如何更改接口使得matlab与C++之间的接口能处理大容量数据,成为了迫切需要解决的问题。
对于这个问题,其解决方案如下:
1.检查自己的代码,将那些指代矩阵索引的下标整数的类型改为 mwSize 或者mwIndex,在matlab的头文件中,已经将 mwSize 和mwIndex重定义为了size_t 类型,而C++中size_t是64位的,这就保证了我们的接口是基于64位的,从而就可以处理 大容量数据了。
2.第一步骤只是给了我们表示和索引大数据量的能力,如何处理这些大数据量还需要进行第二部。在matlab和C++之间的接口函数是基于众多mex函数和mx函数组成的API函数集,要使用这些函数集对我们的大容量数据进行处理,我们必须保证传递到这些函数中的参数必须是大容量的。因此这一步骤,我们主要寻找那些调用mx*和mex*开头的函数,然后查看其传入函数,如果函数院校为int,则将int改为 mwSize 或者mwIndex。
matlab的接口头文件中,定义的以下函数可以对大容量数组进行处理:
mxCalcSingleSubscript | mxCreateSparseLogicalMatrix2 |
mxCalloc | mxCreateStructArray |
mxCopyCharacterToPtr1 | mxCreateStructMatrix |
mxCopyComplex16ToPtr1 | mxGetCell |
mxCopyComplex8ToPtr1 | mxGetDimensions |
mxCopyInteger1ToPtr1 | mxGetElementSize |
mxCopyInteger2ToPtr1 | mxGetField |
mxCopyInteger4ToPtr1 | mxGetFieldByNumber |
mxCopyPtrToCharacter1 | mxGetIr |
mxCopyPtrToComplex161 | mxGetJc |
mxCopyPtrToComplex81 | mxGetM |
mxCopyPtrToInteger11 | mxGetN |
mxCopyPtrToInteger21 | mxGetNumberOfDimensions |
mxCopyPtrToInteger41 | mxGetNumberOfElements |
mxCopyPtrToPtrArray1 | mxGetNzmax |
mxCopyPtrToReal41 | mxGetProperty |
mxCopyPtrToReal81 | mxGetString |
mxCopyReal4ToPtr1 | mxMalloc |
mxCopyReal8ToPtr1 | mxRealloc |
mxCreateCellArray | mxSetCell |
mxCreateCellMatrix | mxSetDimensions |
mxCreateCharArray | mxSetField |
mxCreateCharMatrixFromStrings | mxSetFieldByNumber |
mxCreateDoubleMatrix | mxSetIr |
mxCreateLogicalArray2 | mxSetJc |
mxCreateLogicalMatrix2 | mxSetM |
mxCreateNumericArray | mxSetN |
mxCreateNumericMatrix | mxSetNzmax |
mxCreateSparse | mxSetProperty |
上标1表示只对Fortan语言有效。
上标2表示只对C语言有效
按照以上思路,对C或者C++源代码进行更改后,就可以编译了。编译时和常规编译不同,需要附件大容量编译的参数。具体如下:
>>mex -largeArrayDims hilbertc .c
这样生成的mex程序文件就可以支持大容量的数组和矩阵,并能在程序中对这些数据和矩阵进行处理了。