8

When I started working with matlab sometime ago in the university, my supervisor would kill me if he saw any unnecessary for loop (he would ask for exchanging it for kron or any type of indexes manipulation as possible). Later on, I was trying to avoid as much as possible any loop on matlab, searching for the darkest matlab coding ways to do black magic instead of a simple loop.

And one day I discovered the cellfun, which made the black magic quite simplier, I could change many loops working with cells and cellfun combo, but one day I saw one post about cellfun which made me question if my inherited matlab knowledge was true, that is: that matlab loops would always be slower than one builtin compiled function, which was something I had so much faith. I tested it in one of my implementations and in fact, the for loop would be faster! I was like, OMG, all those days doing obscure code wasted for nothing hahaha. Since that day, I stopped working that hard to try to optimize matlab code, normally it depends for each case and so on.

Today I saw this answer, which remembered my effort for avoiding as much matlab loops as possible (I don't know if that was the author will to avoid for performance, but anyway it reminded all this matlab loop performance thing). And one question came to my mind: Is cellfun better than for loops? When would that be true?

Community
  • 1
  • 1
Werner
  • 2,537
  • 1
  • 26
  • 38
  • 1
    As for my answer that you've mentioned: yes, I try to avoid `for` loops, like you do (or used to do), and my motivation is performance. I think (or used to think?) it is true that `for` loops are usually slower and should be avoided. – Luis Mendo Aug 17 '13 at 00:57
  • Apparently `cellfun` is slower except in special cases. See http://www.mathworks.com/matlabcentral/answers/42335 and http://www.mathworks.com/matlabcentral/newsreader/view_thread/301894 – Luis Mendo Aug 17 '13 at 01:07
  • @LuisMendo Yes, exactly! But unfortunately `cellfun` with anonymous functions handle are usually slower than `for` loops… this would make me so bored for the effort for nothing. Thanks for the references. – Werner Aug 17 '13 at 01:09
  • 1
    It's a pity. I discovered `cellfun` and `arrayfun` just a few days ago, and I was getting the hang of them... – Luis Mendo Aug 17 '13 at 01:16
  • possible duplicate of [arrayfun can be significantly slower than an explicit loop in matlab. Why?](http://stackoverflow.com/questions/12522888/arrayfun-can-be-significantly-slower-than-an-explicit-loop-in-matlab-why) – Werner Aug 18 '13 at 19:23

4 Answers4

11

If performance is a major factor you should avoid using cells, loops or cellfun/arrayfun. It's usually much quicker to use a vector operation (assuming this is possible).

The code below expands on Werner's add example with standard array loop and array operations.

The results are:

  • Cell Loop Time - 0.1679
  • Cellfun Time - 2.9973
  • Loop Array Time - 0.0465
  • Array Time - 0.0019

Code:

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);
grantnz
  • 7,322
  • 1
  • 31
  • 38
  • Great, nice point and contribution. Just one detail: the cellfun tic toc also contains the fprintf function. This won't influence the results, but anyway… – Werner Aug 17 '13 at 19:24
4

I will add one answer with the results that I tested myself, but I would be glad if people contribute with their knowledge, this is just a simple test I've made.

I've tested the following conditions with cell size of 1000 and 1000 loops (results on total time, and I would probably have to run more than 1000 times, because I am having a little fluctuation on the results, but anyway, this is not a scientific article):

  • Basic operation (sum)
    • Simple for loop: 0.2663 s
    • cellfun: 9.4612 s
  • String Operation (strcmp)
    • Simple for loop: 1.3124 s
    • cellfun: 11.8099 s
  • Built-in (isempty)
  • Non-uniform (regexp)
    • Simple for loop: 24.2157 s
    • cellfun (string input): 44.0424 s

So, it seems that cellfun with anonymous function calls are slower than a simple for loop, but if you will use a builtin matlab method, do it with cellfun and use it with the string quotation. This is not necessarily true for all cases, but at least for the tested functions.

The implemented test code (I am far from being an optimization specialist, so here is the code in case I did something wrong):

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;
Werner
  • 2,537
  • 1
  • 26
  • 38
1

Here is how I would typically decide on which solution to use:

  1. Can I do it with straightforward matrix operations? Do it, it will be the fastest you can get and typically more readable.
  2. Am I doing something simple? Go to 3, otherwise go to 4.
  3. Can I do it with bsxfun? Do it, it will be very fast and hopefully readable.
  4. Otherwise use a simple for loop

As you may note, I pretty much never use cellfun for performance. This is due to the following logic:

  1. Cellfun does not perform much faster than a loop usually, and is mostly good for dealing with cells.
  2. If performance is important, you probably want to avoid cells alltogether, and as such you won't need cellfun.
Dennis Jaheruddin
  • 21,208
  • 8
  • 66
  • 122
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

Try this maybe. Test it for few more times. On average, cellfun is faster than the double loop.

user3390652
  • 132
  • 1
  • 13
  • The outer loop that you commented with "what is this doing?" is supposed to repeat the measured loop n-times, so that we reduce the result bias and tend to the mean execution time. If you remove it, as you did, you are comparing 1 execution time to other loops which are repeated n-times. – Werner Aug 01 '16 at 20:54
  • @Werner I have updated the code. I am arguing because I am doing similar things in my project. I have tested the double loop version in my project and the cellfun version, that cellfun is much faster than the double loop.. I have no idea why. But this example can show that cellfun is faster. – user3390652 Aug 02 '16 at 19:08
  • Your comparison seems not fair, the cell has dimension 1x2, and the rand has 100x100x100. Also, there is a bit of time that I last used matlab, but it seems you can change `for j = 1:d; result = A()…` by a `bsxfun` which would probably be faster. – Werner Aug 04 '16 at 22:39
  • 1
    @Werner no it is not 1x2, it is transforming to cell by maintaining dimension 1 and 2. But I agree that bsxfun would perform better. – user3390652 Aug 06 '16 at 18:27
  • I see, I missed the `num2cell(A…`. I ran your sample code and got these benchmarks: array `21.265852` seconds ; cell: `21.795821 seconds`. I used matlab 2013. Anyway, this topic idea was exactly to say that IF you need to optimize, then you optimize testing the methods before trying to do something very complex. In your specific case it doesn't seem to do much difference. – Werner Aug 10 '16 at 05:10