Plot performance

I recently consulted to a client who wanted to display an interactive plot with numerous data points that kept updating in real-time. Matlab’s standard plotting functions simply could not keep up with the rate of data change. Today, I want to share a couple of very simple undocumented hacks that significantly improve plotting performance and fixed my problem.

I begin by stating the obvious: whenever possible, try to vectorize your code, preallocate the data and other performance-improving techniques suggested by Matlab. Unfortunately, sometimes (as in my specific case above) all these cannot help. Even in such cases, we can still find important performance tricks, such as these:

Performance hack #1: manual limits

Whenever Matlab updates plot data, it checks whether any modification needs to be done to any of its limits. This computation-intensive task is done for any limit that is set to ‘Auto’ mode, which is the default axes limits mode. If instead we manually set the axes limits to the requested range, Matlab skips these checks, enabling much faster plotting performance.

Let us simulate the situation by adding 500 data points to a plot, one at a time:

>> x=0:0.02:10; y=sin(x);
>> clf; cla; tic;
>> drawnow; plot(x(1),y(1)); hold on; legend data;
>> for idx = 1 : length(x); plot(x(idx),y(idx)); drawnow; end;
>> toc
 
Elapsed time is 21.583668 seconds.

simple plot with 500 data points

simple plot with 500 data points

And now let’s use static axes limits:

>> x=0:0.02:10; y=sin(x);
>> clf; cla; tic; drawnow; 
>> plot(x(1),y(1)); hold on; legend data;
 
>> xlim([0,10]); ylim([-1,1]);  % static limits
 
>> for idx = 1 : length(x); plot(x(idx),y(idx)); drawnow; end;
>> toc
 
Elapsed time is 16.863090 seconds.

Note that this trick is the basis for the performance improvement that occurs when using the plot’s undocumented set of LimInclude properties.

Of course, setting manual limits prevents the axes limits from growing and shrinking automatically with the data, which can actually be a very useful feature sometimes. But if performance is important, we now know that we have this tool to improve it.

Performance hack #2: static legend

Hack #1 gave us a 22% performance boost, but we can do much better. Running the profiler on the code above we see that much of the time is spent recomputing the legend. Looking inside the legend code (specifically, the legendcolorbarlayout function), we detect several short-circuits that we can use to make the legend static and prevent recomputation:

>> x=0:0.02:10; y=sin(x);
>> clf; cla; tic; drawnow; 
>> plot(x(1),y(1)); hold on; legend data;
 
>> xlim([0,10]); ylim([-1,1]);  % static limits
 
>> % Static legend
>> set(gca,'LegendColorbarListeners',[]); 
>> setappdata(gca,'LegendColorbarManualSpace',1);
>> setappdata(gca,'LegendColorbarReclaimSpace',1);
 
>> for idx = 1 : length(x); plot(x(idx),y(idx)); drawnow; end;
>> toc
 
Elapsed time is 5.209053 seconds.

Now this is much much better – a 76% performance boost compared to the original plot (i.e., 4 times faster!). Of course, it prevents the legend from being dynamically updated. Sometimes we actually wish for this dynamic effect (last year I explained how to use the legend’s undocumented -DynamicLegend feature for even greater dynamic control). But when performance is important, we can still display a legend without its usual performance cost.

In conclusion, I have demonstrated that Matlab performance can often be improved significantly, even in the absence of any vectorization, by simply understanding the internal mechanisms and bypassing those which are irrelevant in our specific case.

Have you found other similar performance hacks? If so, please share them in the comments section below.

Categories: Handle graphics, Low risk of breaking in future versions, Stock Matlab function, Undocumented feature

Tags: , , ,

Bookmark and SharePrint Print

