【问题标题】:Efficient 3D element-wise operations in MATLABMATLAB 中的高效 3D 逐元素操作
【发布时间】:2015-10-23 16:06:31
【问题描述】:

假设我有两个矩阵:

A=50;
B=50;
C=1000;
X = rand(A,B);
Y = rand(A,B,C);

我想从Y 的每个切片C 中减去X。这是一个相当普遍的问题,我找到了三种替代解决方案:

% Approach 1: for-loop
tic
Z1 = zeros(size(Y));
for i=1:C
    Z1(:,:,i) = Y(:,:,i) - X;
end
toc

% Approach 2: repmat
tic
Z2 = Y - repmat(X,[1 1 C]);
toc

% Approach 3: bsxfun
tic
Z3=bsxfun(@minus,Y,X);
toc

我正在构建一个经常(即数千次)解决此类问题的程序,因此我正在寻找最有效的解决方案。以下是常见的结果模式:

Elapsed time is 0.013527 seconds.
Elapsed time is 0.004080 seconds.
Elapsed time is 0.006310 seconds.

循环显然更慢,bsxfun 比 repmat 慢一点。当我将 XY 的切片按元素相乘(而不是相减)时,我发现了相同的模式,尽管 repmat 和 bsxfun 在乘法上更接近一些。

增加数据的大小...

A=500;
B=500;
C=1000;
Elapsed time is 2.049753 seconds.
Elapsed time is 0.570809 seconds.
Elapsed time is 1.016121 seconds.

在这里,repmat 是明显的赢家。我想知道 SO 社区中是否有人有一个很酷的技巧来加快这个操作。

【问题讨论】:

  • 尝试增加矩阵的大小。你会看到bsxfun 更好。它在后台执行元素的复制。此外,这篇文章可能会提供一些见解:stackoverflow.com/questions/23879888/… - 这篇文章执行的操作类似于您正在查看的内容,并且每次尝试都有一些时间安排。 bsxfun 绝对是恕我直言的赢家。
  • 一些可靠的基准测试方法会使用timeit。您还可以在每种方法结束时清除内存。请参阅here 以了解类似的基准测试方法以及 BSXFUN 和 REPMAT 之间的内置插件比较,包括@minus
  • 如果您的程序执行此操作数千次,则可能值得在比这更高的级别上查看问题。也就是说,您可以一次性处理数千个案例吗?

标签: matlab matrix multidimensional-array bsxfun elementwise-operations


【解决方案1】:

根据您的实际情况,bsxfunrepmat 有时会比另一个更有优势,就像@rayryeng 建议的那样。您可以考虑另一种选择:mex。我在这里硬编码了一些参数以获得更好的性能。

#include "mex.h"
#include "matrix.h"

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{

    double *A, *B, *C;
    int ind_1, ind_2, ind_3, ind_21, ind_32, ind_321, Dims[3] = {500,500,5000};

    plhs[0] = mxCreateNumericArray(3, Dims, mxDOUBLE_CLASS, mxREAL);
    A = mxGetPr(prhs[0]);
    B = mxGetPr(prhs[1]);
    C = mxGetPr(plhs[0]);

    for ( int ind_3 = 0; ind_3 < 5000; ind_3++)
    {
        ind_32 = ind_3*250000;
        for ( int ind_2 = 0; ind_2 < 500; ind_2++)
        {
            ind_21 = ind_2*500;         // taken out of the innermost loop to save some calculation
            ind_321 = ind_32 + ind_21;
            for ( int ind_1 = 0 ; ind_1 < 500; ind_1++)
            {
                C[ind_1 + ind_321] = A[ind_1 + ind_321] - B[ind_1 + ind_21];
            }
        }
    }
} 

要使用它,请在命令窗口中输入(假设您将上述 c 文件命名为 mexsubtract.c)

mex -WIN64 mexsubtract.c

那么你可以这样使用它:

Z4 = mexsubtract(Y,X);

以下是在我的计算机上使用 A=500、B=500、C=5000 时的一些测试结果:

(repmat) Elapsed time is 3.441695 seconds.
(bsxfun) Elapsed time is 3.357830 seconds.
(cmex)   Elapsed time is 3.391378 seconds.

这是一个势均力敌的竞争者,在更极端的情况下,它会占据优势。例如,这是我得到的 A = 10, B = 500, C = 200000 :

(repmat) Elapsed time is 2.769177 seconds.
(bsxfun) Elapsed time is 3.178385 seconds.
(cmex)   Elapsed time is 2.552115 seconds.

【讨论】:

  • 忘了墨西哥!我肯定会认为它在 repmat 和 bsx 上会有更大的优势..
  • 我认为像这样的操作肯定不需要 MEX - 特别是因为 bsxfun / repmat 已经将它们的实现外包给 MEX 包装器。但是,看看时间安排是怎样的很有趣。感谢分享!
  • 我同意这对于一般目的来说不是必需的。但优化将是一个特例,对吧?老实说,我之前没有优化的经验。这对我来说是一个有趣的小实验。恕我直言,如果哪怕只是一点点改进都有意义并且开发时间不会太长,那么是时候打开引擎盖了,不是吗?
猜你喜欢
  • 2011-12-08
  • 1970-01-01
  • 2015-07-17
  • 2011-12-14
  • 1970-01-01
  • 2010-10-04
  • 2023-03-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多