8

in this example, even though i will never use the variables WNDCLASSEX, x, y, cx, cy, they will still use memory when i'm in the message loop:

int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpArgs, int iCmdShow)
    {
     WNDCLASSEX wc;
     ...
     RegisterClassEx(&wc);

     const int cx = 640;
     const int cy = 480; 
     // center of the screen
     int x = (GetSystemMetrics(SM_CXSCREEN) - cx) / 2;
     int y = (GetSystemMetrics(SM_CXSCREEN) - cy) / 2;

     CreateWindow(..., x, y, cx, cy, ...);

     MSG msg;

     while (GetMessage(&msg, NULL, 0, 0) > 0)
     {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
     }
     return 0;
    }

But i'm wondering, if i put them in a scope, would they still use memory during the message loop? e.g.

int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpArgs, int iCmdShow)
{
 {
  WNDCLASSEX wc;
  ...
  RegisterClassEx(&wc);

  const int cx = 640;
  const int cy = 480; 
  // center of the screen
  int x = (GetSystemMetrics(SM_CXSCREEN) - cx) / 2;
  int y = (GetSystemMetrics(SM_CXSCREEN) - cy) / 2;

  CreateWindow(..., x, y, cx, cy, ...);
 }

 MSG msg;

 while (GetMessage(&msg, NULL, 0, 0) > 0)
 {
  TranslateMessage(&msg);
  DispatchMessage(&msg);
 }
 return 0;
}

or maybe if i put them into two functions and called them in winmain e.g.

wnd_register(hInst);
wnd_create(hInst);

would that prevent them from using the memory?

Kaije
  • 2,631
  • 6
  • 38
  • 40
  • good question +1 Although, it would be effortless to test like nightcracker says. Could have spent less time testing it yourself than typing this question up! – Nick Rolando Jan 13 '11 at 01:05
  • 1
    Why would you declare variables that you don't use – Falmarri Jan 13 '11 at 01:09
  • This is really a useless micro-optimization. Threads on Windows by default have a 1 MB stack. You are worried about wasting less than one one hundredth of a percent of your available stack space. – Michael Jan 13 '11 at 01:19
  • i didn't test because i thought maybe if the compiler saw that i didn't use it after that scope(even if i could), it would optimize it out – Kaije Jan 13 '11 at 01:28

9 Answers9

6

The compiler has a lot of leeway for handling simple locals, like you have in your examples. They may live on the stack, they may only exist as immediate values in the machine code, or they may just live in registers. Stack space is usually allocated on entry to a function. The compiler will subtract some value from the stack pointer to make space for all the locals. On return of the function, the stack pointer is restored back to its original value. This is not usually done on exit of different scope blocks. Most compilers will try to aggressively reuse stack space as soon as variables are no longer used. In your example, it'd be perfectly legal for x and msg to have the exact same address on the stack, since their usage is non-overlapped.

My answer to this question goes into more detail on how local variables are allocated on the stack.

In your examples, the constants, cx and cy, most likely will have no memory backing them at runtime, and just be immediate values in the generated code. x and y will most likely live in registers until they need to be pushed on the stack for the call to CreateWindow. wc and msg will almost definitely be on the stack.

You shouldn't worry about micro-optimizations at this level - let the compiler allocate space for local variables as it sees fit. You have a 1 MB stack by default, the amount of data consumed by these variables wouldn't even register as noise. Spend your time worrying about more interesting problems instead.

Community
  • 1
  • 1
Michael
  • 54,279
  • 5
  • 125
  • 144
  • +1, though actually the compiler probably CANNOT overlap wc and x, as wc has its address taken and passed to a function, so it might be accessed in a later call while its still live, such as CreateWindow. I say probably as the compiler could conceivably do cross-function analysis, but most do not – Chris Dodd Jan 13 '11 at 02:47
  • Yep, you're right, taking the address does prevent the compiler from reusing the space. The compiler can't do cross-function analysis here since RegisterClassEx and CreateWindow live in a different module. – Michael Jan 13 '11 at 04:51
3

Probably not, but that's an implementation detail. They will have been destroyed though (destructor calls will have been made if there are any to make). Whether and when the system recovers memory used for automatic storage is not specified by the standard. Most do give it back pretty much immediately afaik.

Edward Strange
  • 40,307
  • 7
  • 73
  • 125
  • Any dynamic memory used inside class objects, such as in strings, should be released immediately by the object's destructor. I wonder if there are compilers that retain stack memory until the end of the function for speed purposes, even though it's usually only 1 instruction to release it? – Mark Ransom Jan 13 '11 at 01:12
  • 1
    Most compilers will aggressively reuse stack space. It'd be perfectly legal for msg and wc to have the exact same address in both his examples. – Michael Jan 13 '11 at 01:14
  • @Michael, I can see how that's possible for the second case, but for the first one the compiler can't call the destructor for `wc` until the end of the function so it can't share memory with anything. – Mark Ransom Jan 13 '11 at 01:26
  • WNDCLASSEX is a simple struct and doesn't have a destructor. Even if it did have a destructor, the compiler could reuse the memory as long as that destructor doesn't touch that memory. – Michael Jan 13 '11 at 01:30
  • Also, I want to emphasize that it'd be legal for a compiler to do this, not that all optimizing compilers will definitely perform the optimization I stated. – Michael Jan 13 '11 at 01:35
  • 1
    @Michael, I forgot about the POD case - if the compiler knows the object doesn't have a destructor and also looks ahead to see the variable isn't used before the end of the block, it's certainly free to reuse it. Your point about the optimization being optional is well taken, but as you implied earlier it's an easy one that most compilers would take advantage of. If the object has an inline destructor the compiler could do some analysis of it to make sure there are no side effects, but that's a little more iffy. – Mark Ransom Jan 13 '11 at 01:44
