4

I am writing a program in which a certain for-loop gets iterated over many many times.

One single iteration doesn't take to long but since the program iterates the loop so often it takes quite some time to compute.

In an effort to get more information on the progress of the program without slowing it down to much I would like to print the progress every xth step.

Is there a different way to do this, than a conditional with a modulo like so:

for(int i = 0; i < some_large_number; i++){
    if(i % x == 0)
        printf("%f%%\r", percent);
    //some other code
    .
    .
    .
}

?

Thanks is advance

  • 3
    Is it a performance problem as it is? – klutt Apr 29 '20 at 12:57
  • If the modulo is a performance problem, use a counter instead. – Ian Abbott Apr 29 '20 at 13:05
  • 2
    A nested loop is faster. – Peter - Reinstate Monica Apr 29 '20 at 13:05
  • 2
    Of course the I/O will dominate the performance; the modulo computation will be fast in comparison. If you choose powers of 2 you can also compare a mask over the last bits against 0 which is faster than a division (your compiler may even do that for you when optimizing). – Peter - Reinstate Monica Apr 29 '20 at 13:07
  • @Peter-ReinstateMonica: Whether printing takes more time than the modulo computation is partially a function of `x`, as there are `x` modulo computations for each `printf`. – Eric Postpischil Apr 29 '20 at 13:45
  • 1
    @Yunnosch: “Do it differently, then measure” is a hammer. Skilled practitioners study processor specifications, learn how systems behave, and apply reasoning to craft potential solutions, putting many pieces together to do so in complicated code. One cannot expect to stumble on the best array geometry to optimize cache use by trial and error any more than one can build a fine mechanical watch by smashing pieces together. Recommending measurement as the primary tool for optimization is bad advice for students. The best tool is knowledge. – Eric Postpischil Apr 29 '20 at 13:52
  • @EricPostpischil You are right, I do not and did not contradict you. Coming up with ways to do it differently is practically doomed when it is based on random guessing. That is where the science comes in. However, you do need to know everything in your environment to perfect detail in order to come up with an optimisation without measuring it. Most optimisation questions and answers here are based on the idea that you can seriously optimise by environment-independent code. I would like to clarify that I did not mean "change randomly" and I hope that we can then agree. – Yunnosch Apr 29 '20 at 14:28
  • @EricPostpischil Also thanks for the vote reminder. I am happy with the edit, good work lookatdatcake. – Yunnosch Apr 29 '20 at 14:28
  • If you're worried about efficiency and performance you wouldn't be calling `printf()` in the loop. Don't complicate your code and compromise its readability by trying to optimize something that simply doesn't matter. You'll only make it more bug-prone. Imagine having to go back after some time and change the more complex loops in some of the answers already posted... – Andrew Henle Apr 29 '20 at 15:22

4 Answers4

2

I would do it like this:

int j = x;
for (int i = 0; i < some_large_number; i++){
    if(--j == 0) {
        printf("%f%%\r", percent);
        j = x;
    }
    //some other code
    .
    .
    .
}
Pierre François
  • 5,850
  • 1
  • 17
  • 38
  • from the solutions I've tried up to now this seems to be fastest, when instead of subtracting and comparing to 0, I add up to x and then set the variable back to 0 – lookatdatcake Apr 29 '20 at 13:41
  • This adds another counter. As has been show, this is not necessary, as using a nested loop results in most iterations having no more cost than the original code in that there is no extra variable to increment and test. The inner loop simply runs at “full speed,” and, only when it terminates, is there a small bit of additional work to do for the printing. – Eric Postpischil Apr 29 '20 at 13:44
2

The fastest approach regarding your performance concern would be to use a nested loop:

unsigned int x = 6;
unsigned int segments = some_large_number / x; 
unsigned int y;

for ( unsigned int i = 0; i < segments; i++ ) {

    printf("%f%%\r", percent); 

    for ( unsigned int j = 0; j < x; j++ ) {

       /* some code here */

    }
}


// If some_large_number can´t be divided evenly through `x`:

if (( y = (some_large_number % x)) != 0 )
{
    for ( unsigned int i = 0; i < y; i++ ) {

       /* same code as inside of the former inner loop. */

    }
}

Another example would be to use a different counting variable for the check to execute the print process by comparing that to x - 1 and reset the variable to -1 if it matches:

unsigned int x = 6;
unsigned int some_large_number = 100000000;

for ( unsigned int i = 0, int j = 0; i < some_large_number; i++, j++ ) {

    if(j == (x - 1))
    {
        printf("%f%%\r", percent);
        j = -1;
    }
    /* some code here */
}
2

Divide the some_large_number by x. Now loop for x times and nest it with the new integer and then print the percent. I meant this:

int temp = some_large_number/x;
for (int i = 0; i < x; i++){
   for (int j = 0; j < temp; j++){
        //some code
   }
   printf("%f%%\r", percent);
}
Rithik Banerjee
  • 447
  • 4
  • 16
  • 2
    This is a correct approach, but the details are wrong. The inner loop should run for `x` iterations, not `temp`, as it is desired to print once every `x` iterations. And so the outer loop should run for `temp` iterations. And some code is needed to handle the fragment left ovr when `x` does not evenly divide `some_large_number`. Also, the original value of `i` might be used in the body of the original loop, in which case some provision must be made for making that available. – Eric Postpischil Apr 29 '20 at 13:38
  • 2
    E.g., one might do `int NumberOfWholeBlocks = some_large_number/x; for (int block = 0; block < NumberOfWholeBlocks; ++block)` for the outer loop and `for (int i = block*x; i < (block+1)*x; ++i)` for the inner loop. Then, in the inner loop, `i` always has the same value as in the corresponding iteration of the original code. – Eric Postpischil Apr 29 '20 at 13:40
  • the key concept is x* temp=some_large_number – Rithik Banerjee Apr 29 '20 at 14:40
  • you can do x* temp or temp* x its our choice – Rithik Banerjee Apr 29 '20 at 14:41
2

This code:

for(int i = 0; i < some_large_number; i++){
    if(i % x == 0)
        printf("%f%%\r", percent);
    //some other code
    .
    .
    .
}

can be restructured as:

/*  Partition the execution into blocks of x iterations, possibly including a
    final fragmentary block.  The expression (some_large_number+(x-1))/x
    calculates some_large_number/x with any fraction rounded up.
*/
for (int block = 0, i = 0; block < (some_large_number+(x-1))/x; ++block)
{
    printf("%f%%\r", percent);

    //  Set limit to the lesser of the end of the current block or some_large_number.
    int limit = (block+1) * x;
    if (some_large_number < limit) limit = some_large_number;

    //  Iterate the original code.
    for (; i < limit; ++i)
    {
        //some other code
    }
}

With the following caveats and properties:

  • The inner loop has no more work than the original loop (it has no extra variable to count or test) and has the i % x == 0 test completely removed. This is optimal for the inner loop in the sense it reduces the nominal amount of work as much as possible, although real-world hardware sometimes has finicky behaviors that can result in more compute time for less actual work.
  • New identifiers block and limit are introduced but can be changed to avoid any conflicts with uses in the original code.
  • Other than the above, the inner loop operates identically to the original code: It sees the same values of i in the same order as the original code, so no changes are needed in that code.
  • some_large_number+(x-1) could overflow int.
Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312