- Undocumented Matlab - https://undocumentedmatlab.com -

cellfun – undocumented performance boost

Posted By Yair Altman On May 11, 2009 | 18 Comments

Matlab’s built-in cellfun [1] function has traditionally enabled several named (string) processing functions such as ‘isempty’. The relevant code would look like this:

data = cellfun('isempty',cellArray);

In recent years, newer Matlab releases has added support for function handles, so the previous code snippet can now be written as follows:

data = cellfun(@isempty,cellArray);

The newer function-handle format is “cleaner” and more extensive than the former format, accepting any function, not just the limited list of pre-specified processing function names (‘isreal’, ‘islogical’, ‘length’, ‘ndims’, ‘prodofsize’). Some have even reported that the older format has limitations vis-a-vis compilation etc.
All this is well known and documented. However, it turns out that, counter-intuitively (and undocumented), the older format is actually much faster than the newer format for those pre-specified processing function names. The reason appears to be that ‘isempty’ (as well as the other predefined string functions) uses specific code-branches optimized for performance:

>> c = mat2cell(1:1e6,1,repmat(1,1,1e6));
>> tic, d=cellfun('isempty',c); toc
Elapsed time is 0.115583 seconds.
>> tic, d=cellfun(@isempty,c); toc
Elapsed time is 7.493989 seconds.

Perhaps a future Matlab release will improve cellfun’s internal code, to check for function-handle equality to the optimized functions, and use the optimized code branch if possible. When I posted this issue today as a correction to a reader’s misconception, Matlab’s Loren Shure commented as follows [2]:
We could improve cellfun to check function handles to see if they match specified strings. Even then MATLAB would have to be careful in case the user has overridden the built-in version of whatever the string points to.
While this comment seems to imply that the performance boost feature will be maintained and possibly improved in future releases, users should note that this is not guarantied. One could even argue that future code optimizations would be applied to the new (function-handle) format rather than the old string format. The performance pendulum might also change based on user platform. Therefore, users for whom performance is critical should always test both versions on their target system: ‘isempty’ vs. @isempty etc. (note that the corresponding function for ‘prodofsize’ is @numel).

Categories: Medium risk of breaking in future versions, Stock Matlab function, Undocumented feature


18 Comments (Open | Close)

18 Comments To "cellfun – undocumented performance boost"

#1 Comment By Ashish Sadanadnan On May 11, 2009 @ 16:26

They seem to already have improved it quite a bit in R2009a; here are my results from running your code:

>> c = mat2cell(1:1e6,1,repmat(1,1,1e6));

>> tic, d=cellfun('isempty',c); toc
Elapsed time is 0.032880 seconds.

>> tic, d=cellfun(@isempty,c); toc
Elapsed time is 0.563284 seconds.

#2 Comment By Yair Altman On May 11, 2009 @ 16:32

Ashish – actually your results show a factor of 17 between the slower @isempty and the faster ‘isempty’, consistent with the results I posted above (my reported factor of 65 is almost the same order of magnitude as 17, and may be due to external platform-dependent factors).

The absolute values of the results of course depend on the platform: my results were for a run-down heavily-loaded laptop… The important thing here is the factor between @isempty and ‘isempty’ – not the absolute values. And a factor of 17 is still high enough to be taken into consideration in a performance-critical application.

#3 Comment By Ashish Sadanadnan On May 12, 2009 @ 20:54

Yair,
I wasn’t disputing your results. Just wanted to show that the factor has improved significantly in newer version (65 to 17). Of course, 17 times faster is still very significant as you pointed out.

– Ashish.

#4 Comment By Naor On May 11, 2009 @ 18:12

wow. that’s a pretty significant unnecessary slowdown. at least this would be easy to catch with the profiler.

#5 Comment By Loren S On May 12, 2009 @ 04:10

Yair-

