8

当我前段时间在大学开始使用 matlab 时,如果我的主管看到任何不必要的 for 循环,他会杀了我(他会要求kron尽可能地交换它或任何类型的索引操作)。后来,我试图尽可能避免matlab上的任何循环,寻找最黑暗的matlab编码方法来做黑魔法而不是简单的循环。

有一天,我发现了cellfun,这使得黑魔法变得非常简单,我可以更改许多使用单元格和cellfun组合的循环,但是有一天我看到了一篇关于 cellfun 的帖子,这让我质疑我继承的 matlab 知识是否属实,即:matlab 循环总是比一个内置编译函数慢,这是我非常相信的。我在我的一个实现中对其进行了测试,事实上,for 循环会更快!我当时想,天哪,那些天做晦涩的代码是白白浪费的哈哈哈。从那天起,我停止努力优化matlab代码,通常它取决于每种情况等等。

今天我看到了这个答案,这让我想起了我为尽可能多地避免 matlab 循环所做的努力(我不知道这是否是作者为了性能而避免的,但无论如何它提醒了所有这些 matlab 循环性能的事情)。我想到了一个问题:cellfun 比 for 循环更好吗?什么时候会是真的?

4

4 回答 4

11

如果性能是主要因素,则应避免使用单元、循环或 cellfun/arrayfun。使用矢量运算通常要快得多(假设这是可能的)。

下面的代码使用标准数组循环和数组操作扩展了 Werner 的 add 示例。

结果是:

  • 单元循环时间 - 0.1679
  • Cellfun Time - 2.9973
  • 循环阵列时间 - 0.0465
  • 阵列时间 - 0.0019

代码:

nTimes = 1000;
nValues = 1000;
myCell = repmat({0},1,nValues);
output = zeros(1,nValues);

% Basic operation
tic;
for k=1:nTimes
  for m=1:nValues
    output(m) = myCell{m} + 1;
  end
end
cell_loop_timeAdd=toc;    
fprintf(1,'Cell Loop Time %0.4f\n', cell_loop_timeAdd);

tic;        
for k=1:nTimes
  output = cellfun(@(in) in+1,myCell);
end
cellfun_timeAdd=toc;
fprintf(1,'Cellfun Time %0.4f\n', cellfun_timeAdd);


myData = repmat(0,1,nValues);
tic;
for k=1:nTimes
  for m=1:nValues
    output(m) = myData(m) + 1;
  end
end
loop_timeAdd=toc;
fprintf(1,'Loop Array Time %0.4f\n', loop_timeAdd);

tic;
for k=1:nTimes
    output = myData + 1;
end
array_timeAdd=toc;
fprintf(1,'Array Time %0.4f\n', array_timeAdd);
于 2013-08-17T05:52:29.583 回答
4

我将在我自己测试的结果中添加一个答案,但如果人们用他们的知识做出贡献,我会很高兴,这只是我所做的一个简单测试。

我已经测试了以下条件,单元格大小为 1000 和 1000 循环(总时间的结果,我可能必须运行超过 1000 次,因为我对结果有一点波动,但无论如何,这不是一篇科学文章):

  • 基本操作(总和)
    • 简单的 for 循环:0.2663 秒
    • 细胞乐趣:9.4612 秒
  • 字符串操作 (strcmp)
    • 简单的 for 循环:1.3124 秒
    • 细胞乐趣:11.8099 秒
  • 内置(空)
    • 简单的 for 循环:8.9042 秒
    • cellfun(字符串输入 ->参见此参考):0.0105 s
    • cellfun(fcn 句柄输入​​ ->参见此参考):0.9209 s
  • 非均匀(正则表达式)
    • 简单的 for 循环:24.2157 秒
    • cellfun(字符串输入):44.0424 秒

因此,似乎带有匿名函数调用的 cellfun 比简单的 for 循环慢,但如果您将使用内置的 matlab 方法,请使用 cellfun 并与字符串引号一起使用。这不一定适用于所有情况,但至少适用于测试的功能。

