views:

448

answers:

5

The application I'm writing performs a length algorithm which usually takes a few minutes to finish. During this time I'd like to show the user a progress bar which indicates how much of the algorithm is done as precisely as possible.

The algorithm is divided into several steps, each with its own typical timing. For instance-

  • initialization (500 milli-sec)
  • reading inputs (5 sec)
  • step 1 (30 sec)
  • step 2 (3 minutes)
  • writing outputs (7 sec)
  • shutting down (10 milli-sec)

Each step can report its progress quite easily by setting the range its working on, say [0 to 150] and then reporting the value it completed in its main loop.

What I currently have set up is a scheme of nested progress monitors which form a sort of implicit tree of progress reporting.

All progress monitors inherit from an interface IProgressMonitor:

class IProgressMonitor
{
public:
  void setRange(int from, int to) = 0;
  void setValue(int v) = 0;
};

The root of the tree is the ProgressMonitor which is connected to the actual GUI interface:

class GUIBarProgressMonitor : public IProgressMonitor
{
   GUIBarProgressMonitor(ProgressBarWidget *);
};

Any other node in the tree are monitors which take control of a piece of the parent progress:

class SubProgressMonitor : public IProgressMonitor
{
  SubProgressMonitor(IProgressMonitor *parent, int parentFrom, int parentLength)
  ...
};

A SubProgressMonitor takes control of the range [parentFrom, parentFrom+parentLength] of its parent.

With this scheme I am able to statically divide the top level progress according to the expected relative portion of each step in the global timing. Each step can then be further subdivided into pieces etc'

The main disadvantage of this is that the division is static and it gets painful to make changes according to variables which are discovered at run time.

So the question: are there any known design patterns for progress monitoring which solve this issue?

+1  A: 

This is a tough problem, we have struggled with it too in a previous project.

The best I could come up with is to collect statistics of how long each phase actually takes in real life, and adjust the relative interval lengths accordingly.

We haven't implemented it in that project though (at least as long as I was there), so this is just a theoretical idea :-)

Péter Török
+3  A: 

Péter's was the approach I took on a large project; during our pilot and initial rollout, each of our thousands of mobile devices were sending back timing and usage data, and we used the average, median, and standard-deviations of the time taken to fine-tune the configuration of our tasks (when the task was allowed to run, how long it was allowed to run, what values were used in the progress-bar display, etc). Since our solution was built a bit like yours but driven by values supplied in an XML config file, we thought about building this as an automated system (e.g., the server would check these values on some interval, notice certain tasks were taking longer in recent days than they used to and update the config file to reschedule or lengthen them), but figured it wasn't worth the hassle just to prevent a quick human review every few weeks.

Since I don't know of a technical solution to your problem, I think what you show to the user (and how much time you spend developing a solution) should be based on functional concerns: who is using this? how accurate does the info need to be? Is this an interactive process during which they can do no other work, or can they let it run in the background and come back to it? Is the work process during which your long-running function occurs a time-sensitive or mission-critical one?

I'm sorry that I can't actually give you the answer you're looking for, but perhaps thinking about what you're trying to achieve in broad strokes knocks loose a good idea. = )

RJCantrell
+2  A: 

A very interesting approach is the user perception.

Chris Harrison published a paper on how user perceives the time passing depending on the progress reported by the progress bar (although the actual duration was obviously identical in all experiments)

Note that the preferred display formula is (x + (1-x) / 2 )8 where x is the actual progress on a 0 to 1 scale :)

Therefore, I would suggest:

  • gather some statistics on the percentage of time a given task take
  • measure the initialization and use it to scale your progress on the progress bar, being pessimistic (prepare a buffer of 10-15% for example)
  • just before the last task (or few last tasks, as long as they have deterministic duration), go all out in order to complete the progress bar in time (with progressive speedup)

I know, that's not accurate, but if users think it's faster I'll settle for it!

Matthieu M.
A: 

You might consider replacing the progress bar, with a progress circle. If the task has N steps, then make N wedges in the pie, and fill each wedge like a progress bar, as that step runs.

As an additional step, perhaps have some text displayed for each step, so they have something to read while the step progresses.

EvilTeach
+1  A: 

Build an AggregateProgressMonitor that automatically calculates the child progress divisions based on information reported by the child progress monitors. The child progress monitor should at least inform the parent of "expected" running time. The child monitor estimated running times can then be updated by their respective operations based on runtime parameters and the overall progress reporting will be adjusted accordingly and automatically.

Something like this...

class IAggregateProgressMonitor : public IProgressMonitor
{
   void setChildValue(IProgressMonitor *, int v);
   void setChildEstimatedTime(IProgressMonitor *, int v);
}

class AggregateProgressMonitor : public IAggregateProgressMonitor 
{
   void setChildValue(IProgressMonitor * child, int v)
   {
      int aggregateValue = mapChildValueToAggregateValue(child, v);
      setValue(aggregateValue);
   }

   void setChildEstimatedTime(IProgressMonitor * child, ulong ms)
   {
      children[child]->estimatedTime = ms;
      updateChildProgressRatios();
   }
}

class SubProgressMonitor : public IProgressMonitor
{
  SubProgressMonitor(IAggregateProgressMonitor *parent, int parentFrom, 
                     int parentLength) ... ;
  void setValue(int v)
  {
     parent->setChildValue(this, v);
  }

  void setEstimatedRunningTime(ulong ms)
  {
    parent->setChildEstimatedTime(this, ms);
  } 
};

You can even use the observed time of the first step to remap the subsequent progress reporters to be more accurate.

You'll need to keep an ordered map of some kind in the AggregateProgressMonitor to be able to track and calculate all the info from the children.

Once complete, you can extend AggregateProgressMonitor (overriding IProgressMonitor methods) to display the progress to the user.

seren23