24 days of Perl code from RJBS! Feed

83% of Advent Remaining

Acme::ProgressBar - 2010-12-04

Sufficiently Advanced (ProgressBar) Technology

At YAPC::NA 2004 in Buffalo, Damian Conway gave a talk on "Sufficiently Advanced Technologies," in which he showed off a number of very simple interfaces for fairly complex operations. Among the libraries he talked about was Smart::Comments, a source filter that turns comments into code. It had a bunch of functions, but one of them let you do this:


1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 

 

use Smart::Comments:

my @input = acquire_input();
my @output;

for my $datum (@input) { ### Analyzing [===| ] % done
  push @output, expensive_analysis($datum);
}

 

...and your program would, on one line, cycle through stuff like this:

  Analyzing [|                ]   0% done
  Analyzing [===|             ]  25% done
  Analyzing [========|        ]  50% done
  Analyzing [============|    ]  75% done
  Analyzing [=================] 100% done

Progress bars are hard to get right! Just look at any implementation you've seen in otherwise great, well-designed software, like Microsoft Windows. They often provide progress bars that jump between "almost done" and "-919 days remain."

Smart::Comments can have this problem, too, as the workload changes out from under it -- some items in @input, for example, might be costly to analyze, for example. There might be redo or next statements, or lasts, all of which can interfere with proper job speed detection.

Acme::ProgressBar is always accurate. No matter what the characteristics of the input are, no matter how the loop flow is altered, no matter what, Acme::ProgressBar is accurate. Only extreme system load or fatal signals can interfere with its accuracy.

It's very easy to use, too. It isn't a source filter, and uses no complex features. It should never introduce bugs to your code and can be added or removed to your code easily. Written with Acme::ProgressBar instead, the above code would look like this:


1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
10: 

 

use Acme::ProgressBar;

my @input = acquire_input();
my @output;

progress {
  for my $datum (@input) {
    push @output, expensive_analysis($datum);
  }
};

 

...and would produce output like this (but all on one line, and I've omitted some lines for brevity's sake):

  Progress: [          ] (calculating time remaining)
  Progress: [==        ] 8s remaining                         
  Progress: [====      ] 6s remaining                         
  Progress: [======    ] 4s remaining                         
  Progress: [========  ] 2s remaining                         
  Progress: [==========] 0s remaining                         

There can be a performance cost for using Acme::ProgressBar, but the impact varies from program to program. You should test it in your code before judging the performance costs.

The source code for Acme::ProgressBar may also be instructive.

See Also