实现的测试代码(我远不是优化专家,所以这里是代码,以防我做错了):

function ...
  [loop_timeAdd,cellfun_timeAdd,...
  loop_timeStr,cellfun_timeStr,...
  loop_timeBuiltIn,cellfun_timeBuiltInStrInput,...
  cellfun_timeBuiltyInFcnHandle,...
  loop_timeNonUniform,cellfun_timeNonUniform] ...
  = test_cellfun(nTimes,nCells)

myCell = repmat({0},1,nCells);
output = zeros(1,nCells);

% Basic operation
tic;
for k=1:nTimes
  for m=1:nCells
    output(m) = myCell{m} + 1;
  end
end
loop_timeAdd=toc;

tic;
for k=1:nTimes
  output = cellfun(@(in) in+1,myCell);
end
cellfun_timeAdd=toc;

% String operation
myCell = repmat({'matchStr'},1,nCells); % Add str that matches
myCell(1:2:end) = {'dontMatchStr'}; % Add another str that doesnt match
output = zeros(1,nCells);

tic;
for k=1:nTimes
  for m=1:nCells
    output(m) = strcmp(myCell{m},'matchStr');
  end
end
loop_timeStr=toc;

tic;
for k=1:nTimes
  output = cellfun(@(in) strcmp(in,'matchStr'),myCell);
end
cellfun_timeStr=toc;

% Builtin function (isempty)
myCell = cell(1,nCells); % Empty
myCell(1:2:end) = {0}; % not empty
output = zeros(1,nCells);

tic;
for k=1:nTimes
  for m=1:nCells
    output(m) = isempty(myCell{m});
  end
end
loop_timeBuiltIn=toc;

tic;
for k=1:nTimes
  output = cellfun(@isempty,myCell);
end
cellfun_timeBuiltyInFcnHandle=toc;

tic;
for k=1:nTimes
  output = cellfun('isempty',myCell);
end
cellfun_timeBuiltInStrInput=toc;

% Builtin function (isempty)
myCell = repmat({'John'},1,nCells);
myCell(1:2:end) = {'Doe'};
output = cell(1,nCells);

tic;
for k=1:nTimes
  for m=1:nCells
    output{m} = regexp(myCell{m},'John','match');
  end
end
loop_timeNonUniform=toc;

tic;
for k=1:nTimes
  output = cellfun(@(in) regexp(in,'John','match'),myCell,...
    'UniformOutput',false);
end
cellfun_timeNonUniform=toc;
于 2013-08-17T00:47:53.257 回答
1

以下是我通常会如何决定使用哪种解决方案:

  1. 我可以通过简单的矩阵运算来做到这一点吗?这样做,它会是你能得到的最快的,而且通常更具可读性。
  2. 我在做一些简单的事情吗?转3,否则转4。
  3. 我可以这样做bsxfun吗?这样做,它会非常快并且希望可读。
  4. 否则使用简单的 for 循环

您可能会注意到,我几乎从不使用 cellfun 来提高性能。这是由于以下逻辑:

  1. Cellfun 的执行速度通常不会比循环快得多,并且主要适用于处理单元格。
  2. 如果性能很重要,您可能希望完全避免使用 cell,因此您不需要 cellfun。
于 2016-08-02T19:22:32.677 回答
0
clear all;
ntimes = 1000;

r = 100;
c = 100;
d = 100;
A = rand(r, c, d);
B = rand(r, c, d);

tic
for i = 1:ntimes
    for j = 1 : d
        result = A(:, :, j) * B(:, :, j);
    end
end
toc

A_cell = num2cell(A, [1 2]);
B_cell = num2cell(B, [1 2]);
tic
for i = 1 : ntimes
    result2 = cellfun(@(x, y) x*y, A_cell, B_cell, 'uni', 0);
end
toc

试试这个也许。再测试几次。平均而言,cellfun 比双循环更快。

于 2016-08-01T20:23:01.793 回答