Matlab:从循环中重复调用相同的mex函数会产生太多的开销吗?

时间:2012-10-16 19:03:45

标签: performance matlab mex

我有一些需要加速的Matlab代码。通过分析,我已经确定了一个特定的函数作为减慢执行速度的罪魁祸首。这个函数在循环中被调用了数十万次。

我的第一个想法是将函数转换为mex(使用Matlab Coder)来加速它。然而,常见的编程意义告诉我Matlab和mex代码之间的接口会导致一些开销,这意味着调用这个mex函数数千次可能不是一个好主意。它是否正确?或者Matlab在重复调用相同的mex来消除开销时会做些什么?

如果显着的开销,我正在考虑重构代码,以便将循环添加到函数本身,然后然后创建一个mex。在此之前,我想验证我的假设,以证明花在此上的时间。

更新:

我尝试了@ angainor的建议,并使用以下代码创建了donothing.m:

function nothing = donothing(dummy) %#codegen
nothing = dummy;
end

然后,我创建了一个mex函数作为donothing_mex,并尝试了以下代码:

tic;
for i=1:1000000
    donothing_mex(5);
end
toc;

结果是,对该功能的一百万次调用大约需要9秒。这对我们来说并不是一个重要的开销,所以现在我想我会将被调用的函数单独转换为mex。但是,从一个执行大约一百万次的循环调用一个函数回想起来似乎是一个非常愚蠢的想法,考虑到这是性能关键代码,所以将循环移动到mex函数仍然在书中,但优先级要低得多。

3 个答案:

答案 0 :(得分:5)

与往常一样,这一切都取决于您在MEX文件中的工作量。调用MEX函数的开销是恒定的,并且不依赖于例如问题大小。这意味着参数被复制到新的临时数组。因此,如果它足够工作,则不会显示调用MEX文件的MATLAB开销。无论如何,根据我的经验,MEX调用开销仅在第一次调用mex函数时才有意义 - 必须加载动态库,解析符号等。后续的MEX调用开销非常小且效率很高。

由于这种高级语言的性质,MATLAB中几乎所有内容都会产生一些开销。除非你有一个代码,你确定它是用JIT完全编译的(但是你不需要一个mex文件:))所以你可以选择一个开销而不是另一个......

总结一下 - 我不会太害怕MEX呼叫开销。

编辑正如在这里和其他地方经常听到的那样,在任何特定情况下,唯一合理的做法当然是 BENCHMARK ,并检查一下你自己。您可以通过编写一个简单的MEX函数轻松估算MEX调用开销:

#include "mex.h"
void mexFunction(int nlhs, mxArray *plhs[ ], int nrhs, const mxArray *prhs[ ]) 
{      
}

在我的电脑上,你得到了

tic; for i=1:1000000; mexFun; end; toc
Elapsed time is 2.104849 seconds.

这是每次MEX呼叫的2e-6s开销。添加您的代码,计算时间并查看开销是否处于可接受的水平。

正如Andrew Janke在下面提到的那样(谢谢!),MEX函数开销显然取决于传递给MEX函数的参数数量。这是一个小依赖,但它存在:

a = ones(1000,1);
tic; for i=1:1000000; mexFun(a); end; toc
Elapsed time is 2.41 seconds.

a

的大小无关
a = ones(1000000,1);
tic; for i=1:1000000; mexFun(a); end; toc
Elapsed time is 2.41805 seconds.

但它与参数的数量有关

a = ones(1000000,1);
b = ones(1000000,1);
tic; for i=1:1000000; mexFun(a, b); end; toc
Elapsed time is 2.690237 seconds.

因此,您可能希望在测试中考虑到这一点。

答案 1 :(得分:2)

你应该毫不犹豫地移动mex文件中的循环。 下面的示例演示了for循环中几乎为空的工作单元的1000倍加速。 显然,当for循环中的工作量发生变化时,这种加速会降低。

以下是差异的一个例子:

没有内部循环的Mex功能:

#include "mex.h"
void mexFunction(int nlhs, mxArray *plhs[ ], int nrhs, const mxArray *prhs[ ]) 
{      
    int i=1;    
    plhs[0] = mxCreateDoubleScalar(i);
}

在Matlab中调用:

tic;for i=1:1000000;donothing();end;toc
Elapsed time is 3.683634 seconds.

内部循环的Mex功能:

#include "mex.h"
void mexFunction(int nlhs, mxArray *plhs[ ], int nrhs, const mxArray *prhs[ ]) 
{      
    int M = mxGetScalar(prhs[0]);
    plhs[0] = mxCreateNumericMatrix(M, 1, mxDOUBLE_CLASS, mxREAL);
    double* mymat = mxGetPr(plhs[0]);
    for (int i=0; i< M; i++)
        mymat[i] = M-i;
}

在Matlab中调用:

tic; a = donothing(1000000); toc
Elapsed time is 0.003350 seconds.

答案 2 :(得分:2)

嗯,这是我在Matlab中可以做到的最快的事情:

%#eml
function L = test(s,t)

    m = numel(s);
    n = numel(t);

    % trivial cases
    if m==0 && n==0
        L = 0; return; end
    if n==0
        L = m; return; end
    if m==0
        L = n; return; end

    % non-trivial cases
    M = zeros(m+1,n+1);    
    M(:,1) = 0:m;

    for j = 2:n+1
        for i = 2:m+1
            M(i,j) = min([
                M(i-1,j) + 1
                M(i,j-1) + 1
                M(i-1,j-1) + (s(i-1)~=t(j-1));
                ]);
        end
    end

    L = min(M(end,:));

end

你可以编译它并运行一些测试吗? (出于一些奇怪的原因,编译无法在我的安装上工作......)如果您认为更容易,可能首先将%#eml更改为%#codegen

注意:对于C版本,您还应该交换for循环,以便j上的循环是内部循环。

此外,row1row2方法的内存效率更高。无论如何你要编译,我会使用这种方法。