Cellfun versus Simple Matlab Loop performance
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?
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);
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)
- Simple for loop: 8.9042 s
- cellfun (string input -> see this reference): 0.0105 s
- cellfun (fcn handle input -> see this reference): 0.9209 s
-
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;
Here is how I would typically decide on which solution to use:
- Can I do it with straightforward matrix operations? Do it, it will be the fastest you can get and typically more readable.
- Am I doing something simple? Go to 3, otherwise go to 4.
- Can I do it with
bsxfun
? Do it, it will be very fast and hopefully readable. - 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:
- Cellfun does not perform much faster than a loop usually, and is mostly good for dealing with cells.
- If performance is important, you probably want to avoid cells alltogether, and as such you won't need cellfun.