31 Responses to Plot performance

  1. Perttu Ranta-aho says:

    For comparison, your last code gives for me: 4.42 seconds, by replacing plot with line in for loop times to 4.0 seconds. In general line is more “raw” and thus faster than plot. However, nothing beats direct modification of the first plots x/ydata:

    x=0:0.02:10; y=sin(x);
    clf; cla; tic; drawnow; 
    h=plot(x(1),y(1),'.','markersize',1); hold on; legend data;
    xlim([0,10]); ylim([-1,1]);  % static limits
    % Static legend
    set(gca,'LegendColorbarListeners',[]); 
    setappdata(gca,'LegendColorbarManualSpace',1);
    setappdata(gca,'LegendColorbarReclaimSpace',1);
     
    for idx = 1 : length(x); set(h,'xdata',x(1:idx),'ydata',y(1:idx)); drawnow; end;
    toc
    % Elapsed time is 1.142915 seconds.

    But obviously the result is not the same, since there is now only one line object instead of 501 that your code creates.

  2. Quentin says:

    I have had similar experiences to the previous commenter. For updating an existing plot, it’s always much faster to modify the properties of an existing object than delete old ones and create new ones (as is done in repeated calls to plot). I would also add that in a large GUI with many graphics objects, calls to “gca”, “gco” and “gcf” start to slow down considerably, so it’s always better to store the handles in advance and avoid calling any of those functions to get the handles, particularly for operations that are being done repeatedly inside loops.

  3. I agree with Perttu. Whenever I want performance, I manually set the xdata and ydata. This doesn’t cause any of the automagic stuff (like limit picking and legend setting) to happen.

    My spectrum scope on the file exchange shows this coding pattern, including tracking limits to make sure data isn’t cut off.

  4. Yuri says:

    Yair,

    First off – thanks.

    Second – did you ever happen to need to “plot” icons instead of the usual circle/triangle markers?

    I need to show icons (e.g. picture of a house, or a car) on the axes that behave like markers: not change size on zoom, and stay at the same axes coordinates.

    Could you suggest how to do that?

    • @Yuri – I have never tried to use icons as markers. You can control the images by designing a simple zoom callback hook function, that gets invoked whenever the axes are zoomed:

      hZoom = zoom(hFig);
      hZoom.ActionPostCallback = @cbZoom;

      Within your cbZoom callback function, simply reset all your marker images to the requested size.

  5. Nate says:

    Yair,
    Don’t feel like you have to divulge all your secretes but I have to ask how do you find all these all these undocumented listeners and events? For instance I was recently trying to create a listener for when someone plotted on an axes and it was only after googling pretty extensively that I found the event that I needed to look for but could not find any information on how that person knew that the ‘ObjectChildAdded’ event existed. The same goes for the ‘LegendColorbarListeners’ property you use above. Is there anyway to find this information myself so I don’t have to bother you with unending questions? I tried using findjobj and uiinspect but didn’t see how to get the information from there.

    Thanks,
    Nate

    • @Nate – there are many sources to my knowledge: I learn from posts by other people on the CSSM newsgroup; I learn from publicly-available Matlab m-code (much of the Matlab code-based resides in regular editable m-files, including all the legend code); I learn from tools like uiinspect, findjobj and checkClass; I learn from other people who are gracious enough to report stuff to me; and I learn a lot by simple trial and error. There is never one single way.

      I am not doing anything special that any other Matlab developer can’t do. But over the past years I have gathered lots of information and this helps me to more easily extrapolate where to look for answers, faster perhaps than most people. Still, I sometimes look for an answer to a particular question for many months, and I still do not know the answer to many things. There is still much left to be learned.

      p.s. – here’s another humorous answer to the same question, by U(r)s Schwartz, who is one of my information sources. This is actually one of my favorite posts on CSSM :-)

      -Yair

    • Nate says:

      hahaha. That is quite the post. Well thanks for putting a lot of the information in one easy to use website. While there might not be any other easy shortcuts your knowledge definitely helps out.

      Thanks,
      Nate

    • Nate says:

      P.S. Thanks for the hint about the legend code. It is a treasure trove.

  6. Helge says:

    (1) Don’t forget to set(hAxes,’DrawMode’,’fast’) whenever appropriate! This will tell the renderer NOT to check
    which of the lines, patches etc need to be displayed on top of the others. Instead, Matlab will simply redraw
    objects following the order in which they were created. I do not know whether DrawMode=’fast’ will have any effect
    if we set(hg,’XData’,…) directly. However, as far as I understand, DrawMode=’fast’ will never slow down plotting.

    (2) If I know that out-of-range data can never occur, I am usually setting the Clipping property so that Matlab
    won’t waste any time to check whether data beyond xlim/ylim may need to be excluded from display.

    (3) Finally, lots of automatic checking and property reset actions can be avoided by set(hAxes,’NextPlot’,’replacechildren’);

    (4) One note about unnecessary legend updates:
    Whenever in doubt if legend updates might be an issue, I simply create a secondary axes, a2=copyobj(…),
    draw some fake data points or lines outside xlim/ylim, create the legend once and set(a2,’Visible’,’off’);
    Refreshing the data in the main (first) axes does then obviously not callback any of those nasty legend actions.

    — btw: I did not run any performance tests for my suggestions, since actual performance improvement may vary
    depending on the data/task. However, once again I found some great tips on this site. Thank to all of you!

    H;

  7. Pingback: datestr performance | Undocumented Matlab

  8. Luke says:

    Not sure if this is obvious or not what you’re looking for, but I’m fairly certain the fastest way to plot is to plot to an invisible figure.

    Here are my timings for your three:

    Elapsed time is 15.002303 seconds.
    Elapsed time is 13.848813 seconds.
    Elapsed time is 4.799191 seconds.

    And for Perttu’s:

    Elapsed time is 1.375230 seconds.

    Now with a minor modification to your original code:

    x=0:0.02:10; y=sin(x);
    clf; cla; tic;
    drawnow; plot(x(1),y(1)); set(gcf,'Visible','off'); hold on; legend data;
    for idx = 1 : length(x); plot(x(idx),y(idx)); drawnow; end; set(gcf,'Visible','on');
    toc
    Elapsed time is 0.647195 seconds.
    • shabbychef says:

      would it not be more efficent to remove the drawnow from the loop and put it at the end? like so:

      x=0:0.02:10; y=sin(x);
      clf; cla; tic;
      drawnow; plot(x(1),y(1)); set(gcf,'Visible','off'); hold on; legend data;
      for idx = 1 : length(x); 
         plot(x(idx),y(idx)); 
      end; 
      drawnow; set(gcf,'Visible','on');
      toc
    • @ShabbyChef – indeed, but then you would not see the effect on the legend. My point in this article was simply to illustrate ways in which plot legends can be optimized. Naturally, plotting in an invisible figure and moving drawnow out of the loop would improve the plotting performance, as well as using the vectorized version of the plot function. But again, I wanted to illustrate the point about the legend, which is why I used a simplistic sub-optimal plotting loop.

  9. Sabina says:

    Hello !
    First of all thank you for all the tips you posted here!

    I have the same problem with an interactive plot, a real time plot. I solved the problem but i wonder, is there a way to include this plot into a GUI? i can’t include the plot into a GUI figure, it always opens a new figure.

  10. Pingback: A circular reference » Real Time Plotting with Matlab

  11. Ippei Kotera says:

    Hi Yair,

    Thanks for the useful suggestion and other stuff elsewhere. You have helped me a lot!

    Anyway, by combining your suggestion and Perttu’s along with ‘EraseMode’ = ‘none’ trick, the plotting speed could be boosted little further.

    x=0:0.02:10; y=sin(x);
    clf; cla; tic; drawnow; 
    h=line(x(1),y(1)); hold on; legend data;
    set(h,'EraseMode','none');
    xlim([0,10]); ylim([-1,1]);  % static limits
    % Static legend
    set(gca,'LegendColorbarListeners',[]); 
    setappdata(gca,'LegendColorbarManualSpace',1);
    setappdata(gca,'LegendColorbarReclaimSpace',1);
     
    for idx = 1 : length(x); set(h,'xdata',x(idx),'ydata',y(idx)); drawnow; end;
    toc
    Elapsed time is 0.469131 seconds.

    But now the legend is little messed up. I wonder how you would fix that without compromising the speed.

    • @Ippei – you could set up a single-shot timer that will run after some non-zero time delay, fixing the legend. This will enable your main function to process unhindered, and the legend will be updated a bit later, seemingly in parallel. In practice the timer would still use the main Matlab thread so the total run time would not improve, but the perceived performance would improve since the main bulk of you function would end sooner.

      Alternately, you could set up the legend only after the main function ends, providing similar improved perceived performance although the total run-time will remain unchanged.

  12. cuisinart says:

    Thank you for the EraseMode suggestion. I am plotting in a GUI using the set data directly to YData (3 plots), while it worked OK I could only update the graph at about 12 Hz and still use 50% of a core. Now with EraseMode of the plots’ lines set to ‘background’, I can update the plots at a full 30 Hz and only use 4% of a core. HUGE difference.

    thanks

  13. Pingback: Handle Graphics Behavior | Undocumented Matlab

  14. Chandrakanth says:

    Won’t replacing xdata and ydata using ‘set’ make it faster than using plot command? When I replaced plot function with the following

    set(p,'XData',x(1:idx),'YData',y(1:idx))

    I see 25% improvement. Did i miss something?

    • @Chandrakanth – indeed so, and this was already pointed-out by Perttu, Scott and others. However, my point in the article was to use static axes limits, and I just used the loop to show that it runs faster with static axes limits. We need to compare apples to apples, which is why I used exactly the same plot-update loop. It will also run faster with static limits if you change the loop to set the xdata/ydata properties rather than re-plot. So going back to your example: yes, set(p,'xdata',...) is faster, but it will be even faster if you use it with the static axes limits that I have shown (and even faster with a few other tricks).

  15. Alessandro says:

    Thank you very much. I really enjoyed this topic!

  16. chfakht says:

    i want to use matlab for REAL TIME plotting from the serial port …
    When i run it in matlab , it runs very well , but it stops after about 47 sec , and every time i re-execute it it stops again in something like 47 sec …

    i have improved the performance by doing what you said but without any improvement
    what will be the problem

    • @chfakht – I assume that there is some programmatic or algorithmic bug in your program. From what you describe, it seems that the problem is not in the code performance (speed), but rather in the code correctness. If you’d like me to help you debug your program, contact me by email for a consulting offer.

  17. Pingback: Performance-Tweaks beim Plotten mit MATLAB | Stefan Grandl

  18. Alex Perrone says:

    Great post. How can I implement static legend in 2014b? Specifically, given the axes handle, it doesn’t let me implement your trick:

    set(my_ax, 'LegendColorbarListeners',[]);  % my_ax is the axes where the legend is
    Error using matlab.graphics.axis.Axes/set
    There is no LegendColorbarListeners property on the Axes class.

    However, if I change it to setappdata , that works: setappdata(my_ax, ‘LegendColorbarListeners’,[]). I can also do the other two calls with setappdata, but they don’t seem to help performance at all. I set them before and after making the legend and also at beginning of my ‘update’ function, to no avail — still a whole bunch of slow legend updates. Any ideas to make static legend in 2014b?

    • @Alex – you can try the following in R2014b onward:

      my_ax.AutoListeners__ = {};

      Note that there are less risky alternatives for disabling legend for specific plot lines, for example:

      hasbehavior(hPlotLine,'legend',false);

      as described here: http://undocumentedmatlab.com/blog/handle-graphics-behavior

    • Alex Perrone says:

      Thanks Yair, appreciate the response. I had a bug in setting static axis limits for the plot. I was not setting them on the plot with the legend (but rather another plot mistakenly). I set static limits by manually calculating my own max and min for x- and y-values. Once this was corrected, this got rid of all the legend update calls. It reduced the number of function calls as enumerated by profile(‘info’) from about 90 to 5, and time to update the plot (using set on XData and YData in all cases) from ~0.20 seconds to ~0.02 seconds, a 10x improvement. Persistence! Thanks, Yair.

Leave a Reply


Your email address will not be published. Required fields are marked *