What about variables' life times?

Started by
8 comments, last by GameDev.net 19 years, 4 months ago
Lately I've wondering when thinking about micro-optimizations whether compilers are intelligent enough to notice when a local variable in a function stops being used and hence can be used for something else. Let me illustrate my point:
void some_function(void)
{
    int a, b, c;

    a = 0;
    b = 2;
    std::cout << a << std::endl;
    c = b + 3;
    b = -2;
    std::cout << b << c << std::endl;
}
Here the variables a and c are separate periods of usage. In fact, I can remove c totally and just reuse a for my calculations. Now, there are situations where I don't might want to do this (or forget to). More often, variable names will be more specific than a, b, c, like "turret_angle" or "particle_count". If operations on these variables are performed sequentically in a function the maximum memory used should be that of the largest variable. Of course, more than two variables can be analyzed like this; actually every variable in the function can. In big functions I can often find myself having a dozen or so of variables of which many are interchangable in terms of memory locations. Still, I prefer to keep them different because A) to keep clarification and B) I may add code later which breaks any sharing they may have, and anty spent time sticking two variables together would be in vain, C) I might actually forget what variables were stuck together and it can cause nasty logical bugs that are difficult to locate. What I ask: Do most compilers recognize these situations and reuse memory locations to avoid unnecessary resource pile-up? It does not matter in my simple examples here, but I still like to keep my code as efficient as possible when it comes to these "dumb" things. I also believe it can be pretty important if one writes a very big function with considerable arrays that are accessed in a for the compiler predictable manner and could therefore optimize.
Advertisement
If i was take a guess, which is most likely to be wrong, i'd say no, i dont think they would. It would effectively be changing ur code since it would compile it into not what u'd want it to. Also i don't think there would be much, if any performance or efficency improvement by doing this.

Someone prove me wrong please.

ace
Compilers are smarter than most people think.

However, the general rule of thumb is to declare variables just before they need to be used, not at the start of your program.

(IE. you would declare c before assigning the value of b + 3 to it.)

IMHO, I would think compilers could make these optimizations. Nothing in your example is run-time dependent, so, the compiler could assume what/where/how the variables are going to be used, espically if they stay local in scope.


Possibly. The compiler does a whole lot of voodoo magic when it comes to variables (caching them in registers and whatnot) and I've been rather impressed with some of the optimizations they're able to do.

But this is an almost completely moot point. This is beyond a micro-optimization - this isn't even an optimization anymore. Since you're using extreemly temporary variables, the compiler is likely to use registers. Sharing the same variable name repeatedly increases the chances of the compiler being less able to make a quick optimization-away of said variable, forcing it to actually take up stack space.

The only case where I can think of this being useful is some recursion implementations - which are better off solved by increasing some memory limits via compiler options in most cases.

That said, as I'm sure there's probably at least a theoretical example where the compiler would not spit out the desired code, and every possible precaution needed to be taken to ensure that used sizes were at the bare minimum, you would manually force scoping:

void some_function( void ){    int b;    {        int a = 0;        b = 2;        cout << a << endl;    }    {        int c = b + 3;        b = -2;        cout << b << c << endl;    }}


If your compiler wasn't able to optimize that down for god knows why, and you can't switch to a decent compiler for god knows why, or if your program has overlapping but not nested variable lifetimes, you would probably write the relevant bits in assembly. You might want to do this anyways at that level of micro-optimization. If for god knows why you can't do that, then the most mantainable solution (which requires the compiler to be able to optimize away a minor detail) would be something like this:

void some_function( void ){    int int_1;    int int_2;    int & a = int_1; //compiler should be able to know a is always int_1, and compile as so.    int & b = int_2;    int & c = int_1;    a = 0;    b = 2;    cout << a << endl;    c = b + 3;    b = -2;    cout << b << c << endl;}


But, to even get to the point where such code would be actually useful given the hard to find bugs you will introduce when a and c are used concurrently, the only time you should use this is if you know exactly what you're doing and you've allready been poking your mouse into the assembly versions of your function and debating other approaches. Since you havn't posted a problem with a specific real-use example (aka, "I have this function, but it takes up too much memory causing crashes when it calls itself recursively one too many times"), I must suggest you NOT USE THIS!!! This source is provided for the education of knowing how to deal with such an issue, and to hopefully expand your thinking laterally to help increase your ability to deal with miscellanious problems in the future.
"variable" doesn't even really mean much down at the machine code level, so don't worry about it.
If your compiler has the option to dump the compiled assembly, you could probably learn quite a bit from that - even if you don't know assembly. Since there are only a finite number of registers, the compiler will usually do its best to use them whenever possible, and if a variable has outlived its usefulness, the compiler can use the register that stored it for a new variable. I agree with MaulingMonkey - compilers can do really amazing stuff to optimize the code you write, and unless for some reason the code is using up too much space on the stack (in which case you might want to redesign the way your function works) you generally don't need to give much thought to the way your local variables work.
the variable is declared until end of scope so by that i prosume that the compiler will keep it around untill end of scope. however since it is all translated to machine code iam sure the compiler works out the best rout for ur code, for example i know that register, although being a c++ keyword in other compilers is simply ignored in vc++ as it picks itself when to put variables directly onto a register.
Every compiler (MSVC has it, just dig around in the options "Output files", gcc has it, just add a -s IIRC) has an option to dump the assembly code. Look at it.
Quote:Original post by ace_lovegrove
If i was take a guess, which is most likely to be wrong, i'd say no, i dont think they would. It would effectively be changing ur code since it would compile it into not what u'd want it to. Also i don't think there would be much, if any performance or efficency improvement by doing this.

Someone prove me wrong please.

ace
Borland's Delphi compiler does this I think. Even though in Delphi/Pascal all variables are declared at the top of a function, it must work out the lines where it is first and last used and reuse that bit of memory for variables where their calculated scope does not overlap. Provided you don't take the address of the variables or something like that.
C++ compilers might already do this optimisation to some extent too, but probably only if you declare them in seperate curly-brackets.
An optimisation of this nature would NEVER change the meaning of the code, so it should compile it into EXACTLY what you'd want it to, unless the compiler's optimisation was buggy.

The performance improvement COULD potentially be huge, in the extreme case of a recursive function which has many variables where their scope doesn't overlap. Stack usage could vary hugely depending upon the presence of this optimisation. It could even mean the difference between a stack overflow and not.

This may be an unlikely scenario, but entirely possible.
"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms
Unwise owl: Yes, optimizing compilers do dataflow analysis. (I've taken a course on compiler construction at my university.)

The compiler will find out that a is not used anymore and can reuse that register/memory space. This is not at all be "changing your code" as someone said. If you never use the variable again, it is pointless to save it.

A good optimization in a compiler would be to even replace the a in the cout-expression with an immediate value, probably. I don't know if compilers does this, though.

This topic is closed to new replies.

Advertisement