Profiling techniques for GUI code

Hello,

I am developing a few UI Components for simple data visualisation (scopes, FFT etc.); as they are constantly redrawn performance is important and I’m looking for ways to profile various metrics. XCode’s Instrument is my tool of choice for the moment but I find it a bit cumbersome and not so informative. Plus, I’m willing to instrument my code.

Just OTOH here are a few pieces of info I’d be looking for :

  • global pressure on the GUI thread + dropped frames
  • local (one component) and cumulative (+ all children) :
    • paint time average
    • paint time variance/max (are some frames more expensive to compute than others?)
    • average paint() and repaint() frequency

My questions:

  1. What are the general techniques people use to profile and optimize UI code?
  2. Which JUCE functions are entry points to time paints? For instance, is all the (local) painting work done synchronously in the paint() method?
  3. Any tool, metrics or concepts I might have missed?

Thanks in advance!

All of the drawing code for your custom Components will come from your overridden paint() method (and paintOverChildren()) so if you need to calculate the time it takes to draw your component you can just get the start and end times at the top and bottom of your paint() method.

A few tips and tricks I’ve picked up over the years are:

  1. Only redraw what needs to be redrawn. IE if you have a static background, don’t be drawing it every frame along with your dynamic content.
  2. Draw text as little as often. Text rendering is expensive.
  3. If your component is static and takes longer to draw than drawing an image of the same size, call setBufferedToImage() on it so it only gets repainted when it needs to.
  4. If a Component has children, don’t do any painting in it. Backgrounds for example should be drawn in dedicated Components, not in a component that has other components as children, especially if those child components are expensive to draw.
  5. Don’t worry about performance that much. If your priority is to make a good product to be able to sell, just get your performance to be good enough and move onto other features. Unless you have a large team, it’s not worth the time it’ll take to highly optimise your entire GUI.

I’ve only ever used Visual Studio’s built in profiler for profiling GUI code so I’m afraid I can’t offer any advise on your other 2 questions.

9 Likes

Thanks, this helps @ImJimmi. I had seen and implemented these advices, except for 4.

If a Component has children, don’t do any painting in it.

Could you develop a bit? I could imagine the rationale behind it being that paint() of a child component doesn’t happen every time the parent is paint()ed, (so the background’s paint() could be avoided) but I’m not sure exactly when that happens.

It’s to do with the previous point about setBufferedToImage()

In short, if you call setBufferedToImage() on a Component, its paint() method will only get called if that Component is flagged as needing to be repainted (IE if you call repaint() on it), otherwise JUCE will draw the buffered image instead which may improve performace.

However, if the Component you call setBufferedToImage() on has a child component that is constantly redrawing, it will also invalidate the parent Component meaning even though nothing has changed in your parent, it will still have its paint() method called regularly.

If instead you put that drawing code in a dedicated Component and call setBufferedToImage() on it, it won’t be invalidated when its sibling is redrawn and the parent Component won’t have any drawing code so it won’t matter that it’s constantly being redrawn.

It’s not needed all the time, but it’s a good habit to get into IMO.

1 Like

be sure to call setOpaque (true); on the opaque components also.

3 Likes

Thanks @ImJimmi, it makes sense; @lalala yes I’m aware of that, thanks anyways.
That said I was more asking for profiling techniques, not optimization ones. I might just roll my own if nothing already exists.

The most important thing is to simply figure out which of your processes is taking up a significant chunk of time, which is something pretty much any basic profiler can do. I believe Xcode has Instruments which is a collection of profiling utilities but I’ve never used them myself - I’ve only ever used the built in profiler in Visual Studio on Windows.

They’ll usually point you to some low-level function that’s being called over and over again, taking up a lot of time so you just need to look up through the callstack to find where in your own code it’s being called. A flamegraph can help with that because it shows the callstack in a more visual way where it’s usually easier to see processes that are taking a long time.

This is something I have used when trying to improve the perf of my gui:

2 Likes

@jpo that’s exactly what I was looking for!
Thanks a bunch!

In case you haven’t come across it, there’s also the JUCE_ENABLE_REPAINT_DEBUGGING config option that will let you see which components are being repainted visually.

4 Likes

I’ve looked in the following file path:

juce_gui_basics/components/juce_Component.cpp.

And couldn’t find that debugging information.