No BSD License  

Highlights from
Percent Done

5.0

5.0 | 3 ratings Rate this file 10 Downloads (last 30 days) File Size: 1.25 KB File ID: #24099

Percent Done

by Adam Leadbetter

 

13 May 2009 (Updated 14 May 2009)

Displays the percentage done of a job to the command line interface.

| Watch this File

File Information
Description

Displays the percentage done of a job to the command line interface.

MATLAB release MATLAB 7.2 (R2006a)
Tags for This File  
Everyone's Tags
Tags I've Applied
Add New Tags Please login to tag files.
Comments and Ratings (3)
13 May 2009 John D'Errico

Help is reasonable here. No error checks. (sadly, error checks will just slow this down.) An H1 line.

This works reasonably if you never call it more than 100 times, and if each pass through the loop is lengthy. The fact is, many loop variables will exceed 100. So I tried this:

for i = 1:100000
  perccount(i,100000)
end

What you will see is the numbers flickering, so quickly that you cannot read the percentages. Perhaps a better solution is to use a persistent variable, checking to see if there is a reason to update the screen (i.e., the percentage as reported would be different.) Do something like round(100*jj/Imax). If this changes, then and only then do an update to the screen.

Don't forget that calling such a function many thousands or even millions of times may add significant additional time to the running of your code. 0.001 seconds for each call adds up when you make that call 1e6 times. Admittedly, this is a problem with waitbar too. In general, it is probably better to restrict calls to any style of progress report to only a few hundred at most. So a test outside of this code may make some sense. Whenever I put in a waitbar, I generally do exactly that.

Next, I ran a timing test:

tic,for i = 1:10000
perccount(i,10000)
end
toc

Percentage complete: 99%Elapsed time is 9.704124 seconds.

First, see that toc has reported its result on the same line as the progress report. This would be irritating, at least to me. Clean up after yourself!

Next, it too almost 10 seconds (on my admittedly slow CPU) to just call this function 10000 times. So big loops will indeed add a significant amount of time to your code.

Finally, this code takes its second variable with the name "max"! (Even thought the help calls it Imax, the code itself calls the variable max.) While this is in itself not a bug, it is a poor choice. You have superseded the max function. While this code does not try to call max, it is just bad form. Use names for variables that are NOT the names of existing, frequently used, built-in functions. Someone who is reading your code in the future may easily be confused. Worse, suppose you decided to change this code in a way that required you to use the max function itself? A bug waiting to happen.

I do appreciate that all waitbars are time hogs, even so, this seems to be worth about a 3 rating. As always, improvements are encouraged.

14 May 2009 John D'Errico

Fixed the problems I saw. It does what it claims to do. It is faster now too, taking a much smaller chunk of time away from your own computations. Whereas the last time I did this test, it too almost 10 seconds just to do the progress report, this test is down to only about a second.

tic,for i = 1:10000,perccount(i,10000),end,toc
Elapsed time is 1.243821 seconds.

Perccount is not infinitely fast, but waitbar will be just as slow, if not worse. This begs the question of just how slow?

h = waitbar(0,'I''m busy!');
tic
n = 10000;
for i = 1:n
 waitbar(i/n)
end
delete(h)
toc
Elapsed time is 124.846332 seconds.

Yes, just 10000 calls to waitbar, with nothing else in the loop, took over 2 minutes!

So, for the person who will have wanted to ask, "Why not just use waitbar?" this is a good reason. All of those calls to update a slider bar take considerable time. Yes, I'll admit that more careful coding, calling waitbar only infrequently in that loop would have been faster. So,

h = waitbar(0,'I''m busy!');
tic
n = 10000;
for i = 1:n
  if (rem(i,100) == 0)
    waitbar(i/n)
  end
end
delete(h)
toc

Elapsed time is 1.280422 seconds.

So for the person who does not wish to write the extra code to make waitbar function passably, or for the person who would actually prefer to have progress reported in the command line, perccount is a very viable option.

My thanks for fixing this. Well done.

