13

I'm trying to repeat a block of code, without using a condition, yet still only repeating it a specific number of times.

Basically, something like this:

repeat(50)
{
    //Do stuff here.
}

Is there a way to do this? Other than copying and pasting 50 times?

I'm doing this because I figured if I know how many times I want to repeat something, it'd be quicker than checking a condition every time. Is that accurate? Or would I still be checking how many times it's been repeated?

Basically, is it any faster at all?

Collin
  • 1,777
  • 4
  • 26
  • 42
Serdnad
  • 602
  • 2
  • 9
  • 17
  • Are you looking for just a hard-repeat? as in 50 executions of the code sans-*any* conditionals? – WhozCraig Apr 28 '13 at 22:52
  • 1
    As far as I know, compiler will often optimise a for loop using a literal size (not variable based) into the same resultant code as having written that code x times. So `repeat(50)` would be the same as `for(char i=0; i<50; i++)` (I used char cause just in case its not optimised out it wont allocate 4 bytes for the `int` but only 1 for the `char`). – Sellorio Apr 28 '13 at 22:55
  • 1
    @MrUniverse - Loop unrolling is a delicate issue, "optimizing" is not simply a matter of eliminating loop overhead instructions. Loop unrolling creates extra instructions (copies of the loop's body), which means the code for the unrolled loop now eats up more of the cache, which means other code that you might like to stay in the cache has to be evicted to hold the unrolled loop code which may only execute once (i.e. won't be needed again). If you leave the loop logic in place, suddenly the cache becomes very meaningful, and the loop overhead becomes a blessing rather than a curse. – phonetagger Apr 29 '13 at 15:54
  • @phonetagger Good comment, all true :) Thank god I'm not a compiler, life would be awfully complicated. – Sellorio Apr 29 '13 at 23:06
  • In nearly all cases, if you're thinking about the performance impact of a loop's condition, instead of the actual code in the body, your priorities are wrong. I'd say this is where a tiny bit of knowledge of typical hardware comes in handy, but isn't it just basic intuition? Comparing 2 numbers is surely obviously far cheaper than doing actual work. Any real loop doing vaguely useful work will have a body whose cost completely dwarfs that of a simple condition check. It's then foolish to unroll for large numbers of repeats in such cases, trading bloat in size for a nanoscopic increase in speed – underscore_d Nov 19 '17 at 10:39

5 Answers5

21

Your attempts to optimize the loop by using some construct (incl. manually cutting & pasting the code) to optimize the loop's execution speed are ill-advised. Don't do it; it would probably "un-optimize" the execution speed instead.

In any C++ implementation I've ever encountered (MSVC 6.0, 2003, 2005, 2010, GCC various versions, Diab various versions), there is absolutely zero, sorry I didn't stress that enough, ZERO, time involved with allocating a loop counting variable, assuming any other variables were allocated for the function in which the loop counting variable is allocated. For a simple loop that makes no function calls, the loop counting variable may never even make it out to memory; it may be held entirely in a single CPU register for its entire lifetime. Even if it is stored in memory, it would be on the runtime stack, and space for it (and any other local variables) would be claimed all at once in a single operation, which takes no more or less time depending on the number of variables allocated on the stack. Local variables like your loop counter variable are allocated on the stack, and stack allocations are CHEAP CHEAP CHEAP, as opposed to heap allocations.

Example loop counter variable allocation on the stack:

for (int i=0; i<50; ++i) {
    ....
}

Another example loop counter variable allocation on the stack:

int i = 0;
for (; i<50; ++i) {
    ....
}

Example loop counter variable allocated on the heap (don't do this; it's stupid):

int* ip = new int;
for (*ip=0; *ip<50; ++(*ip)) {
    ....
}
delete ip;

Now to address the issue of attempting to optimize your loop by manually copying & pasting instead of using a loop & counter:

What you're considering doing is a manual form of loop unrolling. Loop unrolling is an optimization that compilers sometimes use for reducing the overhead involved in a loop. Compilers can do it only if the number of iterations of the loop can be known at compile time (i.e. the number of iterations is a constant, even if the constant involves computation based on other constants). In some cases, the compiler may determine that it is worthwhile to unroll the loop, but often it won't unroll it completely. For instance, in your example, the compiler may determine that it would be a speed advantage to unroll the loop from 50 iterations out to only 10 iterations with 5 copies of the loop body. The loop variable would still be there, but instead of doing 50 comparisons of the loop counter, now the code only has to do the comparison 10 times. It's a tradeoff, because the 5 copies of the loop body eat up 5 times as much space in the cache, which means that loading those extra copies of the same instructions forces the cache to evict (throw out) that many instructions that are already in the cache and which you might have wanted to stay in the cache. Also, loading those 4 extra copies of the loop body instructions from main memory takes much, much longer than simply grabbing the already-loaded instructions from the cache in the case where the loop isn't unrolled at all.

