[英]Matlab mex-file with mexCallMATLAB is almost 300 times slower than the corresponding m-file
我開始用C ++實現一些m文件,以減少運行時間。 m文件產生n維點並在這些點評估函數值。 這些函數是用戶定義的,它們作為函數句柄傳遞給m文件和mex文件。 mex文件使用mexCallMATLAB和feval來查找函數值。
我構造了下面的例子,其中在Matlab命令行中構造的函數句柄fn被傳遞給matlabcallingmatlab.m和mexcallingmatlab.cpp例程。 使用新開放的Matlab,mexcallingmatlab在241.5秒內評估此功能200000,而matlabcallingmatlab在0.81522秒內評估它,因此使用mex實現減慢296倍。 這些時間是第二次運行的結果,因為第一次運行似乎更大可能是由於第一次加載程序等相關的一些開銷。
我花了很多天在網上搜索這個問題並嘗試了一些建議。 我嘗試了不同的mex編譯標志來優化mex,但性能幾乎沒有差異。 Stackoverflow上的一篇文章指出,升級Matlab是解決方案,但我在Mac OS X版本:10.8.4上使用的最新版本MATLAB版本:8.1.0.604(R2013a)。 我使用和不使用-largeArrayDims標志編譯了mex文件,但這也沒有任何區別。 有人建議函數句柄的內容可以直接在cpp文件中編碼,但這是不可能的,因為我想將此代碼提供給具有矢量輸入和實數輸出的任何類型函數的任何用戶。
據我所知,mex文件需要通過feval函數來使用函數句柄,而m文件可以直接調用函數句柄,前提是Matlab版本比某些版本更新。
任何幫助將不勝感激。
在Matlab命令行中創建的簡單函數句柄 :
fn = @(x) x'*x
matlabcallingmatlab.m :
function matlabcallingmatlab( fn )
x = zeros(2,1);
for i = 0 : 199999
x(2) = i;
f = fn( x );
end
mexcallingmatlab.cpp :
#include "mex.h"
#include <cstring>
void mexFunction( int nlhs, mxArray *plhs[],
int nrhs, const mxArray *prhs[] )
{
mxArray *lhs[1], *rhs[2]; //parameters to be passed to feval
double f, *xptr, x[] = {0.0, 0.0}; // x: input to f and f=f(x)
int n = 2, nbytes = n * sizeof(double); // n: dimension of input x to f
// prhs[0] is the function handle as first argument to feval
rhs[0] = const_cast<mxArray *>( prhs[0] );
// rhs[1] contains input x to the function
rhs[1] = mxCreateDoubleMatrix( n, 1, mxREAL);
xptr = mxGetPr( rhs[1] );
for (int i = 0; i < 200000; ++i)
{
x[1] = double(i); // change input
memcpy( xptr, x, nbytes ); // now rhs[1] has new x
mexCallMATLAB(1, lhs, 2, rhs, "feval");
f = *mxGetPr( lhs[0] );
}
}
編譯mex文件 :
>> mex -v -largeArrayDims mexcallingmatlab.cpp
所以我試着自己實現這個,我想我找到了緩慢的原因。
基本上,你的代碼有,你沒有釋放的一個小的內存泄漏lhs
mxArray
從調用返回mexCallMATLAB
。 它不完全是內存泄漏,看到MATLAB內存管理器在MEX文件退出時負責釋放內存:
MATLAB分配動態內存以在
mxArrays
中存儲plhs
。 清除MEX文件時,MATLAB會自動釋放動態內存。 但是,如果堆空間非常mxDestroyArray
,請在完成mxArrays
plhs
指向后調用mxDestroyArray
。
仍然顯式優於隱式...所以你的代碼真的強調MATLAB內存管理器的解除分配器:)
#include "mex.h"
#ifndef N
#define N 100
#endif
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
// validate input/output arguments
if (nrhs != 1) {
mexErrMsgTxt("One input argument required.");
}
if (mxGetClassID(prhs[0]) != mxFUNCTION_CLASS) {
mexErrMsgTxt("Input must be a function handle.");
}
if (nlhs > 1) {
mexErrMsgTxt("Too many output arguments.");
}
// allocate output
plhs[0] = mxCreateDoubleMatrix(N, 1, mxREAL);
double *out = mxGetPr(plhs[0]);
// prepare for mexCallMATLAB: val = feval(@fh, zeros(2,1))
mxArray *lhs, *rhs[2];
rhs[0] = mxDuplicateArray(prhs[0]);
rhs[1] = mxCreateDoubleMatrix(2, 1, mxREAL);
double *xptr = mxGetPr(rhs[1]) + 1;
for (int i=0; i<N; ++i) {
*xptr = i;
mexCallMATLAB(1, &lhs, 2, rhs, "feval");
out[i] = *mxGetPr(lhs);
mxDestroyArray(lhs);
}
// cleanup
mxDestroyArray(rhs[0]);
mxDestroyArray(rhs[1]);
}
fh = @(x) x'*x;
N = 2e5;
% MATLAB
tic
out = zeros(N,1);
for i=0:N-1
out(i+1) = feval(fh, [0;i]);
end
toc
% MEX
mex('-largeArrayDims', sprintf('-DN=%d',N), 'mexcallingmatlab.cpp')
tic
out2 = mexcallingmatlab(fh);
toc
% check results
assert(isequal(out,out2))
運行上述基准測試幾次(為了加熱),我得到以下一致的結果:
Elapsed time is 0.732890 seconds. % pure MATLAB
Elapsed time is 1.621439 seconds. % MEX-file
沒有你最初的慢速時間附近! 純MATLAB部分仍然快兩倍,可能是因為調用外部MEX函數的開銷。
(我的系統:Win8運行64位R2013a)
通常,沒有理由期望MEX文件比M文件更快。 這通常是正確的唯一原因是MATLAB中的許多循環會產生大量的函數調用開銷,以及參數檢查等。 在C中重寫可以消除開銷,並為您的C編譯器提供優化代碼的機會。
在這種情況下,C編譯器沒有任何優化...它必須為每次迭代調用MATLAB接口。 實際上,MATLAB優化器會做得更好,因為在某些情況下它可以“看到”函數。
換句話說,忘記使用MEX來加速這個程序。
從mex到Matlab的調用有一些開銷成本,反之亦然。 每次調用的開銷很小,但它確實在這樣的緊密循環中加起來。 正如您的測試所示,在這種情況下,純Matlab可以更快! 您的另一個選擇是消除mexCallMATLAB
調用並在純C ++中執行所有操作。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.