Matlab mex-файл с mexCallMATLAB почти в 300 раз медленнее соответствующего m-файла
Я начал реализовывать несколько M-файлов на C++, чтобы сократить время выполнения. M-файлы создают N-мерные точки и оценивают значения функций в этих точках. Функции определяются пользователем и передаются в m-файлы и mex-файлы в качестве дескрипторов функций. Mex-файлы используют mexCallMATLAB с feval для поиска значений функций.
Я построил приведенный ниже пример, где дескриптор функции fn, построенный в командной строке Matlab, передается matlabcallingmatlab.м и mexcallingmatlab.процедуры ЧГК. С недавно открытым Matlab, mexcallingmatlab оценивает эту функцию 200000 в 241.5 секунд, в то время как matlabcallingmatlab оценивает его в 0.81522 секунд, поэтому 296 раз замедляется с реализацией mex. Эти времена являются результатами вторых запусков, поскольку первые запуски кажутся больше, вероятно, из-за некоторых накладных расходов, связанных с первой загрузкой программы и т. д.
Я потратил много дней на поиск в интернете по этой проблеме и попробовал предложения по этому поводу. Я пробовал разные флаги компиляции mex для оптимизации mex, но почти не было разницы в производительности. Предыдущий пост в Stackoverflow заявил, что обновление Matlab было решением, но я использую, вероятно, последнюю версию MATLAB: 8.1.0.604 (R2013a) на Mac OS X версия: 10.8.4. Я скомпилировал файл mex с флагом largeArrayDims и без него, но это тоже не имело никакого значения. Некоторые предположили, что содержимое дескриптора функции может быть непосредственно кодируется в файле cpp, но это невозможно, так как я хотел бы предоставить этот код любому пользователю с любым типом функции с векторным входом и выходом реального числа.
насколько я узнал, файлы mex должны проходить через функцию feval для использования дескриптора функции, тогда как m-файлы могут напрямую вызывать дескрипторы функций при условии, что версия Matlab новее, чем некоторая версия.
любая помощь была бы весьма признательна.
простая созданная ручка функции в командной строке Matlab:
fn = @(x) x'*x
matlabcallingmatlab.м :
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
3 ответов
поэтому я попытался реализовать это сам, и я думаю, что нашел причину медлительности.
в основном ваш код имеет небольшую утечку памяти, где вы не освобождаете lhs
mxArray
вернулся из вызова в mexCallMATLAB
. Это не совсем утечка памяти, видя, что менеджер памяти MATLAB заботится о освобождении памяти, когда MEX-файл выходит:
MATLAB выделяет динамическую память для хранения
mxArrays
наplhs
. МАТЛАБ автоматически освобождает динамическую память при очистке MEX-файла. Однако, если куча пространства, называютmxDestroyArray
когда вы закончили сmxArrays
plhs
указывает.
еще явное лучше, чем неявное... Таким образом, ваш код действительно подчеркивает deallocator менеджера памяти MATLAB:)
mexcallingmatlab.cpp
#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]);
}
MATLAB
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 и наоборот. Накладные расходы на вызов невелики, но это действительно складывается в тугой цикл, как это. Как показывает ваше тестирование, pure Matlab может быть намного быстрее в этом случае! Ваш другой вариант-устранить mexCallMATLAB
позвоните и сделайте все на чистом C++.