【问题标题】:Cellfun versus Simple Matlab Loop performanceCellfun 与简单的 Matlab 循环性能
【发布时间】:2013-08-19 11:49:06
【问题描述】:

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

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

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

【问题讨论】:

标签: performance matlab


【解决方案1】:

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

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

结果是:

  • 单元循环时间 - 0.1679
  • Cellfun 时间 - 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);

【讨论】:

  • 很好,很好的观点和贡献。只有一个细节:cellfun tic toc 还包含 fprintf 函数。这不会影响结果,但无论如何……
【解决方案2】:

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

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

  • 基本运算(总和)
    • 简单的 for 循环:0.2663 秒
    • cellfun:9.4612 秒
  • 字符串操作(strcmp)
    • 简单的 for 循环:1.3124 秒
    • cellfun:11.8099 秒
  • 内置(空)
  • 非统一(正则表达式)
    • 简单的 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;

【讨论】:

    【解决方案3】:
    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 比双循环更快。

    【讨论】:

    • 您用“这是在做什么?”评论的外循环应该重复测量的循环 n 次,以便我们减少结果偏差并趋向于平均执行时间。如果你删除它,就像你做的那样,你将 1 个执行时间与重复 n 次的其他循环进行比较。
    • @Werner 我已经更新了代码。我争论是因为我在我的项目中做类似的事情。我已经在我的项目中测试了双循环版本和 cellfun 版本,cellfun 比双循环快得多。我不知道为什么。但是这个例子可以表明 cellfun 更快。
    • 您的比较似乎不公平,单元格的尺寸为 1x2,而兰特的尺寸为 100x100x100。另外,我上次使用 matlab 有一段时间,但似乎您可以将 for j = 1:d; result = A()… 更改为 bsxfun,这可能会更快。
    • @Werner 不,它不是 1x2,它通过保持维度 1 和 2 转换为单元格。但我同意 bsxfun 会表现更好。
    • 我明白了,我错过了num2cell(A…。我运行了您的示例代码并获得了这些基准: array 21.265852 seconds ;单元格:21.795821 seconds。我使用了matlab 2013。无论如何,这个主题的想法就是说如果你需要优化,那么你在尝试做一些非常复杂的事情之前优化测试方法。在您的具体情况下,它似乎没有太大区别。
    【解决方案4】:

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

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

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

    1. Cellfun 的执行速度通常不会比循环快多少,但主要适用于处理单元格。
    2. 如果性能很重要,您可能希望完全避免使用 cell,因此您不需要 cellfun。

    【讨论】:

      猜你喜欢
      • 2013-04-15
      • 2018-01-20
      • 1970-01-01
      • 2013-01-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-16
      相关资源
      最近更新 更多