数组 – 对单元数组的每个元素执行算术运算的最快方法是什么?

假设我想将单元格数组A的每个元素与系数k相乘.我可以这样做:

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

但这非常缓慢.有更快更好的方法吗?单元阵列元素是可变长度向量,因此cell2num不适用.

编辑:基于fpe对for循环的推荐,这是一个示例基准.从这些数据开始

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

上面的cellfuncall需要9.45秒,而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秒,这是一个显着的改进.我仍然更喜欢几个数量级的东西. (我也不明白为什么Matlab解释器无法像for循环一样快地进行cellfun调用.它们在语义上是相同的.)

编辑2:Amro建议制作一个单循环的速度要快得多:

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

需要1.11秒,如果我在它之前运行pack来对齐内存只需0.88秒.

实现MEX功能实际上并不是更好:0.73秒,(包后0.53秒),这表明在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秒.这使用Amro建议的未记录的mxUnshareArray.

#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;
    }
}
最佳答案
不完全是答案,但这里有一种方法可以看到JIT编译器和加速器在两种方法中的影响(cellfun与for循环):

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.

优化在第二个开启.

顺便说一句,并行parfor的表现要差得多(至少在我的本地测试机器上,池大小为2个进程).

看到你发布的结果,MEX功能是要走的路:)

转载注明原文:数组 – 对单元数组的每个元素执行算术运算的最快方法是什么? - 代码日志