Windows 64 + Matlab 64 MEX混合程式設計初步
說明
記錄本人在Windows下,使用Matlab 64-bit 版進行混合程式設計時遇到的問題,主要記錄編譯器的安裝與簡要的混編過程。
- Matlab版本:Matlab R2014a 64-bit、Matlab R2015b 64-bit
- 作業系統版本:Windows 8.1 64-bit、Windows10 64-bit
- 編譯器:Microsoft Windows SDK 7.1(C/C++) 和 GNU編譯器(有MinGW(Minimalist GNU for Windows,僅32位)、MinGW64(64位和32位)、TMD-GCC(非官方的編譯器包)、CygWin等等,它們間的區別與聯絡參見
Matlab混合程式設計
下載與安裝編譯器
Microsoft Windows SDK 7.1(C/C++)
下載完成後根據提示安裝即可,如果出現問題,安裝失敗,參考: 部分。
TDM-GCC(gcc/g++)
For Matlab 2015
注意,MATLAB 2015現僅支援GCC4.9.2,在安裝TMD-GCC時,不要勾選更新,如下圖:
然後,還需注意的是安裝路徑不能有空格;
- 需要新增系統環境變數
MW_MINGW64_LOC=your tdm-gcc path
。 - 重啟系統和MATLAB後,輸入
mex -setup
至此完成!
For Matlab 2014
如下文章針對於MATLAB 2014a。
點此tmd-gcc開啟下載頁面,選擇64位版本,40M+,全部安裝佔用400M+磁碟空間。
根據以前使用經驗,安裝路徑最好不要有空格中文。
安裝完成後,開始 -> 執行 -> cmd
開啟Windows命令視窗,輸入gcc -v
,如下圖所示,顯示安裝資訊,說明安裝成功:
下面的程式碼是從Bogdan主頁上下載的mexopts.bat檔案程式碼,使用時需要做一點小小的改動。
在下面的程式碼中找到:set MINGWPATH=p:\mingw64
這句程式碼,將其中的路徑p:\mingw64
E:\devtools\TDM-GCC-64
,然後儲存並將mexopts.bat檔案copy到計算機如下路徑(複製下面的路徑地址,貼上到資源管理器視窗位址列,回車即可):
%USERPROFILE%\AppData\Roaming\Mathworks\MATLAB\R2014a\
下面是程式碼:
@echo off
:: NOTE: this is actually not a proper .bat file executed by Windows. MEX
:: parses it and only understands a very reduced set of commands:
:: "set" and "rem" apparently, everything else is ignored (behaves as
:: "rem"), so don't do any fancy batch stuff in here. There are some
:: undocumented special vars you can set here that will trigger MEX
:: to do fancy stuff.
:: You can use MinGW64 builds (win32 threads + seh unwinding) from here:
:: http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/
:: Tested with the following:
:: http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/4.9.1/threads-win32/seh/x86_64-4.9.1-release-win32-seh-rt_v3-rev1.7z
:: Set this to your Mingw64 top folder, where you extracted the above
set MINGWPATH=p:\mingw64
:: Leave these alone unless you know what you're doing.
set PATH=%MINGWPATH%\bin;%PATH%
set PRELINK_CMDS=echo.>%TEMP%\mexstaticlibs
:: You can have MEX run some commands before calling the linker.
:: The two examples below will cause gcc to output the full path to some
:: static libraries so you can link statically to them (see the
:: LINGFLAGSPOST special var below). You can set any command here, however.
rem set PRELINK_CMDS1=gcc -print-file-name=libwinpthread.a >> %TEMP%\mexstaticlibs
rem set PRELINK_CMDS2=gcc -print-file-name=libquadmath.a >> %TEMP%\mexstaticlibs
rem set PRELINK_CMDS3=...
:: You can have MEX run some commands also after calling the linker
:: (e.g. upx compress the output .mex)
rem set POSTLINK_CMDS1=upx -9 "%OUTDIR%%MEX_NAME%%MEX_EXT%"
rem set POSTLINK_CMDS2=...
:: You can change these if you really need to.
set COMPILER=g++
set COMPFLAGS=-c -I"%MATLAB%\extern\include" -DMATLAB_MEX_FILE
set OPTIMFLAGS=-O3 -funroll-loops -DNDEBUG
set DEBUGFLAGS=-g
set NAME_OBJECT=-o
set LINKER=g++
set LINKFLAGS=-shared -static-libstdc++ -static-libgcc -L"%MATLAB%\bin\win64" -L"%MATLAB%\extern\lib\win64\microsoft" -lmex -lmx -leng -lmat -lmwlapack -lmwblas
set [email protected]%TEMP%\mexstaticlibs
set NAME_OUTPUT=-o "%OUTDIR%%MEX_NAME%%MEX_EXT%"
:: EXAMPLES
:: ========
:: You can compile simple files using "mex file.cpp". To support more than 2^32 elements, use
:: "mex -largeArrayDims file.cpp" ... use this by default, unless you know what you're doing.
:: To add include dirs, lib dirs, or compile/link flags, do:
:: mex COMPFLAGS="$COMPFLAGS -std=gnu99 -Ix:/include/dir" LINKFLAGS="$LINKFLAGS -Lx:/libs/dir -lmylib" -largeArrayDims file.cpp
選擇編譯器
檢視可用編譯器:使用
mex -setup
檢視可選擇的編譯器,如果沒有需要自己安裝,如果已安裝且只安裝了編譯器Microsoft Windows SDK 7.1(C/C++),顯示如下結果:
選擇編譯器:使用
mex -setup lang
選擇編譯器,如mex -setup C++
選擇C++編譯器,如下圖:
如果你也按上面TDM-GCC(gcc/g++) 中講的方法,配置了TDM-GCC-64位的編譯器,那麼,你會發現結果如下:
剛開始我也以為不能用,後來試著編譯了一下,竟然可以!
DT.c檔案中重複定義了無窮大,註釋掉,就沒有提示啦!
編寫c/cpp檔案
與普通的C檔案的編寫有兩點不同:
包含mex.h標頭檔案,即:
#include "mex.h"
;編寫mexFunction函式。
下面以一個求和的例子說明:
#include "mex.h" // 使用MEX檔案必須包含的標頭檔案
// 執行具體工作的C函式
double add(double x, double y)
{
return x + y;
}
// MEX檔案介面函式
void mexFunction(int nlhs,mxArray *plhs[], int nrhs,const mxArray *prhs[])
{
double *z;
double x, y;
plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL);
z = mxGetPr(plhs[0]);
x = *(mxGetPr(prhs[0]));
y = *(mxGetPr(prhs[1]));
*z = add(x, y);
}
Matlab命令視窗輸入mex add.cpp
,執行結果如下圖:
在Windows上編譯後生成mexw32
和mexw64
檔案,在Linux上編譯會生成mexa32
和mexa64
檔案。
mexFunction函式介紹
mexFunction函式介面如下:
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
可見,是沒有返回值的,其實是通過指標陣列plhs傳遞的。它共有四個引數,即左邊的輸出引數及數目和右邊的輸入引數及數目,具體引數意義如下:
引數 | 意義 | 英文全稱 | 型別 |
---|---|---|---|
nlhs | 左邊輸出引數的數目 | number of left-hand side | 整型(int) |
plhs | 指向輸出引數的指標 | pointer of left-hand side | 指標陣列 |
nrhs | 右邊輸入引數的數目 | number of right-hand side | 整型(int) |
prhs | 指向輸入引數的指標 | pointer of right-hand side | 指標陣列 |
詳情參考:
編譯多個c/cpp檔案
假設A.c
呼叫B.c
,B.c
呼叫C.c
可以使用如下命令編譯:
mex C.c
mex B.c
mex A.c
或者
mex A.c B.c C.c
一些例項
MATLAB2015編譯MeanShift
MeanShift分割原始碼包,可以從這裡下載,也可以從本人網盤(含MATLAB介面,和編譯好的mexw32,mexw64,mexa64)下載。
原本程式碼中沒有適用於Windows64和MATLAB64位的mexw64檔案,需要自己編譯生成,方法很簡單,執行資料夾中的compile_edison_wrapper.m檔案即可。
然後,讀入一幅影象,呼叫edison_wrapper程式實現分割,注意要使edison_wrapper資料夾處於當前路徑。如讀入一幅Lena RGB影象,分割結果如下:
I=imread('D:\DataSets\oi\nsi\classical\LenaRGB.bmp');
[fimage, labels] = edison_wrapper(I, @RGB2Luv);
figure
subplot(121)
imshow(I)
title('Orignal RGB image')
subplot(122)
imagesc(labels)
axis image % 座標軸與影象尺寸一致
title('Segmentation result:labels')
Problem&Solution
找不到編譯器或SDK
Problem
提示:“未找到支援的編譯器或 SDK”,如下圖:
Solution
名稱 | 版本 |
---|---|
GRMSDK_EN_DVD.iso | x86 |
GRMSDKIAI_EN_DVD.iso | Itanium |
GRMSDKX_EN_DVD.iso | amd64 |
注意
A problem occurred while installing selected Windows SDK components.
Installation of the “Microsoft Windows SDK for Windows 7” product has reported the following error: Please refer to Samples\Setup\HTML\ConfigDetails.htm document for further information.
Please attempt to resolve the problem and then start Windows SDK setup again. If you continue to have problems with this issue, please visit the SDK team support page at http://go.microsoft.com/fwlink/?LinkId=130245.
Click the View Log button to review the installation log.
To exit, click Finish.
如下圖:
解決辦法:
出現此問題,很可能是你的PC機已經安裝了:Microsoft Visual C++ 2010,而在安裝Microsoft Windows SDK for Windows 7 and .NET Framework 4 (ISO)時預設選擇安裝Microsoft Visual C++ 2010,如下圖,造成的衝突,可以解除安裝已經安裝的,注意32和64位的都要解除安裝。
解除安裝完成後從新安裝即可!!!
error C2143: syntax error
Problem
提示錯誤:error C2143: syntax error : missing ‘;’ before ‘type’,如下圖所示:
Solution
出現這種現象,很可能是你在用VC的編譯器編譯GNU的C語言寫的程式碼,兩個編譯器中的C標準不一樣。
一種方法是將檔案字尾名.c
,改成.cpp
,重新編譯,但可能還會有其它的錯誤。
如果原始檔是GNU的C程式碼,建議安裝相應的編譯器,然後編譯,可參考TDM-GCC(gcc/g++)
一些有用的總結
注意系統平臺和檔案路徑
MATLAB 匹配查詢檔案的方式是這樣的:
先從當前目錄尋找,假如沒有,再到搜尋路徑中從頂層尋找,如果在同一目錄下找到了函式名相同的.m檔案、.mex…檔案,如果.mex…檔案和平臺匹配,則執行之,反之執行.m檔案。
系統平臺
假如你有這樣一個工具包tools,檔案結構如下,tools為當前目錄:
其中,func.m函式為MATLAB版,同時又用C語言寫了類似的函式,並mex混編生成了Linux平臺下的:mexa32、mexa64;Windows平臺下的:mexw32、mexw64以及MAC平臺下的:mexmac檔案,如上圖。
- 假如你現在處於Windows系統,並且MATLAB版本為64位,那麼,你只能成功呼叫func.m、func.mexw64兩個檔案,並且會優先選擇func.mexw64,而“mexw32”和“mexa64”都是不能用的。
- 假如你現在處於Windows系統,並且MATLAB版本為32位,那麼,你只能成功呼叫func.m、func.mexw32兩個檔案,並且會優先選擇func.mexw32,“mexw64”和“mexa32”都是不能用的。
檔案路徑
依然如上圖tools目錄結構示意所示,tools為當前目錄,不同的是:
- 你刪除了func.mexw32和func.mexw64檔案,你的執行環境是Windows下的MATLAB,那麼你呼叫成功的是func.m檔案,而不是“mex”資料夾中的func.mexw32或func.mexw64,因為MATLAB第一匹配到“func”函式的是func.m;
執行效率對比
實驗說明:
主要對比MATLAB與C的(mex函式)分別在32和64位MATLAB上的執行效率。
實驗環境:ThinkPad E430c + Windows 10 + MATLAB 2015a(32bit)/ MATLAB 2015b(64bit)。
- 實驗1:迴圈
N 次,N=2×108 ;
test.m
原始碼:
N = 2*10e8;
tic
func(N)
toc
tic
for i=0:N-1
end
toc
混編C程式碼(func.c):
#include "mex.h" // include head files
// function
double loopN(double N)
{
int i = 0;
for(i = 0; i< N; i++);
return i;
}
// interface
void mexFunction(int nlhs,mxArray *plhs[], int nrhs,const mxArray *prhs[])
{
double *z;
double N;
plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL);
z = mxGetPr(plhs[0]);
N = *(mxGetPr(prhs[0]));
*z = loopN(N);
}
- 實驗2:提取影象特徵,工具包 matlabPyrTools;
測試MATLAB程式碼:
N = 20;
I=imread('D:\DataSets\oi\nsi\classical\BaboonRGB.bmp');
time_using = cputime;
grayimg = double(rgb2gray(uint8(I)));
for i = 1:N
[pyr,pind] = buildSpyr(grayimg,3,'sp3Filters');
pyramids = getSpyr(pyr,pind);
end
time_using = cputime - time_using;
time_using = time_using/N;
disp(time_using)
- 實驗3:MeanShift分割,工具包 edison_wrapper,含MATLAB interface。
測試MATLAB程式碼:
N = 20;
I=imread('D:\DataSets\oi\nsi\classical\BaboonRGB.bmp');
time_using = cputime;
for i = 1:N
[fimage, labels] = edison_wrapper(I, @RGB2Luv);
end
time_using = cputime - time_using;
time_using = time_using/N;
disp(time_using)
- 實驗4:MeanShift分割,工具包 edison_wrapper,含MATLAB interface,對用matlabPyrTools提取的金字塔特徵進行分割。
測試MATLAB程式碼:
N = 20;
I=imread('D:\DataSets\oi\nsi\classical\BaboonRGB.bmp');
time_using = cputime;
for i = 1:N
[fimage, labels] = edison_wrapper(I, @ExtractFeature, 'SpatialBandWidth', 7, 'RangeBandWidth', 12, 'MinimumRegionArea', 200);
end
time_using = cputime - time_using;
time_using = time_using/N;
disp(time_using)
實驗結果:
單位:
實驗序號 | MATLAB(32) | MATLAB(64) | C(MEX, 32) | C(MEX, 64) |
---|---|---|---|---|
1 | 4.91 | 7.42 | 4.54 | 1.43 |
2 | - | - | 0.9953 | 0.4453 |
3 | - | - | 4.10 | 4.98 |
4 | - | - | 11.64 | 17.63 |
實驗結果很不可思議!
- 實驗1:64位MATLABfor迴圈竟然比32位的慢了好些,而mex的C則相反(正常表現),不過可以看出C語言要比MATLAB快;
- 實驗2:正常表現;
- 實驗3:有些反常;
- 實驗4:64位竟然慢了那麼多!
對於實驗4,觀察返回值labels,即超畫素的標籤,發現32位和64位結果並不一致,但大致相同,圖形顯示如下:
MATLAB 32位和64位下得到的labels的部分值: