views:

203

answers:

5

Hi,

Can someone please tell me whether AddB below will result in less CLR allocations than AddA? I've examined disassembly and it looks to be the case but I'd like confirmation from the Experts please. Can someone Exchange this information with me please?

Cheers, Charlie.


namespace A
{
    struct Vec2
    {
        public float x;
        public float y;

        public Vec2 AddA(Vec2 other)
        {
            Vec2 v = new Vec2(); // Reference variable
            v.x = x + other.x;
            v.y = y + other.y;
            return v;
        }

        public Vec2 AddB(Vec2 other)
        {
            Vec2 v;              // Value variable
            v.x = x + other.x;
            v.y = y + other.y;
            return v;
        }
    }
}
+6  A: 

If Vec2 is a struct in both examples, by using Vec2 v = new Vec2(); you are not creating a reference to your struct, you are simply creating a new struct on your stack. If you don't use the new keyword, your struct is nevertheless created on the stack and you can initialize each field separately.

In that case, using the new keyword for a struct does't make much sense if your constructor doesn't accept some meaningful parameters to initialize the data in a single line.

If the first method uses a class instead of a struct, then it does create an object for GC to collect, unlike the second method. Since v in AddB is allocated on the stack, it is not collected, the stack is simply popped when your method is finished.

Groo
+1, but note that if Vec2 is a class then AddB simply won't work
Henk Holterman
Note: If Vec2 would be a class, the code in AddB doesn't work at all. You have to create an instance of a class to use it.
Guffa
Yeah, and also make sure that the GC is works on a highly optimized mechanism. Though you can control the GC behavior somehow using the GC class-which is a handle on the GC for your process, It's very discouraged to do it. http://msdn.microsoft.com/en-us/library/ms973837.aspx
Galilyou
thanks for this, very clear - there are no reference or value variables, just reference and value types. Got it.
cskilbeck
+3  A: 

There's no guarantee for anything. Value items are not necessarily stored in the heap.

See The Stack Is An Implementation Detail by Eric Lippert, and the following second part.

Simon Svensson
Thankyou, this looks like a good read.
cskilbeck
Good link. However: while this *is* an implementation detail, the OP asked specifically for such details. Since these are not likely to change in the next few years, assumptions about the local variable being allocated on the stack can be relied on.
Konrad Rudolph
Although it's an implementation detail, the CLR cannot choose to allocate the struct as a GC tracked reference, so it will not add memory pressure on the GC anyway.
Cecil Has a Name
An excellent read, to be sure, although it's worth bearing in mind that the CLR runs on some platforms with a much less friendly garbage collector than the Windows implementation. XNA Game Studio (which is awesome, and based on C# and managed DirectX) runs on both Xbox and Windows, and garbage collects quite differently on the 2 platforms which can make a big difference to your frame rate.
cskilbeck
+1  A: 

Note that AddA and AddB do not differ in their memory behaviour, both will be allocated on the stack. What does make a difference is that AddB does not call the default constructor for the type and thereby leaves the fields of the Vec2 uninitialized, whereas AddA will zero them out first.

Having said that I'd imagine that the performance hit of zeroing the contents is probably not measurable (if the JIT compiler doesn't even remove it altogether anyway).

jerryjvl
+3  A: 

The code in your AddA method simply does the same as:

public Vec2 AddA(Vec2 other) {
   Vec2 v;
   v.x = 0;
   v.y = 0;
   v.x = x + other.x;
   v.y = y + other.y;
   return v;
}

The parameterless constructor of a struct returns the default value for the struct, which is a value where all the members are zeroed out. The value is just returned by the call to the constructor, it's never allocated on the heap.

Also, a mutable struct is generally a bad idea. I would implement it like:

struct Vec2 {

    public float X { get; private set; }
    public float Y { get; private set; }

    public Vec2(float x, float y) {
       X = x;
       Y = y;
    }

    public Vec2 Add(Vec2 other) {
       return new Vec2(X + other.X, Y + other.Y);
    }

}
Guffa
For truly immutable the X/Y would probably be better implemented as public readonly float X;
Henk Holterman
+1  A: 

To add to the other answers:

Firstly, the code can be improved by using an appropriate constructor and thus omitting the tedious assignments. This isn't really optional: it should be done. Additionally, it would be good to implement such a structure as an immutable type, i.e. making the fields readonly so chances to it aren't at all possible: thus, you would implement pure value semantics.

Secondly, concerning the question of stack vs. heap allocation: I wouldn't bother too much here anyway: the kind of GC used in .NET is specialized to handle lots of such small allocations: your code would be the optimal scenario as far as the .NET GC is concerned. Don't try to avoid it.

Konrad Rudolph