169

I am just wondering if there would be any loss of speed or efficiency if you did something like this:

int i = 0;
while(i < 100)
{
    int var = 4;
    i++;
}

which declares int var one hundred times. It seems to me like there would be, but I'm not sure. would it be more practical/faster to do this instead:

int i = 0;
int var;
while(i < 100)
{
    var = 4;
    i++;
}

or are they the same, speedwise and efficiency-wise?

  • 7
    To be clear, the above code does not "declare" var one hundred times. – jason Jun 11 '09 at 19:22
  • 1
    @Rabarberski: The referenced question is not an exact duplicate as it does not specify a language. **This question is specific to C++**. But according to the answers posted to your referenced question, the answer depends on the language and possibly the compiler. – DavidRR Jul 16 '13 at 18:28
  • 2
    @jason If the first snippet of code does not declare the variable 'var' one hundred times, can you explain what's happening? Does it just declare the variable once and initialize it 100 times? I would've thought that the code declares and initializes the variable 100 times, since everything in the loop is executed 100 times. Thanks. – randomUser47534 May 24 '15 at 04:15
  • 1
    Does this answer your question? [Declaring variables inside loops, good practice or bad practice?](https://stackoverflow.com/questions/7959573/declaring-variables-inside-loops-good-practice-or-bad-practice) – user202729 Oct 17 '22 at 14:04

13 Answers13

202

Stack space for local variables is usually allocated in function scope. So no stack pointer adjustment happens inside the loop, just assigning 4 to var. Therefore these two snippets have the same overhead.

laalto
  • 150,114
  • 66
  • 286
  • 303
  • 54
    I wish those guys who teach at out college at least knew this basic thing. Once he laughed at me declaring a variable inside a loop and I was wondering what's wrong until he cited performance as the reason not to do so and I was like "WTF!?". – Mehrdad Afshari Jun 11 '09 at 19:35
  • 21
    Are you sure you should be talking about stack space right away. A variable like this could also be in a register. – toto Jun 25 '09 at 11:46
  • 5
    @toto A variable like this could also be _nowhere_ – the `var` variable is initialized but never used, so a reasonable optimiser can just remove it completely (except the second snippet **if** the variable was used somewhere after the loop). – CiaPan Dec 04 '16 at 20:27
  • 1
    @Mehrdad Afshari a variable in a loop gets its constructor called once per iteration. EDIT - I see you mentioned this below, but I think it deserves mention in the accepted answer as well. – hoodaticus Jul 26 '17 at 20:07
115

For primitive types and POD types, it makes no difference. The compiler will allocate the stack space for the variable at the beginning of the function and deallocate it when the function returns in both cases.

For non-POD class types that have non-trivial constructors, it WILL make a difference -- in that case, putting the variable outside the loop will only call the constructor and destructor once and the assignment operator each iteration, whereas putting it inside the loop will call the constructor and destructor for every iteration of the loop. Depending on what the class' constructor, destructor, and assignment operator do, this may or may not be desirable.

Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
  • This doesn't seem true. If you use a nonPOD type and assign it in the loop over and over again as in his example, that assignment to a new variable is calling the constructor over and over again, too. – Brian Jun 11 '09 at 19:16
  • 43
    Correct idea wrong reason. Variable outside the loop. Constructed once, destroyed once but asignment operator applied every iteration. Variable inside the loop. Constructe/Desatructor aplied every iteration but zero assignment operations. – Martin York Jun 11 '09 at 19:34
  • 9
    This is the best answer but these comments are confusing. There's a big difference between calling a constructor and an assignment operator. – Andrew Grant Jun 11 '09 at 19:50
  • 1
    It *is* true if the loop body does the assignment anyway, not just for initialization. And if there's just a body-independent/constant initialization, the optimizer can hoist it. – peterchen Jun 11 '09 at 19:59
  • 7
    @Andrew Grant: Why. Assignment operator is usually defined as copy construct into tmp followed by swap (to be exception safe) followed by destroy tmp. Thus assignment operator is not that different to construction/destroy cycle above. See http://stackoverflow.com/questions/255612/c-dynamically-allocating-an-array-of-objects/255744#255744 for example of typical assignment operator. – Martin York Jun 12 '09 at 08:32
  • 1
    If construct/destruct are expensive, their total cost is a reasonable upper limit on the cost of the operator=. But the assignment could indeed be cheaper. Also, as we expand this discussion from ints to C++ types, one could generalize 'var=4' as some other operation than 'assign variable from a value of the same type'. – greggo Mar 05 '14 at 20:15
70

They are both the same, and here's how you can find out, by looking at what the compiler does (even without optimisation set to high):

Look at what the compiler (gcc 4.0) does to your simple examples:

1.c:

main(){ int var; while(int i < 100) { var = 4; } }

gcc -S 1.c

1.s:

_main:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $24, %esp
    movl    $0, -16(%ebp)
    jmp L2
L3:
    movl    $4, -12(%ebp)
L2:
    cmpl    $99, -16(%ebp)
    jle L3
    leave
    ret

2.c

main() { while(int i < 100) { int var = 4; } }

gcc -S 2.c

2.s:

_main:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $24, %esp
        movl    $0, -16(%ebp)
        jmp     L2
L3:
        movl    $4, -12(%ebp)
L2:
        cmpl    $99, -16(%ebp)
        jle     L3
        leave
        ret

From these, you can see two things: firstly, the code is the same in both.

Secondly, the storage for var is allocated outside the loop:

         subl    $24, %esp

And finally the only thing in the loop is the assignment and condition check:

L3:
        movl    $4, -12(%ebp)
L2:
        cmpl    $99, -16(%ebp)
        jle     L3

Which is about as efficient as you can be without removing the loop entirely.

Alex Brown
  • 41,819
  • 10
  • 94
  • 108
  • 2
    "Which is about as efficient as you can be without removing the loop entirely" Not quite. Partially unrolling the loop (doing it say 4 times per pass) would speed it up dramatically. There are probably many other ways to optimize... although most modern compilers would probably realize that there's no point in looping at all. If 'i' was used later, it'd simply set 'i' = 100. – darron Sep 27 '09 at 22:18
  • that's assuming the code changed to incremented 'i' at all... as is it's just a forever loop. – darron Sep 27 '09 at 22:20
  • As was the Original Post! – Alex Brown Jun 21 '14 at 01:09
  • 2
    I like answers that back the theory with proof! Nice to see ASM dump backing up the theory of being equal codes. +1 – Xavi Montero Aug 27 '14 at 23:40
  • Sorry, but how did you ACTUALLY produce your results when the code you have provided above does not have an increment statement to the variable `i`? Are modern compilers that intelligent that they can determine when one wishes to increment forward or backwards and by how much on each iteration? – Nicholas Hamilton Aug 24 '16 at 02:48
  • 1
    I actually produced the results by generating the machine code for each version. No need to run it. – Alex Brown Aug 24 '16 at 02:51
  • @Nicholas Hamilton You are correct in your evaluation that since i doesn't increment, it will infinite loop forever. However, since we are not actually running the code, we don't care if it ever actually finishes. We just print the assembly the compile produces and leave it there. – Thomas Mar 01 '17 at 16:09
  • `while(int i < 100)` doesn't compile even with `-std=gnu89`. https://godbolt.org/z/zcEWr1WPE . But yeah, even in a debug build, whatever you did compile shows GCC doesn't futz with the stack for each loop iteration, as expected. – Peter Cordes Feb 07 '23 at 21:19
14

These days it is better to declare it inside the loop unless it is a constant as the compiler will be able to better optimize the code (reducing variable scope).

EDIT: This answer is mostly obsolete now. With the rise of post-classical compilers, the cases where the compiler can't figure it out are getting rare. I can still construct them but most people would classify the construction as bad code.

Joshua
  • 40,822
  • 8
  • 72
  • 132
  • 4
    I doubt it will affect optimization -- if the compiler performs any sort of data flow analysis, it can figure out that it's not being modified outside the loop, so it should produce the same optimized code in both cases. – Adam Rosenfield Jun 11 '09 at 19:07
  • 3
    It won't figure it out if you have two different loops using the same temp variable name though. – Joshua Jun 11 '09 at 19:26
11

Most modern compilers will optimize this for you. That being said I would use your first example as I find it more readable.

Matt Davis
  • 45,297
  • 16
  • 93
  • 124
Andrew Hare
  • 344,730
  • 71
  • 640
  • 635
  • 3
    I don't really count it as an optimization. Since they are local variables, the stack space is just allocated at the beginning of the function. There's no real "creation" involved to harm performance (unless a constructor is being called, which is completely another story). – Mehrdad Afshari Jun 11 '09 at 19:06
  • You are right, "optimization" is the wrong word but I am at a loss for a better one. – Andrew Hare Jun 11 '09 at 19:11
  • problem is that such an optimizer will use live range analysis, and both variables are rather dead. – MSalters Jun 12 '09 at 15:17
  • How about "the compiler won't see any difference between them once it does the data flow analysis". Personally I prefer that the scope of a variable should be confined to where it is used, not for efficiency but for clarity. – greggo Mar 05 '14 at 18:54
9

For a built-in type there will likely be no difference between the 2 styles (probably right down to the generated code).

However, if the variable is a class with a non-trivial constructor/destructor there could well be a major difference in runtime cost. I'd generally scope the variable to inside the loop (to keep the scope as small as possible), but if that turns out to have a perf impact I'd look to moving the class variable outside the loop's scope. However, doing that needs some additional analysis as the semantics of the ode path may change, so this can only be done if the sematics permit it.

An RAII class might need this behavior. For example, a class that manages file access lifetime might need to be created and destroyed on each loop iteration to manage the file access properly.

Suppose you have a LockMgr class that acquires a critical section when it's constructed and releases it when destroyed:

while (i< 100) {
    LockMgr lock( myCriticalSection); // acquires a critical section at start of
                                      //    each loop iteration

    // do stuff...

}   // critical section is released at end of each loop iteration

is quite different from:

LockMgr lock( myCriticalSection);
while (i< 100) {

    // do stuff...

}
Michael Burr
  • 333,147
  • 50
  • 533
  • 760
7

Both loops have the same efficiency. They will both take an infinite amount of time :) It may be a good idea to increment i inside the loops.

