Displaying progress during long operations – some thoughts. (UPDATED)

Humans generally hate to wait.  So as software developers we try to make everything as fast as possible.  Sometimes things just take time.  Like when you have to do 38,000 of something moderately slow.

Finite progress reporting (by which I mean “X of Y” or “X%”) is generally better than indeterminate reporting (a spinner which shows only that something is happening, if just the spinner spinning) from a user’s perspective.

Experienced programmers will have analyzed the performance on their application and realized that in some cases the updating of the progress count UI is actually taking a significant percentage of the total time to do the operation.  The usual “fix” is to only update the count after processing a chunk of items – say 10, 50, or 100 items – instead of after each one.  This amortizes the cost of updating the screen across more than one item and reduces the total number of updates which thus makes updating the UI a smaller percentage of the total time for the operation.  I often see people use round numbers like 10, 50 or 100 in a modulo test like this:

if (done % 100 == 0 )
    [self updateProgressDone: done of: total];

Here’s the thing I just noticed: as a user it doesn’t feel as fast or good to see something count up by hundreds, or even fifties.  I decided that it was because so many of the digits aren’t changing for such large percentage of the time I watched.  So I tried changing the modulo to 99 so that all the digits change and sure enough: it feels faster.  Fascinating!

I then tried something else that I kind of like too:  take the performance hit on the last few and display them all at the end like this:

if (done % 99 == 0 || done + 100 > total  )
    [self updateProgressDone: done of: total];

So the UI will visually count up by 99 at a time until the last 100 and then it’ll count up by ones.  If that’s too slow you could count by fives at the end like this:

if (done % 99 == 0 || (done + 100 > total && done % 5 == 0) )
    [self updateProgressDone: done of: total];

Anyway, something I was playing with today.  I’m sure there’s a lot more interesting psychology involved with this (I remember reading about a progress bar that speeds up it’s reporting over time), but this is an interesting simple change that really made a difference in some UI I just wrote.  I’d love to do actual research on this but alas I have software to write and ship 🙂

2013.03.17 UPDATE:

So after using this on various machines with varying other loads on the machine I decided that anything which isn’t time based is doomed to failure because what matters is how often the user perceives an update of status happening.  So I’ve switched to the following:

static dispatch_time_t lastUpdate = 0;
dispatch_time_t now = dispatch_time( DISPATCH_TIME_NOW, 0);

if ( now - lastUpdate > 250 * NSEC_PER_MSEC )
  lastUpdate = now;
  [self updateProgressDone: done of: total];

and am updating every quarter second (250ms).  I may play with putting the ramping up of updates in there (starting with say 350ms and going to 200ms towards the end, or something like that), but this actually seems good enough now and handles the case where the app is running on a slow machine and spotlight is dogging the entire machine etc.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: