Use if statements to micromanage stack

Go To StackoverFlow.com

4

Should if statements be used to assist in the stack's memory de-allocation?

Example A:

        var objectHolder = new ObjectHolder();
        if (true)
        {
            List<DefinedObject> objectList;
            using (var sr = new GenericStreamReader<DefinedObject>())
            {
                objectList= sr.Get().ToList();
            }
            if (true)
            {
                var DOF = new DefinedObjectFactory();
                objectHolder.DefinedObjects = DOF.DefineObjects(objectList);
            }
        }
        //example endpoint

Example B:

        var objectHolder = new ObjectHolder();
        List<DefinedObject> objectList;
        using (var sr = new GenericStreamReader<DefinedObject>())
        {
         objectList= sr.Get().ToList();
        }
        var DOF = new DefinedObjectFactory();
        objectHolder.DefinedObjects = DOF.DefineObjects(objectList);
        //example endpoint

Will Example A have a lighter footprint on the stack when example endpoint is reached versus when example endpoint is reached in Example B??

2012-04-03 23:27
by Travis J
I'd be mildly surprised if they didn't translate to the exact same machine code - cHao 2012-04-03 23:30
@cHao - But isn't the memory going to be de-allocated when the variables go out of scope - Travis J 2012-04-03 23:31
Not necessarily. Locals are allocated at the beginning of the function, and would more than likely only get cleaned up when the function ends - cHao 2012-04-03 23:32


5

Based on the usage, I'm assuming DefinedObjectFactory is a class, not a struct. Therefore, the only thing that's on the stack is a reference to DefinedObjectFactory. The actual object is on the heap, and is controlled by the garbage collector.

The only stack space you're potentially saving is the space for a single pointer, so it's not worth it.

2012-04-03 23:48
by David Yaw


12

First off, the whole point of a stack-based allocation system is precisely that you do not need to optimize it in any way. Don't worry about it. The jitter is perfectly capable of realizing that a local will never be read or written again, and re-using its storage if it feels that's the best thing to do. Let the jitter do its job; it doesn't need your help. (*)

Rather, write your program so that local variables make sense to the reader. That's what you should be optimizing for.

Finally, there is never a need to say "if (true) { }" to introduce a new scope. Just introduce a new scope. It is perfectly legal to say:

void M()
{
    { // new scope
    }

    { // another one
    }
} 

(*) There is a situation where the jitter needs your help, and that is the situation where a local refers to an object on the heap that contains a resource that is going to be used by unmanaged code. The jitter does not know that unmanaged code is going to use the object's resources, and might decide that no one is using this object any more and clean it up early. The finalizer of the object might then release the resource on the finalizer thread while the unmanaged code is using the resource! An object is not guaranteed to stay alive just because a local variable is holding onto it. If the local variable is never read from again then the jitter can re-use its storage and tell the garbage collector that it is safe to collect the referred-to object, which will then potentially crash the unmanaged code. You can use KeepAlive to hint to the jitter that a particular object needs to remain alive and not be optimized away.

2012-04-04 00:12
by Eric Lippert


7

if(true) will be compiled out in optimized build (the only build where variable lifespan is shorter than whole method) - so there is absolutely no difference between two versions you've suggested.

2012-04-03 23:33
by Alexei Levenkov


1

Even if this does make some difference, I think it's very likely that you're worrying about the wrong things. Is stack space allocation really an issue for your app?

In general, doing "clever" things in code for the sake of micro-optimizations is usually not worth it. It's usually a much better idea to write your code in the most clean and straightforward way possible. After doing that, if you find you actually have some perf/scalability problems (based on doing actual measurements), you can choose to rewrite/optimize the parts that are bottlenecks.

Most times you'll find that the clean/straightforward/readable version of the code performs just fine. And if it doesn't, the problems are probably not in the places you thought they were.

2012-04-03 23:40
by triangle_man
Absolutely true, but I find this question is often asked by people who don't understand the GC properly and therefore don't trust it. Like a hyper NIH syndrome or something - Russ Clarke 2012-04-03 23:42


0

There's only one thing you can guarantee about de-allocation and garbage collection - it will happen at some stage.

As other people have said, the only thing your if(true) will achieve is being optimised out by the Jitter.

You're already using a using(..) { } pattern so instead of using the if(true) block I'd refactor your code to support this:

if (true)
{
   var DOF = new DefinedObjectFactory();
   objectHolder.DefinedObjects = DOF.DefineObjects(objectList);
}

To:

using (var DOF = new DefinedObjectFactory())
{
   objectHolder.DefinedObjects = DOF.DefineObjects(objectList);
}

And see if that helps.

There is one otherthing that you can try but it's absolutely not recommended for production code as you shouldn't pre-empt the memory manager, but that is simply add a call to

GC.Collect();

When you exit your blocks.

I don't think it'll help but it might demonstrate to you why it's generally not worth worrying about scope and de-allocation.

2012-04-03 23:41
by Russ Clarke