Larry Watanabe
  • 10,126
  • 9
  • 43
  • 46
  • Ah yes, I forgot to address space efficiency - that's ok - 2 ints for both. It just seems odd to me that programmers are missing the forest for the tree -- all these suggestions about some code that doesn't terminate. – Larry Watanabe Jun 11 '09 at 21:08
  • It's OK if they don't terminate. Neither one of them is called. :-) – Nosredna Jun 12 '09 at 00:53
2

I once ran some perfomance tests, and to my surprise, found that case 1 was actually faster! I suppose this may be because declaring the variable inside the loop reduces its scope, so it gets free'd earlier. However, that was a long time ago, on a very old compiler. Im sure modern compilers do a better job of optimizing away the diferences, but it still doesn't hurt to keep your variable scope as short as possible.

  • The difference comes probably from the difference in scope. The smaller the scope the more likely the compiler is able to eliminate serialization of the variable. In the small loop scope, the variable was likely put in a register and not saved on the stack frame. If you call a function in the loop, or dereference a pointer the compiler doesn't really know where it points to, it will spill the loop variable if it is in function scope (the pointer might contain `&i`). – Patrick Schlüter Dec 01 '15 at 10:14
  • Please post your setup and results. – jxramos Oct 15 '16 at 00:19
2
#include <stdio.h>
int main()
{
    for(int i = 0; i < 10; i++)
    {
        int test;
        if(i == 0)
            test = 100;
        printf("%d\n", test);
    }
}