19 Aug 2011 MechtEngineer

Fantastic program. It does exactly what it says it does. Extremely easy to insert into a for loop and get an update on the progress through the for loop in real-time.

I just made a few changes:
1. The first time the program is run, the persistent variable 'lastCall' is empty, and so the "Percentage Complete" line displayed inside previous text I had printed to the workspace. So I just added a catch, which sets lastCall to -1 on the very first run, and that means the first time the function 'perccount' is run, the "Percentage Complete" line is displayed correctly.

2. I modified the last check (which checks whether the progress is complete) from
fprintf(1,'\n\n');
to
fprintf(1,'\b\b\b100%%\n\n');
This means the displayed line ends up showing "Percentage Complete: 100%" rather than "Percentage Complete: 99%", which is untrue - everything has completed properly!

3. A program without good commenting makes it very difficult to use and understand for other programmers. A description of the required variables used by the function at the start of the code helps a huge amount, and Adam, you did this very nicely! But if I want to understand how the program works, then it really helps to have some commenting through the code.

The code I am now running is:

function perccount(jj,maxjj)

%Reports the percentage of the job done to the screen.
%
% PERCOUNT(I,Imax)
% I is the current iteration between 1 and Imax
% Imax is the maximum number of iterations
%
% Do not print anything to the screen between calls of this function!
%

% title - s cmspike/perccount vr - 1.2
% author - bodc/alead date - 2006may16

% Updated 2009May14
% At suggestion of John D'Errico renamed internal variable "max" to
% "maxjj"
% Also following D'Errico's suggestions the following functionality has
% been added:
% 1. An invocation check - checks that two input arguments are
% supplied

% lastCall is the last integer percent displayed
persistent lastCall;
% if the number of arguments is 2
if(nargin == 2)
    % if this is the first run, make lastCall a value that will get the
    % following if loops working from the start.
    if isempty(lastCall)
        lastCall = -1;
    end
    % if the percentage has increased to the next integer, then the
    % displayed percentage will need to be updated
    if(lastCall ~= floor(((jj-1)/maxjj) * 100))
        % if the progress so far (jj out of maxjj) is not the first
        % representation, then backspace the percentage area to prepare for
        % the next percentage update.
        if(jj ~= 1)
            fprintf(1,'\b\b\b');
        % if the progress so far IS the first time it is displayed, then
        % display the "context line" of the percentage.
        else
            fprintf(1,'\n\tPercentage complete: ');
        end
        % Calculate the percentage done
        pc_done = num2str(floor(((jj-1)/maxjj) * 100));
        % if the percentage done so far is in the single digit range
        % (between 0 and 10), then add a zero before it so the percentage
        % displays neatly
        if(length(pc_done) == 1)
            pc_done(2) = pc_done(1);
            pc_done(1) = '0';
        end
        fprintf(1,'%s%%',pc_done);
    end
    
    lastCall = floor(((jj-1)/maxjj) * 100); % the last int percent displayed
    % if this is the last part of the total, then display 100% complete and
    % add a few lines of whitespace for neatness
    if(jj == maxjj)
        fprintf(1,'\b\b\b100%%\n\n');
    end
else
    error('Error: PERCCOUNT needs two input arguments...');
end

One final suggestion: Perhaps change the function to percentComplete.m, or percentDone.m, as this makes more sense than perccount.m, but that's entirely up to you!

Please login to add a comment or rating.
Updates
14 May 2009

Renamed internal variable "max" to "maxjj" at John D'Errico's suggestion

14 May 2009

An invocation check now ensures two input variables are used.

The function now has a persistent variable so only updates the screen when necessary, not on every call.

The screen output has been tidied.

Tag Activity for this File
Tag Applied By Date/Time
programming Adam Leadbetter 13 May 2009 09:23:37
interface Adam Leadbetter 13 May 2009 09:23:37
waitbar Adam Leadbetter 14 May 2009 05:37:29
percent done Adam Leadbetter 14 May 2009 05:37:30

Contact us at files@mathworks.com