5

If I do one of the following (which I assume are equivalent for the purpose of this question)

for(int i=0; i<A; i++)
{
  //... do stuff
  for(int j=0; j<B; j++)
  {
    //... do stuff
  }
  //... do stuff
}

for(int i=0; i<A; i++)
{
  int j;
  //... do stuff
}

Does the variable j get recreated on the stack every loop (is the SP constantly being updated every loop), or is the compiler smart enough to know how many local variables a function might have at one time, and then makes room for all of them on the stack at function entry?

I understand that this is theoretically compiler-dependent, but I assume simple things like this are common across all major compilers. If not, does someone know specifically about GCC and VC++ compilers?

Baruch
  • 20,590
  • 28
  • 126
  • 201
  • 4
    You could look at the assembly generated with different optimization levels. It is very likely the variables will only be created once. – juanchopanza May 26 '13 at 17:06
  • Usually gets optimized away. Don't bother declaring it outside. Its not necessary. – dchhetri May 26 '13 at 17:08

4 Answers4

9

It is efficient. gcc will optimize that, like most of the modern compilers. Also keep in mind what Donald Knuth said:

We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil.

You can check that it is efficient by comparing assembly code. Use for example diff to do the comparison. To generate assembly use -S flag, gcc -S. It is equivalent to gcc -S -O0, because default optimization level is 0. So, even on the lowest level optimizaton, gcc will take care of this variable for you.

First version (for better readability prefer this way):

#include <stdio.h>

int main()
{
    for (int i = 0; i < 10; i++)
    {
        for (int j = 0; j < 10; j++)
        {
            printf("%d ", i + j);
        }
    }
    return 0;
}

Second version:

#include <stdio.h>

int main()
{
    for (int i = 0; i < 10; i++)
    {
        int j;
        for (j = 0; j < 10; j++)
        {
            printf("%d ", i + j);
        }
    }
    return 0;
}

Identical assembly result:

    .file   "main.cpp"
    .section    .rodata
.LC0:
    .string "%d "
    .text
.globl main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    .cfi_personality 0x3,__gxx_personality_v0
    pushq   %rbp
    .cfi_def_cfa_offset 16
    movq    %rsp, %rbp
    .cfi_offset 6, -16
    .cfi_def_cfa_register 6
    subq    $16, %rsp
    movl    $0, -4(%rbp)
    jmp .L2
.L5:
    movl    $0, -8(%rbp)
    jmp .L3
.L4:
    movl    -8(%rbp), %eax
    movl    -4(%rbp), %edx
    leal    (%rdx,%rax), %eax
    movl    %eax, %esi
    movl    $.LC0, %edi
    movl    $0, %eax
    call    printf
    addl    $1, -8(%rbp)
.L3:
    cmpl    $9, -8(%rbp)
    setle   %al
    testb   %al, %al
    jne .L4
    addl    $1, -4(%rbp)
.L2:
    cmpl    $9, -4(%rbp)
    setle   %al
    testb   %al, %al
    jne .L5
    movl    $0, %eax
    leave
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3"
    .section    .note.GNU-stack,"",@progbits

int i is -4(%rbp), int j is -8(%rbp). As you can see int j is not reallocated or something.

Adam Stelmaszczyk
  • 19,665
  • 4
  • 70
  • 110
  • 2
    Your Knuth quote would look more serious if you applied the **other** rule of optimization, “do not make generalizations based on unrepresentative testcases”. Your nested for loops have no effects and any compiler worth its salt will optimize both versions of `f()` to `return 0;`, but this may or may not imply that the compiler always generates the same code in the far more common case where the loops are not trivial. – Pascal Cuoq May 26 '13 at 21:48
  • It wasn't optimized to just `return 0`, but I get your point. I made an edit, thank you. The assembly is still the same. – Adam Stelmaszczyk May 27 '13 at 08:29
1

I believe the variables will only be created once, although I do not care and I don't believe you should either.

This is likely an example of pre-optimization (or unnecessary optimization) on your part; the potential inefficiency created by declaring variables within a loop is incredibly small and "optimizing" your code by declaring variables in different locations will have a negligible impact on the overall runtime and memory usage of your program.

Consider spending time optimizing your algorithms and finding efficient data structures, as this will likely be a much better use of your time.

Jake Greene
  • 5,539
  • 2
  • 22
  • 26
  • In a way, this is a question about compilers, not about code optimization. I am not actually caring about this, just curious. – Baruch May 26 '13 at 17:22
  • I am sorry, but "I believe" and "I do not care" disqualifies the answer. The question is valid and deserves a bit more elaboration than this. Perhaps, although unlikely, this loop is the loop that some super computer spends most of its time with. – johannes_lalala Oct 07 '19 at 20:37
0

Compilers are too much powerful today to optimize such things. So don't bother and use as per your convenience. Premature optimization is more evil.

shadow
  • 90
  • 11
0

Why not try it yourself and see?

class foo {
public:
    foo () { std::cout << "Construct\n"; }
    ~foo () { std::cout << "Destruct\n"; }
    };

int main () {
    for ( int i = 0; i < 10; ++i ) {
        foo f;
    }

    return 0;
}

You'll see that the constructor (and destructor!) for f gets called each time through the loop. So the answer to your question is "Yes, the variable gets recreated each time through the loop."

Going back to your example, you are declaring an int, which has a constructor and destructor that do nothing. So, there's no performance penalty for declaring an int inside your loop.

Marshall Clow
  • 15,972
  • 2
  • 29
  • 45
  • 4
    Constructors and destructors are a separate issue from stack adjustments. – aschepler May 26 '13 at 17:12
  • 2
    That's not the same case as a built in type, since built-ins have no implicit constructor/destructor or initialization. – Robert S. Barnes May 26 '13 at 17:13
  • I was asking about stack adjustment. Constructors can be called without stack adjustment via placement `new`, which is what happens anyway with each construction of an object (memory is allocated and then the contructor gets called for that memory) – Baruch May 26 '13 at 17:20
  • Ok. Sorry for the confusion. Stack adjustment is really cheap, though (changing the value in a register) – Marshall Clow May 26 '13 at 17:22