Code above always prints 100 10 times which means local variable inside loop is only allocated once per each function call.

Byeonggon Lee
  • 196
  • 10
0

The only way to be sure is to time them. But the difference, if there is one, will be microscopic, so you will need a mighty big timing loop.

More to the point, the first one is better style because it initializes the variable var, while the other one leaves it uninitialized. This and the guideline that one should define variables as near to their point of use as possible, means that the first form should normally be preferred.

  • "The only way to be sure is to time them." -1 untrue. Sorry but another post proved this wrong by comparing the generated machine language and finding it essentially identical. I don't have any problem with your answer in general, but isn't wrong what the -1 is for? – Bill K Jun 12 '09 at 00:58
  • Examining emitted code is certainly useful, and in a simple case like this may be sufficient. However, in more complex cases issues such as locality of reference rear their head, and these can only be tested by timing the execution. –  Jun 12 '09 at 07:03
-1

I think that most answers are missing a major point to consider which is: "Is it clear" and obviously by all the discussion the fact is; no it is not. I'd suggest in most loop code the efficiency is pretty much a non-issue (unless you calculating for a mars lander), so really the only question is what looks more sensible and readable & maintainable - in this case I'd recommend declaring the variable up front & outside the loop - this simply makes it clearer. Then people like you & I would not even bother to waste time checking online to see if it's valid or not.

Rob
  • 15
  • 1
-1

With only two variables, the compiler will likely be assign a register for both. These registers are there anyway, so this doesn't take time. There are 2 register write and one register read instruction in either case.

MSalters
  • 173,980
  • 10
  • 155
  • 350
-6

thats not true there is overhead however its neglect able overhead.

Even though probably they will end up at same place on stack It still assigns it. It will assign memory location on stack for that int and then free it at the end of }. Not in heap free sense in sense it will move sp (stack pointer) by 1. And in your case considering it only has one local variable it will just simply equate fp(frame pointer) and sp

Short answer would be: DONT CARE EITHER WAY WORKS ALMOST THE SAME.

But try reading more on how stack is organized. My undergrad school had pretty good lectures on that If you wanna read more check here http://www.cs.utk.edu/~plank/plank/classes/cs360/360/notes/Assembler1/lecture.html

grobartn
  • 3,510
  • 11
  • 40
  • 52