Matlab是矩陣語言,如果運算可以用矩陣實現,其運算速度非常快。但若運算中涉及到大量循環,Matlab的速度令人難以忍受的。當必須使用for循環且找不到對應的矩陣運算來等效時,可以將耗時長的函數用C語言實現,並編譯成Mex文件,Matlab便可以像調用內建函數一樣調用C編寫的函數。Mex文件其實是一種動態鏈接庫,舊版本Matlab可以直接調用.dll,新版本要調用.mexw32或.mexw64文件。
編譯過程需要C語言編譯器,在Matlab中鍵入mex –setup進行安裝與配置。
MEX文件的源代碼組成:
(1)功能子程序。該過程包含了Mex文件實現計算功能的代碼,是標准的C語言子程序。
(2)入口子程序。該過程提供功能子程序與Matlab之間的接口,以mexFunction函數實現。注意,入口過程的名稱必須是mexFunction,並且包含四個參數,即
void mexFunction(int nlhs,mxArray*plhs[],int nrhs,const mxArray *prhs[]);
nrhs(left hand side): 輸入參數的個數;
prhs是一個輸入數組,其內容為指針,指向mxArray類型的數據(MATLAB中所有數據都是以矩陣的形式mxArray保存的)。
nlhs, plhs含義類似。
具體地,若在Matlab中執行[a,b]=test(c,d,e) ,則nlhs=2, nrhs=3,prhs[0]指向c,prhs[1]指向d,prhs[2]指向e(可以理解為:prhs[0]=&c, prhs[1]=&d, prhs[2]=&e),注意prhs是const指針數組,故不能改變其指向內容;函數返回時將plhs[0],plhs[1]指向的內容賦給a,b(可以理解為a=*plhs[0], b=*plhs[1])。
例:新建add.c,源碼如下:
#include mex.h double add(double x, double y) { return x + y; } void mexFunction(int nlhs,mxArray *plhs[], int nrhs,const mxArray *prhs[]) { double *a; double b, c; plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL); a = mxGetPr(plhs[0]); b = *(mxGetPr(prhs[0])); c = *(mxGetPr(prhs[1])); *a = add(b, c); }
將add.c拷貝至Matlab當前目錄,執行mex add.c,生成add.mexw64,該文件實現求和功能。此時便可在Matlab中調用該函數:
>> output = add(1.1, 2.2);
分析:
#include mex.h
Mex源文件必須包含mex.h,該頭文件提供了大量Matlab與C(或Fortran)語言的之間的接口函數,函數前綴有mex-和mx-兩種,帶mx-前綴的大多是對mxArray數據進行操作的函數,如mxIsDouble,mxCreateDoubleMatrix等;而帶mex-前綴的則大多是與Matlab環境進行交互的函數,如mexPrintf,mexErrMsgTxt等。具體可參考Apiref.pdf。
plhs[0] = mxCreateDoubleMatrix(1, 1,mxREAL);
建立一個1x1的double類型的矩陣,返回剛建立的mxArray的地址,賦給指針plhs[0];
a = mxGetPr(plhs[0]);
返回指針plhs[0]所指向矩陣的第一個實數的地址,並賦給a;
b = *(mxGetPr(prhs[0]));
獲取指針prhs[0]指向矩陣的第一個實數,並賦給b;
*a = add(b, c);
調用C程序add,計算b,c之和並賦給a指向的內容;
例:新建myhilb.c,源碼如下:
#include mex.h void myhilb(double *y,int n) { int i,j; for(i=0;i將myhilb.c拷貝至Matlab當前目錄,執行mex myhilb.c,生成myhilb.mexw64,該文件實現了計算Hilbert矩陣的功能(Hilbert矩陣:H(i,j)=1/(i+j-1))。
此時便可在Matlab中調用該函數:
>> output = myhilb (6);
分析:
mexFunction中進行了參數檢查,函數mexErrMsgTxt顯示出錯信息後即退回到MATLAB。
mxGetScalar:獲取輸入矩陣第一個元素的實數部分;mxGetM:獲取矩陣的行數。
為了測試一下Mex文件與m文件的速度差異,編寫m文件並運行之:
tic m=10000; a=zeros(m,m); for i=1:m for j=1:m a(i,j)=1/(i+j); end end toc結果:Elapsed time is3.620924 seconds.
接著運行Mex文件
tic output = myhl(10000); toc
結果:Elapsed timeis 0.730596 seconds.
可以看出Mex文件與M文件速度差異很大。
VS2010生成Mex文件(本人64位操作系統)
上述利用Matlab編譯生成Mex文件,同樣也可以使用VS2010生成Mex文件,只不過需要對VS環境進行配置,過程如下:
1、 新建一個win32 控制台的dll 空項目”myhilb”;
2、 新建源文件myhilb.c,將上述myhilb.c內容拷進即可;
3、 添加.def文件,內容為:
LIBRARY
EXPORTSmexFunction
4、 配置項目屬性, 打開項目屬性配置頁:
(1)C/C++—>常規—>附加包含目錄,輸入matlab下安裝目錄下externinclude
本人輸入E:Matlab2010Installexterninclude
(2)鏈接器->常規—>附加庫目錄,輸入matlab下安裝目錄下externlibwin64microsoft
本人輸入E:Matlab2010Installexternlibwin64microsoft
(3)連接器 ->輸入->附加依賴項,輸入
libmx.lib
libeng.lib
libmat.lib
libmex.lib
(4)鏈接器->常規—>輸出文件,輸入$(OutDir)$(TargetName).mexw64(若此處不更改,可在生成dll文件後將後綴名改為mexw64即可,這也驗證了Mex實際上就是DLL,只是後綴名不同罷了)
(5)鏈接器->高級—>目標計算機,設為MachineX64(32位系統不用更改)
設置好點擊應用,執行了(5)的64位系統還需要在執行:
生成—>配置管理器—>活動解決平台,改為x64
5、按F7編譯工程,會在Debug下生成.mexw64文件,如下圖:
VS中單步調試Mex文件
在Matlab環境下使用 mex –g myhilb.c命令進行調試,但無法加斷點進行單步調試,故需轉到VS環境下調試。
不管是利用VS還是利用Matlab生成Mex文件,只要有c源文件和Mex文件就可以利用VS對Mex源程序加斷點進行單步調試(我們用上面myhilb.c和myhilb.mexw64做測試)。
1、將Matlab當前目錄改為Mex文件(C文件)所在目錄;
2、在VS2010中打開C文件,調試—>附加到進程,附加MATLAB.exe;
3、VS中在C源碼中添加斷點,在Matlab命令窗口調用Mex文件提供的接口;
如Matlab執行:out=myhilb(6);
此時,VS2010中便可按F10進行單步調試:
要說明的是,在調試階段Matlab處於假死狀態,另外,Matlab調用了Mex文件後需要執行clear all命令後才能刪除Mex文件;
同樣地,若利用VS生成Mex文件後直接將Matlab當前目錄改至Debug目錄進行調試,則調試完必須執行clear all指令才能重新編譯工程。