Cellfun与简单的Matlab循环性能

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

有一天,我发现了cellfun ,这使得黑魔法变得相当简单,我可以改变许多使用cell和cellfun组合的循环,但有一天我看到一篇关于cellfun的文章 ,这让我怀疑我的遗传matlab知识是否真实是: 那个matlab循环总是比一个内置的编译函数慢 ,这是我有这么多的信心。 我在我的一个实现中testing了它,事实上for循环会更快 ! 我就是说,OMG,所有这些日子里做晦涩难懂的代码浪费了无用的哈哈哈。 从那天起,我就不再努力去尝试优化matlab代码,通常这取决于每种情况等等。

今天,我看到了这个答案 ,它记住了我尽可能避免matlab循环的努力(我不知道这是否是作者为了避免性能,但无论如何它提醒了所有这些matlab循环性能的东西)。 还有一个问题出现在我脑海里: Cellfun比循环更好吗? 什么时候会是真的?

如果性能是一个主要因素,你应该避免使用单元格,循环或cellfun / arrayfun。 使用向量操作通常要快得多(假设这是可能的)。

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

结果是:

  • 细胞循环时间 – 0.1679
  • Cellfun时间 – 2.9973
  • 循环arrays时间 – 0.0465
  • arrays时间 – 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); 

我会给自己testing的结果添加一个答案,但是如果人们用自己的知识来贡献,我会很高兴,这只是我做的一个简单的testing。

我已经testing了1000个和1000个循环的单元格大小(总计时间的结果,我可能需要运行超过1000次,因为我的结果有一点波动,但不pipe怎样,这不是一篇科学文章):

  • 基本操作(总和)
    • 简单的循环:0.2663秒
    • cellfun:9.4612秒
  • string操作(strcmp)
    • 简单的循环:1.3124秒
    • cellfun:11.8099 s
  • 内置(isempty)
    • 简单的循环:8.9042秒
    • cellfun(stringinput – > 见这个参考 ):0.0105秒
    • cellfun(fcn句柄input – > 看这个参考 ):0.9209 s
  • 非一致(正则expression式)
    • 简单的循环:24.2157秒
    • cellfun(stringinput):44.0424 s

所以,看起来匿名函数调用的cellfun比简单的for循环要慢,但是如果你要使用内置的matlab方法,可以用cellfun来完成,并用string引用。 对于所有情况,这不一定是正确的,但至less对于testing的function是如此。

实施的testing代码(我不是一个优化专家,所以这里是我做错了的代码):

 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; 
 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 

试试这可能。 再testing几次。 平均来说,cellfun比双循环更快。

以下是我通常会决定使用哪种解决scheme:

  1. 我可以使用简单的matrix操作吗? 做到这一点,这将是最快,你可以得到,通常更具可读性。
  2. 我在做简单的事吗? 转到3,否则转到4。
  3. 我可以用bsxfun吗? 做到这一点,这将是非常快速,希望可读。
  4. 否则使用一个简单的for循环

正如你可能会注意到,我几乎从来没有使用cellfun的性能。 这是由于以下逻辑:

  1. Cellfun通常不会比循环执行得快,而且对于处理细胞来说也是非常有用的。
  2. 如果性能很重要,你可能想避免所有的细胞,因此你不需要cellfun。