Updating text with timerCallback- high cpu


#1

Hi folks, I’m working on a cross-platform Juce app that I currently have running on OS X 10.7 and Windows 7. It’s an audio app in which I’m doing such things as displaying the audio amplitude in a Label (as a float value converted to String). I’m updating the String using a Timer with a 50 ms interval. I have multiple Labels (sometimes 25-50), but I’m updating them all using the same timerCallback.

Simple enough, right?

So, what I’ve noticed is that when I run the app on Mac, it uses like 70% of the CPU, vs. like 10% on Windows. But, when I hide the text boxes so that they don’t need to be updated by the timer, the CPU usage drops to below 10% on both systems.

I don’t quite understand why it uses so much more CPU on the Mac to update the Labels. Does it have something to do with rendering fonts? Anyway, it’s worth mentioning that my Mac is much newer (and I think much faster) than my PC - it’s a 2.5GHz Mini that I bought a few months ago, vs. my PC, which is a Core 2 Duo that’s almost 4 years old.

Any ideas? Please help if you can! Thanks!


#2

I found this post where Jules commented about cpu meters and such:

viewtopic.php?f=4&t=7328&hilit=cpu


#3

Please never post a question about performance based on what the “CPU meter” says!

ALWAYS profile your app first, to see what the hotspots are, and then if you still need to ask your question, you’ve got some actual useful evidence to present. “CPU” is a meaningless number unless you know what the CPU is being used for.


#4

Thanks, I didn’t see that post, sometimes it’s hard to know what keywords to use when searching for existing posts :).

I understand that the CPU meter can’t necessarily be trusted, but I do have another metric. In my app, I have an OpenGLComponent in which I am tracking the frame rate (fps), and the fps drops to around 40-50 when the Labels are being updated with a timerCallback in another Component. This is true even though I’ve specifically given the OpenGL thread a priority of 7 (which I assume is higher than the priority of the Juce Message Thread?). When I hide the Labels, the OpenGL frame rate goes back up to where it should be (~60). So I can’t necessarily agree that spare CPU cycles are being given to higher priority threads.

Appreciate any further insights.


#5

Go and PROFILE it, like I said!

If you actually tell us WHERE too much CPU is being used, then we might be able to help.

If you just keep quoting vague frame rates and cpu numbers, then you’re wasting everybody’s CPU time!


#6

However vague my description might be, perhaps there is someone out there who has experienced a similar situation. That’s all I’m trying to find out. I’m not forcing anyone to respond to my posts. If I’ve wasted someone’s time then I apologize.

If profiling is what I have to do, then that’s what I’ll do.


#7

(No, I was just attempting a “cpu time” joke there…)

But honestly, we get lots of questions like yours, and I can never understand why people get as far as the forum without having bothered to run a profiler first!


#8

Alright, well, here’s some information I got from the Time Profiler in the Instruments application:

When my application is exhibiting the high CPU usage, 85% of the time is spent in Component::paintEntireComponent. If I dig further, it seems that a lot of time is spent painting components that I’m not specifically asking to repaint, such as ComboBoxes, Buttons, etc. Could this be the issue?

As a random test, I tried putting a breakpoint in LookAndFeel::drawButtonText. This breakpoint gets hit immediately in XCode, every time, whereas in Visual Studio it only gets hit if I move the mouse over the button.

Thoughts? Anything else I should try?

edit This seems to be consistent with what other users are experiencing in http://rawmaterialsoftware.com/viewtopic.php?f=4&t=7328&hilit=cpu


#9

The first question I have is, are you having a performance issue? That is, does the app stutter or have problems on OS-X? Or is this just an observed phenomena with observed CPU metering? From your original description, it isn’t clear.

Second, I’d be curious what refresh rates you are actually achieving. I haven’t looked, but I assume that JUCE uses NSTimer on OS-X and a Win32 Timer on Windows. Windows timers are really low priority. It is just a flag in the dispatch loop. NSTimer is a little smarter and more aggressive, it will schedule on the next interval for process overruns, as opposed to ‘schedule next at end’. It’s possible that you are getting a significantly higher update rate on OS-X. You could log time stamps or something to get a quick idea.

As far as what is and isn’t being redrawn, if you are having performance issues (vs. just high usage reports) and you think that too much is being redrawn, you might want to walk through updating a single label with a debugger. I haven’t really had one of these problems with Juce yet, but I often find that it is something simple (transparency, how I invalidate, etc.) that causes me to draw too much.


#10

Yes, I am having a performance issue. Besides the OpenGL frame rate dropping, as I described earlier, other things are affected as well, such as native OS X File Choosers running very slow, almost unusably slow.

If you look at the code in the Timer class, it’s just a Thread with a periodic wait() call. It’s not platform-specific and there are no NSTimers or Win32 Timers being used. Also, the Timer is asynchronous, meaning the callback gets called via a message posted on the message queue. The callback isn’t called on the Timer thread. So the priority doesn’t matter as much.

Once you start getting into the Component class, the code path is the same on OSX vs Windows. So debugging that aspect of it doesn’t really help. It’s only in the LowLevelGraphicsContext that things are different. I’m investigating that, but I don’t think debugging a single Label is relevant.


#11

Is it this?
http://www.rawmaterialsoftware.com/viewtopic.php?f=4&t=6701


#12

Yes, I think it is!

So does that mean I’m stuck? This really affects the usability of my app on OS X. My testers have already complained about it. What can I do?


#13

Well, I can’t really add anything that’s not already been said on that other thread. The OSX API simply doesn’t provide a way to fix it.


#14

Ok, then can you explain more specifically what’s wrong with his proposed fix in that thread? It seems reasonable to me, though I’m no Cocoa expert.


#15

His suggestion would ruin the performance in every other situation except this one.

There’s at least one other long thread about the same thing, if you want more info. I’m not going to repeat it all yet again here.


#16

Interesting. Now that I know what to look for, there seems to be a lot of threads that are essentially talking about this same problem. So I gather that I’m not the only one frustrated about it :).

Wouldn’t it be possible in some way to solve this by using Component’s bufferedImage? Here’s what I mean. Right now, it looks as though when a Component is repainted, its bufferedImage is set to Image::null no matter what. But if only a small part of the Component has changed, it doesn’t make sense to throw the whole bufferedImage away – you just need to redo a small section of it. Couldn’t there be some kind of clipping logic here? That way, you’d be able to repaint a child Component into the parent’s existing Image. Make sense?


#17

That does make sense, and that’s the kind of thing that could be done with the new CachedComponentImage class.