5

Aセル配列の各要素に係数を掛けたいとしkます。私はそれを行うことができます:

A = cellfun(@(x) k*x, A, 'UniformOutput', false)

しかし、これは非常に遅いです。より速くて良い方法はありますか?cell 配列の要素は可変長ベクトルであるため、cell2num適用されません。

編集: fpe のfor ループの推奨に基づいて、ベンチマークの例を次に示します。まずはこのデータから

A = arrayfun(@(n) rand(n,1), randi(5,1000,1000), 'UniformOutput',false);

上記のcellfun呼び出しでは9.45 seconds、 for ループが実行されます。

A2 = cell(size(A));
for i = 1:size(A,1), for j = 1:size(A,2), A2{i,j} = A{i,j}*k; end; end
A = A2;

これは1.67 seconds大幅な改善です。私はまだ数桁速いものを好むでしょう。(また、Matlab インタープリターが for ループと同じくらい速く cellfun 呼び出しを行うことができない理由もわかりません。意味的には同じです。)

編集 2:単一の for ループを作成するという Amro の提案は、大幅に高速です。

for i = 1:numel(A), A{i} = A{i}*k; end

かかり1.11 seconds、それより前に実行packすると、メモリがちょうど整列します0.88 seconds

これを行うために MEX 関数を実装することは、実際にはあまり良くありません: 0.73 seconds, ( 0.53 secondsafter pack)。これは、Matlab で多くの小さな行列を割り当てるのが遅いことを示しています。

#include "mex.h"

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) {
    if (nrhs != 2)
        mexErrMsgTxt("need 2 arguments (Cell, Coefficient)");

    mwSize const* size = mxGetDimensions(prhs[0]);
    int N = mxGetNumberOfDimensions(prhs[0]);

    if (mxGetNumberOfElements(prhs[1]) != 1)
        mexErrMsgTxt("second argument to multcell must be a scalar");

    double coefficient = *mxGetPr(prhs[1]);

    plhs[0] = mxCreateCellArray(N, size);

    int M = mxGetNumberOfElements(prhs[0]);

    for (int i = 0; i < M; i++) {
        mxArray *r = mxGetCell(prhs[0], i);
        mxArray *l = mxCreateNumericArray(mxGetNumberOfDimensions(r),
                                          mxGetDimensions(r),
                                          mxDOUBLE_CLASS,
                                          mxREAL);
        double *rp = mxGetPr(r);
        double *lp = mxGetPr(l);
        int num_elements = mxGetNumberOfElements(r);
        for (int i = 0; i < num_elements; i++)
            lp[i] = rp[i] * coefficient;
        mxSetCell(plhs[0], i, l);
    }
}

ただし、少しごまかし、メモリを実際にその場で編集する MEX 関数を実装することが、操作から妥当なパフォーマンスを得る唯一の方法のようです0.030 seconds。これは、mxUnshareArrayAmro によって提案された文書化されていないものを使用します。

#include "mex.h"

extern "C" bool mxUnshareArray(mxArray *array_ptr, bool noDeepCopy);

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) {
    if (nrhs != 2)
        mexErrMsgTxt("need 2 arguments (Cell, Coefficient)");

    mwSize const* size = mxGetDimensions(prhs[0]);
    int N = mxGetNumberOfDimensions(prhs[0]);

    if (mxGetNumberOfElements(prhs[1]) != 1)
        mexErrMsgTxt("second argument to multcell must be a scalar");

    double coefficient = *mxGetPr(prhs[1]);

    mxUnshareArray(const_cast<mxArray *>(prhs[0]), false);
    plhs[0] = const_cast<mxArray *>(prhs[0]);

    int M = mxGetNumberOfElements(prhs[0]);

    for (int i = 0; i < M; i++) {
        mxArray *r = mxGetCell(prhs[0], i);
        double *rp = mxGetPr(r);
        int num_elements = mxGetNumberOfElements(r);
        for (int i = 0; i < num_elements; i++)
            rp[i] = rp[i] * coefficient;
    }
}
4

1 に答える 1

3

正確な答えではありませんが、両方のアプローチ (cellfun と for-loop) で JIT コンパイラとアクセラレータの影響を確認する方法を次に示します。

feature('jit', 'off'); feature('accel', 'off');
tic, A = cellfun(@(x) k*x, A, 'UniformOutput', false); toc
tic, for i=1:numel(A), A{i} = A{i}*k; end, toc

feature('jit', 'on'); feature('accel', 'on');
tic, A = cellfun(@(x) k*x, A, 'UniformOutput', false); toc
tic, for i=1:numel(A), A{i} = A{i}*k; end, toc

私は次のことを得る

Elapsed time is 25.913995 seconds.
Elapsed time is 13.050288 seconds.

対。

Elapsed time is 10.053347 seconds.
Elapsed time is 1.978974 seconds.

2番目に最適化をオンにします。

ちなみに、並列のparforパフォーマンスははるかに劣っていました (少なくとも、プール サイズが 2 プロセスのローカル テスト マシンでは)。

あなたが投稿した結果を見て、MEX関数が道です:)

于 2013-04-06T16:47:28.220 に答える