Stack Variable and Speed

This is a discussion on Stack Variable and Speed within the C++ Programming forums, part of the General Programming Boards category; On almost all of the compilers I've used, space for variables is allocated just once at function entry, regardless of ...

  1. #16
    and the hat of wrongness Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    32,555
    On almost all of the compilers I've used, space for variables is allocated just once at function entry, regardless of whatever inner scope rules apply.

    The compiler then creates/destroys and limits scope visibility as per the semantics of the language.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.
    I support http://www.ukip.org/ as the first necessary step to a free Europe.

  2. #17
    Malum in se abachler's Avatar
    Join Date
    Apr 2007
    Posts
    3,189
    stack variables are allocated on scope entry, which for inner loops is entry to the loop, so -

    Code:
    for(int x = 0;x<5;x++){
        for(int y = 0;y<6;y++){
            int z = 0;
            }
        }
    y and z get allocated 5 times ( each entry of the inner loops scope), while x only gets allocated once on entry to the outer loop.
    Until you can build a working general purpose reprogrammable computer out of basic components from radio shack, you are not fit to call yourself a programmer in my presence. This is cwhizard, signing off.

  3. #18
    Captain Crash brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,243
    This program should demonstrate whether stack objects get allocated (NOT constructed) unconditionally at function entry.

    Code:
    #include <iostream>
    
    class foo
    {
    public:
    	foo()
    		: x(0)
    	{
    	}
    
    	int x;
    };
    
    class big
    {
    public:
    	foo y[1024];
    };
    
    void stacksize(int n, bool construct_big_object)
    {
    	if(n == 0) return;
    	std::cout << "Stack pointer: " << &n << std::endl;
    	stacksize(n - 1, construct_big_object);
    	if(construct_big_object)
    	{
    		big obj;
    	}
    }
    
    int main()
    {
    	stacksize(2, false);
    }
    On my system this displayed:

    Code:
    Stack pointer: 0x22ccc0
    Stack pointer: 0x22bc90
    Which indicates that the space for "big obj" is allocated at function entry, not when it is constructed (and it never is, in this example).
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  4. #19
    Registered User bboozzoo's Avatar
    Join Date
    Jan 2009
    Posts
    14
    Quote Originally Posted by abachler View Post
    stack variables are allocated on scope entry, which for inner loops is entry to the loop, so -

    Code:
    for(int x = 0;x<5;x++){
        for(int y = 0;y<6;y++){
            int z = 0;
            }
        }
    y and z get allocated 5 times ( each entry of the inner loops scope), while x only gets allocated once on entry to the outer loop.
    uhh write down this code and get it actually compiled, if x, y, z are not stored in registers then change your compiler (btw. the loop is pointless, so the compiler would most probably just get rid of it anyway)

  5. #20
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by bboozzoo View Post
    uhh write down this code and get it actually compiled, if x, y, z are not stored in registers then change your compiler (btw. the loop is pointless, so the compiler would most probably just get rid of it anyway)
    Or at the very least, just set z once outside the loop.

    Now, if we were to set z to x * y, and the end-values for the two loops were variables rather than compile-time constants, then the compiler MAY decide to calcualte x * y each time in the loop.

    But most likely [although there is no rule to say that a compiler MUST do it this way] the space for a variable is allocated at start of function, once and for all - no matter how many times the variable is actually "created" in a local scope. As has been "proven" above by brewbuck, of course.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  6. #21
    Captain Crash brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,243
    Quote Originally Posted by matsp View Post
    But most likely [although there is no rule to say that a compiler MUST do it this way] the space for a variable is allocated at start of function, once and for all - no matter how many times the variable is actually "created" in a local scope. As has been "proven" above by brewbuck, of course.
    Although the compiler could allocate space only as needed I don't think it would be worth it. First, large objects should not be placed on the stack anyway since stack is a scarce resource. Second, fiddling around with the stack frame will most likely interfere with other optimizations the compiler is trying to perform (like delayed argument cleanup) and it might even lead to pipeline stalls or cache problems since the frame base pointer is being manipulated.

    Even if your platform is extremely memory-limited, you (as the programmer) can break your function into multiple functions to gain more control over when stack allocation actually happens. I don't think any realistic compiler is going to bother optimizing this case.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  7. #22
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by brewbuck View Post
    Although the compiler could allocate space only as needed I don't think it would be worth it. First, large objects should not be placed on the stack anyway since stack is a scarce resource. Second, fiddling around with the stack frame will most likely interfere with other optimizations the compiler is trying to perform (like delayed argument cleanup) and it might even lead to pipeline stalls or cache problems since the frame base pointer is being manipulated.

    Even if your platform is extremely memory-limited, you (as the programmer) can break your function into multiple functions to gain more control over when stack allocation actually happens. I don't think any realistic compiler is going to bother optimizing this case.
    I completely agree with all of the above - I was just pointing out that there is (as far as I can see) no reason for this to be true in ALL cases.

    I have never seen a compiler that doesn't allocate the full space of the stack content at the very start of the function, and reclaiming the space at the end of the function - but I very much doubt that the standard REQUIRES this behaviour - it just makes the smallest code that runs the fastest, and since the space is PROBABLY needed in some cases, if you don't have a very borderline case of stack usage (that is, function F() is called from function G() only when it uses small amount of stack, and when F() is called without the interveening G() function, it uses the bigger amount of stack - but no, I don't think that's a case worth worrying about in a modern machine - it's a different story on severely limited stack-size processors, e.g. 6502 with a 256 byte stack [however, that probably use a software managed argument stack, because I'd very much doubt that even calling printf("Hello, World") could be done without using up the entire stack].


    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  8. #23
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,893
    Well, there are sensible functions where the compiler might want to avoid allocating everything up front, but I think this is rare enough that they don't.
    Code:
    void f()
    {
      if(something) {
        g();
        return;
      }
    
      huge_object ho;
      ...
    }
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  9. #24
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,596
    I've never seen a compiler that didn't allocate the entire stack space at the start of the function. This makes sense in almost every case. A simple sub esp,size is all that is needed.

    I really cannot think of many situations where this approach would not be optimal.

    Code:
    push ebp
    mov ebp,esp
    sub esp,needed_stack_space
    It does not make sense to incrementally subtract various variable sizes from esp as the compiler encounters local variable declarations. It makes more sense for the compiler to first compute how much size is needed to accomodate all local variables and then allocate that in one operation as opposed to many. It also makes no sense for the compiler to delay the allocation of a stack variable until it reaches it as in the case of a loop.

    Code:
    for (unsigned int i = 0;i < 10; ++i)
    {
       unsigned int total = 0;
       total = i * 5 + i;
    }
    In this case it has been my experience that total would be allocated long before the loop was ever reached in the code. Also if you notice the creation of total is essentially non-variant code that is inside of a loop and even the dumbest of compilers would optimize this out of the loop.
    Also you can see that the i variable used to loop is obviously not being allocated every time through the loop which according to the late de-allocation crowd would happen. Clearly this is not happening.

    I would say that the compiler in this case would do this:
    Code:
    push ebp
    mov ebp,esp
    sub esp,8
    ...
    However if the compiler chose to use ECX for i and do a simple LOOP operation then the stack size would change to 4 bytes. Depending on the calling convention of the function you may or may not see the stack cleaned up.
    Last edited by VirtualAce; 01-12-2009 at 05:50 PM.

  10. #25
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by Bubba View Post
    Code:
    for (unsigned int i = 0;i < 10; ++i)
    {
       unsigned int total = 0;
       total = i * 5 + i;
    }

    However if the compiler chose to use ECX for i and do a simple LOOP operation then the stack size would change to 4 bytes. Depending on the calling convention of the function you may or may not see the stack cleaned up.
    I would expect BOTH the i and total to be registers in this code. The compiler may even remove the entire loop, as all components are constants, and i & total aren't used outside the loop, so no need for a loop at all.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  11. #26
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,596
    Ok so bad example. But I have seen MSVC 2003 produce some pretty stupid assembly for a loop as simple as this.

  12. #27
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by Bubba View Post
    Ok so bad example. But I have seen MSVC 2003 produce some pretty stupid assembly for a loop as simple as this.
    Sure, the compiler MAY not realize what's going on and leave parts or all of the loop "floating about". But it COULD remove it completely too - we can't say for sure either direction.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  13. #28
    Malum in se abachler's Avatar
    Join Date
    Apr 2007
    Posts
    3,189
    Quote Originally Posted by bboozzoo View Post
    uhh write down this code and get it actually compiled, if x, y, z are not stored in registers then change your compiler (btw. the loop is pointless, so the compiler would most probably just get rid of it anyway)
    the point of the code was to demonstrate the situation, not produce functional code.
    Until you can build a working general purpose reprogrammable computer out of basic components from radio shack, you are not fit to call yourself a programmer in my presence. This is cwhizard, signing off.

Page 2 of 2 FirstFirst 12
Popular pages Recent additions subscribe to a feed

Similar Threads

  1. I am very new . . . :(
    By Eternalglory47 in forum C++ Programming
    Replies: 6
    Last Post: 09-05-2008, 11:29 AM
  2. Resource Management question..
    By Raigne in forum C++ Programming
    Replies: 37
    Last Post: 03-08-2008, 08:36 AM
  3. Replies: 6
    Last Post: 01-08-2006, 01:49 PM
  4. static class variable vs. global variable
    By nadamson6 in forum C++ Programming
    Replies: 18
    Last Post: 09-30-2005, 03:31 PM
  5. Replies: 5
    Last Post: 06-01-2002, 11:24 PM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21