Posts Tagged ‘performance’

Performance: scatter vs. line

Wednesday, October 14th, 2009

Following my previous article on the undocumented behavior of the scatter function, one of my readers, Benoit Charles, reported a discovery that in many circumstances the line function generates identical plots much faster than scatter.

Unlike scatter, line does not enable specific data-point marker customization, although the colors could be modified. On the other hand, line only uses a single handle object, saving memory and system resources compared to scatter keeping a separate handle for each data point. So, if you just need to quickly plot a bunch of scattered points then line could be a better choice than scatter.

Here is a simple code snippet, which generates identical plots and shows the performance difference:

>> x=rand(1000,1); y=rand(1000,1);
 
>> tic, for idx=1:100, cla; h=scatter(x,y); end; toc
Elapsed time is 2.521322 seconds.
 
>> props = {'LineStyle','none','Marker','o','MarkerEdge','b','MarkerSize',6};
>> tic, for idx=1:100, cla; h=line([x,x],[y,y],props{:}); end; toc
Elapsed time is 0.333369 seconds.

In the past, I have posted about other undocumented performance aspects, comparing the documented ismember function with the undocumented ismembc and about cellfun’s undocumented options. If you are aware of other similar functions having identical outputs and a significant performance difference, please let me know.

Bookmark and Share

tic / toc - undocumented option

Friday, May 22nd, 2009

I have recently discussed undocumented features in Matlab’s built-in profiler. Another tool often used for ad-hoc performance profiling of Matlab code are the built-in duo functions tic and toc:

>> tic; magic(500); toc
Elapsed time is 0.474647 seconds.

Both tic and toc are documented functions, but they contain an undocumented option (at least until R2008b - see history below) that enables nested clocking. The problem that is solved using this undocumented option is that if we try to use tic/toc in external function parentFcn and also in the internal function childFcn, we get unexpected results. This happens because our childFcn’s invocation of tic has reset the clock and so parentFcn’s clock is now obviously incorrect:

function parentFcn
 
    % Start clocking
    tic;
 
    % Do something long...
    m1 = magic(1500);
 
    % Call nested function
    toc1 = childFcn;
 
    % Present clock timing
    toc2 = toc;
    fprintf('Elapsed time for childFcn:  %0.3f secs\n',toc1)
    fprintf('Elapsed time for parentFcn: %0.3f secs\n',toc2)
 
 
    % Nested function with separate clocking
    function t = childFcn
        tic;
        m = magic(800);
        t = toc;
    end  % childFcn
 
end  % parentFcn
>> parentFcn
Elapsed time for childFcn:  1.063 secs
Elapsed time for parentFcn: 1.068 secs

The solution: use a separate handle for each clock. The format is very simple:

>> t = tic;
>> toc(t)

This format ensures independent clocking of the clocks. Clockings can now be safely nested and even partial overlap is possible:

function parentFcn
 
    % Start clocking
    t1 = tic;
 
    % Do something long...
    m1 = magic(1500);
 
    % Call nested function
    toc1 = childFcn;
 
    % Present clock timing
    toc2 = toc(t1);
    fprintf('Elapsed time for childFcn:  %0.3f secs\n',toc1)
    fprintf('Elapsed time for parentFcn: %0.3f secs\n',toc2)
 
 
    % Nested function with separate clocking
    function t = childFcn
        tic;
        m = magic(800);
        t = toc;
    end  % childFcn
 
end  % parentFcn
>> parentFcn
Elapsed time for childFcn:  1.123 secs
Elapsed time for parentFcn: 4.992 secs

For the record, this undocumented option was first presented by the MathWorks support team as an official workaround for the aforementioned problem. The solution was originally posted for Matlab R2006b, but actually the option was supported by Matlab versions at least as early as R14SP3 (7.1) - perhaps even earlier (I don’t have ready access to earlier versions). I do know that it was not supported on release R12 aka 6.0, but I don’t know whether it was introduced in 6.5, 7.0 or 7.1. This option has only been documented since R2008b, although it has existed in many earlier releases.

Now here’s a puzzle for all you undoc fans out there: toc has a really undocumented option of using an uninitialized uint64 value, rather than an actual tic handle. It seems that whichever value is passed will always result in the same result, but this result is uncorrelated to any possible event (start of Matlab, midnight etc.). It even works without any prior tic invocation! I would love to hear your thoughts on what you think this strange toc result might represent:

>> toc(uint64(1))
Elapsed time is 72825.947547 seconds.
>> toc(uint64(1234))
Elapsed time is 72826.538296 seconds.
Bookmark and Share

cellfun - undocumented performance boost

Monday, May 11th, 2009

Matlab’s built-in cellfun 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:

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).

Bookmark and Share

ismembc - undocumented helper function

Wednesday, April 8th, 2009

Matlab has a variety of internal helper functions which are used by the main (documented) functions. Some of these helper functions are undocumented and unsupported, but may be helpful in their own right - not just as internal support functions.

In this post I want to present Matlab’s built-in ismembc helper function. This function is used within the stock Matlab ismember and setxor functions for fast processing of the core ismember functionality in “regular” cases: arrays of sorted, non-sparse, non-NaN data in which we’re only interested in the logical membership information (not the index locations of the found members). In such cases, ismembc can be used directly, saving ismember’s sanity-checks overhead. ismembc uses the same interface (two inputs, single logical output) as ismember and can be a drop-in replacement for ismember for these “regular” cases.

The performance improvement may be significant: In a recent post, MathWorks’ Loren Shure presented different approaches for fast data retrieval, highlighting the ismember function. Let’s compare:

>> % Initial setup
>> n=2e6; a=ceil(n*rand(n,1)); b=ceil(n*rand(n,1));

>> % Run ismember several times, to rule-out JIT compilation overheads
>> tic;ismember(a,b);toc;
Elapsed time is 2.882907 seconds.
>> tic;ismember(a,b);toc;
Elapsed time is 2.818318 seconds.
>> tic;ismember(a,b);toc;
Elapsed time is 3.005967 seconds.

>> % Now use ismembc:
>> tic;ismembc(a,b);toc;
Elapsed time is 0.162108 seconds.
>> tic;ismembc(a,b);toc;
Elapsed time is 0.204108 seconds.
>> tic;ismembc(a,b);toc;
Elapsed time is 0.156963 seconds.

ismembc is actually a MEX file (%matlabroot%\toolbox\matlab\ops\ismembc.mexw32). Its source code is included in the same folder (%matlabroot%\toolbox\matlab\ops\ismembc.cpp) and is actually very readable. From the source code comments we learn that the comment in setxor about ismembc usage is misleading: that comment stated that the inputs must be real, but the source-code indicates that imaginary numbers are also accepted and that only the real-part should be sorted.

ismembc should not be used carelessly: as noted, its inputs must be sorted non-sparse non-NaN values. In the general case we should either ensure this programmatically (as done in setxor) or use ismember, which handles this for us.

The nice thing about ismembc is that its source code (ismembc.cpp) is included, so even if future Matlab releases stop using this function, you can always mex-compile the source code and use it.

Readers interested in ismembc might also be interested in its sibling help function, ismembc2, which is also a mex file located (with source-code) in the same folder as ismembc. Whereas ismembc returns an array of logical values, ismembc2 returns the index locations of the found members.

Bookmark and Share