8

高速化する必要があるいくつかの Matlab コードがあります。プロファイリングを通じて、特定の関数が実行速度低下の原因であることを特定しました。この関数は、ループ内で何十万回も呼び出されます。

私が最初に考えたのは、関数を mex (Matlab Coder を使用) に変換して高速化することでした。ただし、一般的なプログラミングの感覚では、Matlab と mex コード間のインターフェイスがオーバーヘッドにつながることがわかります。つまり、この mex 関数を何千回も呼び出すのは良い考えではない可能性があります。これは正しいです?または、オーバーヘッドを取り除くために同じ mex が繰り返し呼び出されている場合、Matlab はいくつかの魔法を行いますか?

オーバーヘッドが大きい場合、コードを再構築して関数自体にループを追加し、その mex を作成することを考えていますそれを行う前に、これに費やされた時間を正当化するために、私の仮定を検証したいと思います。

アップデート:

@angainor の提案を試し、次のコードで donothing.m を作成しました。

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

次に、これから donothing_mex として mex 関数を作成し、次のコードを試しました。

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

その結果、関数の 100 万回の呼び出しに約 9 秒かかりました。これは私たちの目的にとって大きなオーバーヘッドではないので、今のところ、呼び出された関数だけを mex に変換すると思います。ただし、約 100 万回実行されるループから関数を呼び出すことは、これがパフォーマンス クリティカルなコードであることを考えると、かなりばかげた考えのように思えます。そのため、ループを mex 関数に移動することはまだ本に記載されていますが、優先度ははるかに低くなります。

4

3 に答える 3

5

いつものように、すべては MEX ファイルで行う作業の量に依存します。MEX 関数を呼び出すオーバーヘッドは一定であり、たとえば問題のサイズには依存しません。これは、引数が新しい一時配列にコピーされないことを意味します。したがって、十分な作業であれば、MEX ファイルを呼び出す MATLAB オーバーヘッドは表示されません。とにかく、私の経験では、MEX 呼び出しのオーバーヘッドが重要になるのは、mex 関数が初めて呼び出されたときだけです。動的ライブラリをロードしたり、シンボルを解決したりする必要があります。その後の MEX 呼び出しのオーバーヘッドは非常に小さく、非常に効率的です。

MATLAB のほとんどすべては、この高水準言語の性質によるオーバーヘッドに関連しています。JITで完全にコンパイルされていると確信しているコードがない限り(ただし、me​​xファイルは必要ありません:))したがって、一方のオーバーヘッドを他方よりも選択できます..

要約すると、MEX 呼び出しのオーバーヘッドはあまり怖くありません。

編集ここや他の場所でよく耳にするように、特定のケースで行うべき唯一の合理的なことは、もちろん、ベンチマークを行い、自分で確認することです。自明な 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-6 秒のオーバーヘッドです。コードを追加し、時間を計って、オーバーヘッドが許容レベルにあるかどうかを確認します。

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.

そのため、テストでそれを考慮に入れたいと思うかもしれません。

于 2012-10-16T19:09:58.477 に答える
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.
于 2012-10-17T07:53:02.467 に答える
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が内側になるようにする必要があります。

また、row1androw2アプローチはメモリ効率が大幅に向上します。とにかくコンパイルする場合は、そのアプローチを使用します。

于 2012-10-18T09:01:26.617 に答える