Reputation: 6655
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
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
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
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