Cellfun与简单Matlab循环性能

时间:2013-08-17 00:47:53

标签: performance matlab

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

有一天,我发现了cellfun,这让黑魔法更加简单,我可以改变很多使用单元格和cellfun组合的循环,但有一天我看到一个post about cellfun这让我怀疑我的继承matlab知识是否真实,那就是: matlab循环总是比一个内置编译函数慢,这是我非常有信心的东西。我在我的一个实现中测试了它,事实上, for循环会更快!我觉得,OMG,那些日子里做的模糊代码浪费了什么哈哈哈。从那天起,我就停止了努力尝试优化matlab代码,通常它取决于每个案例等等。

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

4 个答案:

答案 0 :(得分:11)

如果性能是一个主要因素,则应避免使用cell,loops或cellfun / arrayfun。 使用向量操作通常要快得多(假设这是可能的)。

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

结果是:

  • Cell Loop Time - 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);

答案 1 :(得分:4)

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

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

  • 基本操作(总和)
    • 简单循环:0.2663 s
    • cellfun:9.4612 s
  • 字符串操作(strcmp)
    • 简单循环:1.3124 s
    • cellfun:11.8099 s
  • 内置(isempty)
  • 非均匀(正则表达式)
    • 简单循环:24.2157 s
    • cellfun(字符串输入):44.0424 s

因此,似乎带有匿名函数调用的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;

答案 2 :(得分: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比双循环更快。

答案 3 :(得分:0)

以下是我通常决定使用哪种解决方案的方法:

  1. 我可以通过简单的矩阵运算来实现吗?这样做,它将是您获得的最快速度,通常更具可读性。
  2. 我做的很简单吗?转到3,否则转到4。
  3. 我可以使用bsxfun吗?做到这一点,它将非常快速,并且希望可读。
  4. 否则使用简单的for循环
  5. 正如您可能注意到的,我几乎从不使用cellfun来提高性能。这是由于以下逻辑:

    1. Cellfun的执行速度通常不如循环,并且最适合处理细胞。
    2. 如果性能很重要,您可能希望完全避免使用单元格,因此您不需要使用cellfun。