So all in all, it's often more advantageous to just use only one copy of the loop body and go ahead and leave the loop logic in place. (I.e. don't do any loop unrolling at all.)

phonetagger
  • 7,701
  • 3
  • 31
  • 55
  • The second loop will never be executed. I guess you meant `int i = 0;`? – Aleph Apr 30 '13 at 15:12
  • @AnotherTest - You make the case for code peer reviews! Thanks. – phonetagger Apr 30 '13 at 15:53
  • Nice, well thought out answer. It might be that the OP was just looking for a more elegant way to do it in C++. Like in Perl, for example, you could just say "for (1..50) {...}" or even "print 'hello' for (1..50)" – MarkR May 26 '22 at 00:45
  • @MarkR - Thank you for the compliment. Based on the OP's last line, "Basically, is it any faster at all?", I think they were interested in performance, not elegance. – phonetagger May 27 '22 at 12:51
4

If you want the syntactic nicety of being able to write repeat(x) {} then you could use a macro.

Something like:

#include <iostream>

#define repeat(x) for(int i = x; i--;)

int main()
{
    repeat(10) 
    {
        std::cout << i << std::endl;
    }

    return 0;
}

The implementation here also uses a comparison to zero in the for loop rather than the less than operator, which may be slightly faster.

j b
  • 5,147
  • 5
  • 41
  • 60
  • Any decent compiler will recognize a standard for loop that loops a constant number of times so comparing against zero does not make things faster. Here's an example: https://godbolt.org/z/rYWsGdWf6. The compiler unrolled the first loop and changed the second loop to count down instead of up. Using macros in these cases just make code harder to read for other people who aren't familiar with your macros and causes weird errors. If you have a variable named `i` defined and you use your macro, `i` in the loop will unexpectedly refer to the hidden loop variable instead of the existing `i`. – eesiraed Aug 24 '22 at 23:30
3

It would be entirely possible to have a repeat(x) as part of the language, but there isn't such a thing for some reason - the design of C and C++ does somewhat follow what the processors can do, and I'm not familiar with a single processor (I've worked with about 10 different processor architectures) that can do a "loop this many times" without some sort of "check if we reached the number".

So, you will have to write some code that checks how many times you've repeated something (or, how many times there is left to do - there is an x86 instruction called "loop" that does just that - counts down, and if the counter is not zero, jump to beginning of the loop).

If the compiler wishes to then "unroll" a loop because it has a constant number of iterations, and it decides "unrolling this is faster" [compilers decide these sort of things all the time, and often get it right], then the compiler may well do so. But you still have to write code that "checks".

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
  • Ok, thanks for the processor things, that was interesting. Completely forgot about the compiler optimizing. – Serdnad Apr 28 '13 at 23:04
-4

I would have to say that the biggest increase in speed would be the fact that you are not allocating an iteration variable, but what you asked for would inevitably need to check a condition. in essence what you have there is as follows.

int i = 0;
for(; i< 50; i++)
{
    Do Something.
}

The reason I have moved I to outside the for loop is to state it can be any variable initialized before the loop. It is percisely the same thing as,

for(int i =0; i< 50; i++)
{
}
Nomad101
  • 1,688
  • 11
  • 16
  • Why not `for(int i = 0; i < ...)`? – Mats Petersson Apr 28 '13 at 22:52
  • its exactly the same as above? just moved the initialization out of the for loop. Just stating it could be a variable created at any point before the loop. – Nomad101 Apr 28 '13 at 22:53
  • 1
    The variable doesn't get allocated every time anyway, this code is equivalent to declaring `i` inside the loop. – Hunter McMillen Apr 28 '13 at 22:53
  • I am aware however if he wanted to save an allocation he could use a different variable and not necesarily use a new one. that is all I meant. – Nomad101 Apr 28 '13 at 22:54
  • 1
    It is better for the compiler to use a "fresh" variable each time you do a loop, as the compiler doesn't have to figure out if you are needing the variable from earlier somewhere later... – Mats Petersson Apr 28 '13 at 22:55
  • I completely agree however I was just stating a simple way to save an allocation, which would make that pretty much as fast as it could possibly be. Also using the original in a function could have i set in the arguments. – Nomad101 Apr 28 '13 at 22:56
  • "percisely the same"? Scope of i is different. – CW Holeman II Jun 07 '19 at 03:02
-4

How about this:

typedef std::vector<int> times;
for (auto count : times(5))
{
    // execute the loop 5 times
}
DonMathi
  • 1
  • 1