1

Well, I'm not sure about them using memory or what the standard say about it.

What I do know is that at the end of a memory block { } the destructor will be called and variables will be unreachable. This could mean that, while it's not freed, at least it can be reused.

Example:

struct Foo {
    Foo(void) { std::cout << "Hi!"; }
    ~Foo(void) { std::cout << "Bye!"; }
};

int main(int argc, char * argv[])
{
    {
        Foo bar; // <- Prints Hi!
    } // <- Prints Bye!

    // Memory used by bar is now available.
}

Edit: Thanks Tomalak Geret'kal ;)

BenMorel
  • 34,448
  • 50
  • 182
  • 322
MatiasFG
  • 576
  • 2
  • 8
  • "This could mean that, while it's not freed, at least it can be reused." - You have no way of controlling where your automatic variables go. There'd be no way to "reuse" memory not given back yet. – Edward Strange Jan 13 '11 at 01:23
  • Note that since you have an explicit constructor that runs code, the compiler MUST run it on exit of the block. This doesn't say anything about the underlying stack space. – Michael Jan 13 '11 at 01:24
  • @Noah, popping one variable off the stack and pushing another back on is essentially a NOP. Why wouldn't a compiler simply reuse the memory and construct a new object in-place? *You* might not control where your automatic variables go, but the compiler certainly does. – Mark Ransom Jan 13 '11 at 01:30
  • @Mark - again, probably. An implementation is free to do otherwise though. In fact it doesn't even have to use a stack. The things we KNOW happen in C++ are that destructors get called and the names become unavailable. The rest is up to the implementation, which is of course going to do something reasonable so that it's useful, but what kind of tricks the implementation pulls that may violate any generic, common-sense answer we might be tempted to give make such answers inadvisable and possibly in error. – Edward Strange Jan 13 '11 at 01:52
1

One magic piece of advice: Trust your compiler. It optimizes. It is clever. It optimizes better than most of us could.

If you're unsure, use a profiler or examine the assembler output of the compiler after optimizations. But remember - trivial optimizations is something you should not do in your code, as it's pointless and only hurts your code's readability.

Some variables (especially the constants) will not use any memory on the stack because they will be either mapped onto CPU registers or embedded directly into an assembler instruction.

This implies that the codes:

func(123+456*198*value);

and

int a = 123;
int b = 56;
int c = 400;
int d = b+c;
int e = d*198;
e *= value;
e += a;
func(e);

would compile to exactly the same thing (if the variables are never used again).

Seriously, don't bother. If you want to optimize, optimize from the algorithmic point of view, not syntaxical.

Kos
  • 70,399
  • 25
  • 169
  • 233
0

Oh god no, four integers in the memory while the program is running, what a waste!

  1. Try it out, a simple messagebox trying to print them should suffice (I think).
  2. Don't even mind.
orlp
  • 112,504
  • 36
  • 218
  • 315
0

Your declared variables inside the {}'s will go out of scope and be lost. In fact, you'll get a compile error if you try to use them outside the block: 'x' is undeclared. However, this it is sloppy. Just make a function for this code, as you said in your edit. Keeping your main() as few lines as possible is plain good programming practice.

Nick Rolando
  • 25,879
  • 13
  • 79
  • 119
0

They will not. They will only live until the end of their enclosing block.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
0

If you put them in a nested scope within the function (your first option), then when control reaches the end of the scope, the variables become inaccessible (either a compile error if you use them directly, or runtime undefined behavior if you save a pointer to one of them), their destructors are run (if there are destructors), and the implementation may reuse their storage space within the stack frame. But the standards do not require it to reuse the space.

If you split your function in two (your second option) ... in terms of standard-hairsplitting, there is no difference! When the function returns, the variables become inaccessible, their destructors are run, and the implementation may reuse their storage space, but it is not required to. And there have been serious implementations -- albeit not of C/C++ -- that do not immediately recycle that memory: see most famously the paper "Cheney on the M.T.A."

However, all implementations of C/C++ that I am presently aware of do recycle the memory allocated for a function's local variables when a function returns. Recycling of memory for nested-local-scope variables is much less certain. In any event, as several other people have mentioned, it is not worth worrying about a few tens of bytes of stack space in this context.

Personally, I would break up your code into two functions just because that way each function does only one task. That's generally better for long-term maintenance.

zwol
  • 135,547
  • 38
  • 252
  • 361
0

Generally yes, if the variable resides on the stack at all, then space for it will be taken on the stack for the entire duration of the enclosing function. Compilers usually calculate the maximum amount of space the function's variables could occupy and then make the function allocate all of it at once when the function is first entered. Constructors and destructors will still get called upon entry and exit of the inner scopes, though. Space for variables in one scope may get reused to represent variables from a separate scope.

Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467