jramm
jramm

Reputation: 6655

C++ print progress with minimal impact on runtime

I have a C++ program with a long running loop. It has about 500000 iterations. I'd like to print the progress every 5%.

What I have so far is below. Problem with this is that it will keep writing the same line (0%, %5 etc) over and over. This is because after truncating the percent to an integer there are 1000's of iterations that will hit upon a multiple of 5. This makes the program noticeably slower.

Conversely, if I don't truncate to an integer, then the result of percent % 5.0 is unlikely to be precisely 0.

How can I print a progress indicator with the minimum possible impact on runtime?

// Counters for progress indicator
int i = 0;
float totalWindows = (float)(win.nX * win.nY);
int percent;

while (win.next() == 0)
{
    // Read the data

    // Nicely formatted progress indicator
    i++;
    percent = (i / totalWindows) * 100;
    if (percent % 5 == 0)
    {
        std::cout << "\r" << std::string(percent/5, '|') << percent << "%";
        std::cout.flush();
    }



}

EDIT: Thanks for the answers. I have gone with christophes' which just came to the least number of instructions. It shaved 25% of runtime, so pretty significant!

Upvotes: 5

Views: 2301

Answers (3)

Christophe
Christophe

Reputation: 73376

Considering that totalWindows seems to remain unchanged, and integer increments/decrements will probably be faster than a lot of double operations with conversion to int, I propose:

// Counters for progress indicator
int i = 0;
float totalWindows = (float)(win.nX * win.nY);
int increment5 = 0.05 * totalWindows; // how many iterations does 5% represent ? 
int countdown = increment5;   // decrement countdown instead of modulo
int percent5 = 0;  // number of elements in the progress bar (1 means 5%)

while (win.next() == 0)
{
    // Read the data

    // Nicely formatted progress indicator
    i++;
    if (--countdown == 0)
    {
            percent5++;
            std::cout << "\r" << std::string(percent5, '|') << percent5*5 << "%";
            countdown = increment5;  
            std::cout.flush();
    }

}

If you fear that the cumulative roundings would not be acceptable for displaying the progress, you could always opt to calculate the exact value in the if-block: the calculation would be performedonly once every 5% instead of at each iteration.

Upvotes: 2

Austin Maliszewski
Austin Maliszewski

Reputation: 1

Instead of calculating the percentage each time, you can calculate what 5% of the total is and use this directly.

Consider this simplified example:

int main(int argc, char * argv) {
  int total = 50000;
  int total_percent_stop = total * 5 / 100; //Set the percentage here.

  for (int i = 0; i < total; i++) {
    if (i % total_percent_stop == 0) {
      printf("%d percent done\n", (i / total_percent_stop) * 5);
    }
  }
}

If the performance of this particular code is very important to you, you can avoid the relatively expensive division operations by doing something like this (at the expense of some readability).

int main(int argc, char * argv) {
  int total = 50000;
  int total_percent_stop = total * 5 / 100; //Set the percentage here

  for (int i = 0, percent_counter = 0, n_percent = 0;
       i < total;
       i++, percent_counter++) {
    if (percent_counter == total_percent_stop) {
      percent_counter = 0;
      n_percent++;
      printf("%d percent done\n", n_percent * 5);
    }
  }
}

On my machine, for sufficiently large values of total the second is significantly faster. When I changed everything to unsigned long longs and set total to 5 billion, the second took 9.336 seconds and the first took 40.159 seconds

Upvotes: 0

4386427
4386427

Reputation: 44274

How about:

int step = 5;
int nextPrint = step;

while (win.next() == 0)
{
    // Read the data

    // Nicely formatted progress indicator
    i++;
    percent = (100 * i) / totalWindows;
    if (percent >= nextPrint)
    {
        std::cout << "\r" << std::string(percent/5, '|') << percent << "%";
        std::cout.flush();
        nextPrint += step;
    }
}

BTW: Why do you have totalWindows as a float? That also hurts performance. If the number of iterations are 500000 an 32 bit int should be sufficient.

Another and better approach because percent isn't calculated in every loop:

const int percentPrint = 5;
int step = totalWindows / (100/percentPrint);
int nextPrint = step;

while (win.next() == 0)
{
    // Read the data

    // Nicely formatted progress indicator
    i++;
    if (i >= nextPrint)
    {
        percent = (100 * i) / totalWindows;
        std::cout << "\r" << std::string(percent/percentPrint , '|') << percent << "%";
        std::cout.flush();
        nextPrint += step;
    }
}
std::cout << "\r" << std::string(100/percentPrint , '|') << percent << "%";
std::cout.flush();

Upvotes: 2

Related Questions