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++.