Over-updating…


Raymond writes an interesting post about how some programs run faster if you hold down the mouse.


This is a classic situation to get yourself into – I’ve probably seen it 5 times, and hit it again recently.


When we’re burning a DVD, the burn engine calls back into the UI layer so that we can update the progress bar. To give the user a single monotonically-increasing progress bar, we (well, Dean, actually) have to play some games with our progress tracking, which means that sometimes we get progress callbacks once a second, and sometimes we get them 100 times a second.


The progress dialog looks fine, but if you look a profile of the burn, you’ll find that a fair amount of time is being spent in the UI code updating. In this case, it’s not a big deal, but I’ve seen cases where programs are spending 90% of their time updating the UI. That takes a 10 second operation and makes it take 100 seconds, which is pretty bad.


The fix? Well, there are two good ones.


The simplest one is to simply update every <N> calls. I traditionally start with N=10, which is guaranteed to same 90% of the overhead, and usually works fine.


If your callbacks are sporadic – as they are in the DVD Maker case – it works better to timebox the updates. Whenever you update the UI, record the time, and then don’t update it again until a specific time has gone by. I usually find 1/4 second to work well.


 

Comments (6)

  1. Maurits says:

    Is there an "advanced" option to see the actual status of the underlying dependency chain rather than the single monotonically increasing progress bar?

  2. ericgu says:

    No, there is no advanced option. I don’t think there is a lot of utility in it – DVD burning will take hours with typical content.

  3. Miral says:

    I think you missed the point of Raymond’s post — or perhaps I’m misinterpreting what you’re saying.

    The "worker" code should *never* call into UI code.  Ever.

    At most it could post a message to the UI thread, but preferably any interaction should be the other way around, with the UI code either polling for new status information (if updates are frequent) or sleeping on an event object that the worker signals (if updates are infrequent).

    The simplest way to do this is to do all your progress updates from within a window timer callback (System.Windows.Forms.Timer, for managed code), in which you query the worker objects for info on how far they’ve gotten.

  4. Miral says:

    Oh, and regarding the "advanced view" — that’s exactly why you *should* be able to get more detail, so that you can see whether it’s still encoding video or whether it’s made it through to constructing the IFO tables (or whatever).  And if it’s encoding video, which source file it’s currently working on.

    People like to know details about what’s actually going on, especially for long-running processes.  Otherwise there’s the tendency to think it just locked up.

  5. ericgu says:

    Miral,

    I was a bit imprecise here. It is true that you can’t update UI from a non-UI thread, but in this case the callback calls a UI method which uses a PostMessage, so there’s no issue.

    On the "more detail" topic, it’s something we could do, but I’d rank it pretty low down the feature list.

  6. Alexd's says:

    Eric Gunnerson (of C# designer fame) writes about over-updating.