Just curious about this. Following are two code snippets for the same function:
void MyFunc1()
{
int i = 10;
object obj = null;
if(something) return;
}
And the other one is...
void MyFunc1()
{
if(something) return;
int i = 10;
object obj = null;
}
Now does the second one has the benefit of NOT allocating the variables when something is true? OR the local stack variables (in current scope) are always allocated as soon as the function is called and moving the return statement to the top has no effect?
A link to dotnetperls.com article says "When you call a method in your C# program, the runtime allocates a separate memory region to store all the local variable slots. This memory is allocated on the stack even if you do not access the variables in the function call."
UPDATED
Here is a comparison of the IL code for these two functions. Func2 refers to second snipped. It seems like the variable in both the cases are allocated at the beginning, though in case of Func2() they are initialized later on. So no benefit as such I guess.
Peter Duniho's answer is correct. I want to draw attention to the more fundamental problem in your question:
Why ought that to be a benefit? Your presumption is that allocating the space for a local variable has a cost, that not doing so has a benefit and that this benefit is somehow worth obtaining. Analyzing the actual cost of local variables is very, very difficult; the presumption that there is a clear benefit in avoiding an allocation conditionally is not warranted.
To address your specific question:
I can't answer such a complicated question easily. Let's break it down into much simpler questions:
Storage locations for "ordinary" local variables -- and formal parameters of lambdas, methods, and so on -- have short, predictable lifetimes. None of them live before the method is entered, and none of them live after the method terminates, either normally or exceptionally. The C# language specification clearly calls out that local variable lifetimes are permitted to be shorter at runtime than you might think if doing so does not cause an observable change to a single-threaded program.
Storage locations for "unusual" local variables -- outer variables of lambdas, local variables in iterator blocks, local variables in async methods, and so on -- have lifetimes which are difficult to analyze at compile time or at run time, and are therefore moved to the garbage-collected heap, which uses GC policy to determine the lifetimes of the variables. There is no requirement that such variables ever be cleaned up; their storage lifetime can be extended arbitrarily at the whim of the C# compiler or the runtime.
Yes. If the C# compiler or the runtime can determine that removing the local from the program entirely has no observable effect in a single-threaded program, then it may do so at its whim. Essentially this is reducing its lifetime to zero.
This is an implementation detail, but typically there are two techniques. Either space is reserved on the stack, or the local is enregistered.
This is an implementation detail of the jitter's optimizer. There are many factors, such as:
Again, this is an implementation detail, but typically the answer is yes.
Typically, yes.
Suppose we have two locals, A and B, and one is used conditionally and the other is used unconditionally. Which is faster:
or
Keep in mind that "add one" and "add two" have the same cost.
This scheme is not cheaper if the variable B is unused, and has twice the cost if it is used. That's not a win.
Correct. Stack space is cheap. Or, more accurately, the million bytes of stack space you get per thread is insanely expensive, and that expense is paid up front, when you allocate the thread. Most programs never use anywhere close to a million bytes of stack space; trying to optimize use of that space is like spending an hour deciding whether to pay $5.01 for a latte vs $5.02 when you have a million dollars in the bank; it's not worth it.
In theory, yes. Whether the jitter actually makes this optimization -- an optimization which saves literally less than a billionth of a second -- I don't know. Keep in mind that any code the jitter runs to make the decision to save that billionth of a second is code that takes far more than a billionth of a second. Again, it makes no sense to spend hours worrying about pennies; time is money.
And of course, how realistic is it that the billionth of a second you save will be the common path? Most method calls do something, not return immediately.
Also, keep in mind that the stack pointer is going to have to move for all the temporary value slots that aren't enregistered, regardless of whether those slots have names or not. How many scenarios are there where the condition that determines whether or not the method returns itself has no subexpression which touches the stack? Because that's the condition you're actually proposing that gets optimized. This seems like a vanishingly small set of scenarios, in which you get a vanishingly small benefit. If I were writing an optimizer I would spend exactly zero percent of my valuable time on solving this problem, when there are far juicier low-hanging fruit scenarios that I could be optimizing for.
Yes. In the straightforward scheme where you move the stack pointer two slots and say "stack pointer is A, stack pointer + 1 is B", you now have a consistent-throughout-the-method way to characterize the variables A and B. If you conditionally move the stack pointer then sometimes the stack pointer is A, sometimes it is B, and sometimes it is neither. That greatly complicates all the code that uses A and B.
Then this becomes a problem in register scheduling; I refer you to the extensive literature on this subject. I am far from an expert in it.
The only way to know for sure when this happens for your program, when you run it, is to look at the code the JIT compiler emits when you run your program. None of us can even answer the specific question with authority (well, I guess someone who wrote the CLR could, provided they knew which version of the CLR you're using, and possible some other details about configuration and your actual program code).
Any allocation on the stack of a local variable is strictly "implementation detail". And the CLS doesn't promise us any specific implementation.
Some locals never wind up on the stack per se, normally due to being stored in a register, but it would be legal for the runtime to use heap space instead, as long as it preserves the normal lifetime semantics of a local vaiable.
See also Eric Lippert's excellent series The Stack Is An Implementation Detail