As I noted to you on my blog, MATLAB doesn’t convert from FH to string method because the user might have overridden whatever the method, e.g., isempty. MATLAB could, at runtime, see if it’s overridden, and if not, call the optimized version. But it can’t do that blindly without risk of wrong answers.

–Loren

#6 Pingback By datestr performance | Undocumented Matlab On October 5, 2011 @ 13:17

[…] Different performance hotspots can have different solutions: caching, vectorization, internal library functions, undocumented graphics properties, smart property selection, smart function selection, smart indexing, smart parameter selection etc. […]

#7 Comment By tdc On November 24, 2011 @ 04:48

I know this is old, but I just noticed it gets even worse if you use the other way of calling cellfun (which I’ve been using a lot):

>> c = mat2cell(1:1e6,1,repmat(1,1,1e6));

>> tic, d=cellfun('isempty',c); toc
Elapsed time is 0.034638 seconds.

>> tic, d=cellfun(@isempty,c); toc
Elapsed time is 0.859156 seconds.

>> tic, d=cellfun(@(x) isempty(x),c); toc
Elapsed time is 7.961039 seconds.

#8 Comment By Jordi Gutiérrez Hermoso On November 29, 2011 @ 09:30

I looked at your example, and I noticed that in Octave we hadn’t quite optimised this as much as possible. I went ahead and committed a change to fix this:

[9]

On my laptop with Intel Core 2 Duo @ 2.20G, I see the following:

octave:1> c = mat2cell(1:1e6,1,repmat(1,1,1e6));
octave:2> tic, d=cellfun('isempty',c); toc
Elapsed time is 0.0171831 seconds.
octave:3> tic, d=cellfun('isempty',c); toc
Elapsed time is 0.0182698 seconds.
octave:4> tic, d=cellfun('isempty',c); toc
Elapsed time is 0.0223808 seconds.

octave:5> tic, d=cellfun(@isempty,c); toc
Elapsed time is 0.0193319 seconds.
octave:6> tic, d=cellfun(@isempty,c); toc
Elapsed time is 0.01612 seconds.
octave:7> tic, d=cellfun(@isempty,c); toc
Elapsed time is 0.019449 seconds.

This should be part of our 3.6 release that should happen very soon!

Sadly, your preferred method of calling cellfun cannot be easily optimised:

octave:8> tic, d=cellfun(@(x) isempty(x),c); toc
Elapsed time is 0.924903 seconds.
octave:9> tic, d=cellfun(@(x) isempty(x),c); toc
Elapsed time is 0.873197 seconds.
octave:10> tic, d=cellfun(@(x) isempty(x),c); toc
Elapsed time is 0.875425 seconds.

Note that Octave still is single-threaded, so this does not benefit from any parallelisation right now. There’s work to build parallelisation into Octave, so perhaps we can see more dramatic speedups in the future.

#9 Comment By Jordi Gutiérrez Hermoso On November 29, 2011 @ 07:59

This is very interesting. Our independent implementation of cellfun in Octave actually behaves very similarly! However, I did optimise it to check function handles for built-in string cases.

We have a thread about it:

[10]

#10 Comment By Yair Altman On November 29, 2011 @ 08:05

@Jordi – thanks. If you have any other comparisons to Octave for any of the other articles here, please do post a comment.

#11 Comment By damayi On May 22, 2012 @ 19:48

MATLAB 2011b result:

>> c = mat2cell(1:1e6,1,repmat(1,1,1e6));
>> tic, d=cellfun('isempty',c); toc
Elapsed time is 0.032773 seconds.

>> tic, d=cellfun(@isempty,c); toc
Elapsed time is 2.100385 seconds.

>> 2.100385/0.032773
ans =
   64.0889

#12 Pingback By How to compute effectively string length of cell array of strings | PHP Developer Resource On May 28, 2012 @ 21:47

[…] – I found out the reason for the speedup. It is indeed recognition of length specifically. Thanks to @reve_etrange for the […]

#13 Comment By Rody Oldenhuis On July 1, 2014 @ 01:32

MATLAB R2013a result:

 
>> c = mat2cell(1:1e6,1,repmat(1,1,1e6));
>> tic, d=cellfun('isempty',c); toc
Elapsed time is 0.038423 seconds.

>> tic, d=cellfun(@isempty,c); toc
Elapsed time is 0.681082 seconds.

>> 0.681082/0.038423
ans =
   17.7259

The MathWorks don’t seem to be in a rush to optimize this…

#14 Comment By Marco Riani On October 19, 2015 @ 13:22

This is what happens in R2015b:

>> c = mat2cell(1:1e6,1,repmat(1,1,1e6));
>> tic, d=cellfun('isempty',c); toc
Elapsed time is 0.011079 seconds.

>> tic, d=cellfun(@isempty,c); toc
Elapsed time is 0.618598 seconds.

The gap is still present

#15 Comment By Carlo Monjaraz On December 13, 2016 @ 12:54

R2016a :/

>> c = mat2cell(1:1e6,1,repmat(1,1,1e6));
>> tic, d=cellfun('isempty',c); toc
Elapsed time is 0.008351 seconds.

>> tic, d=cellfun(@isempty,c); toc
Elapsed time is 0.461371 seconds.

>> 0.461371 / 0.008351
ans =
   55.2474

#16 Comment By Anon On December 11, 2017 @ 18:07

2017b

>> c = mat2cell(1:1e6,1,repmat(1,1,1e6));
>> tic, d=cellfun('isempty',c); toc
Elapsed time is 0.011489 seconds.

>> tic, d=cellfun(@isempty,c); toc
Elapsed time is 0.927122 seconds.

>> 0.927122/0.011489
ans =
   80.6965

#17 Comment By juanpi carbajal On March 22, 2018 @ 15:22

Octave 4.2.2

>> c = mat2cell(1:1e6,1,repmat(1,1,1e6));
>> tic, d=cellfun('isempty',c); toc
Elapsed time is 0.0282919 seconds.

>> tic, d=cellfun(@isempty,c); toc
Elapsed time is 0.0258329 seconds.

>> 0.0258329 / 0.0282919
ans =  0.91308

#18 Comment By Sunham On May 29, 2023 @ 06:59

This is an old article, but the issue persists even in 2023.
2023a:

z = mat2cell(1:1e6,1,repmat(1,1,1e6));
f = @() cellfun(‘isempty’,z);
g = @() cellfun(@isempty,z);

a1=timeit(f)
a1 =
0.0053
a2=timeit(g)
a2 =
0.7142

a2/a1
ans =
135.1974


Article printed from Undocumented Matlab: https://undocumentedmatlab.com

URL to article: https://undocumentedmatlab.com/articles/cellfun-undocumented-performance-boost

URLs in this post:

[1] cellfun: http://www.mathworks.com/access/helpdesk/help/techdoc/ref/cellfun.html

[2] commented as follows: http://blogs.mathworks.com/loren/2009/05/05/nice-way-to-set-function-defaults/#comment-30304

[3] datestr performance : https://undocumentedmatlab.com/articles/datestr-performance

[4] HG's undocumented parameters interface : https://undocumentedmatlab.com/articles/hgs-undocumented-parameters-interface

[5] sprintfc – undocumented helper function : https://undocumentedmatlab.com/articles/sprintfc-undocumented-helper-function

[6] Performance: scatter vs. line : https://undocumentedmatlab.com/articles/performance-scatter-vs-line

[7] More undocumented timing features : https://undocumentedmatlab.com/articles/more-undocumented-timing-features

[8] Undocumented scatter plot behavior : https://undocumentedmatlab.com/articles/undocumented-scatter-plot-behavior

[9] : http://hg.savannah.gnu.org/hgweb/octave/rev/cf8cd43cdeb3

[10] : http://octave.1599824.n4.nabble.com/More-cellfun-related-benchmarks-td3724314.html

Copyright © Yair Altman - Undocumented Matlab. All rights reserved.