Optimizing Field Initializers

Brad Abrams posted a blog(https://blogs.msdn.com/brada/archive/2004/10/05/238446.aspx) on field initializers and static constructors yesterday. In the VS2005 Beta the C# compiler added an optimization for instance field initializers which get assigned to their default value. For example:

class C

{

    public C () {}

   

    private int i = 0; // the C# compiler optimizes out this assignment because it does not have any affect

    private int j = 1; // ... of course this is never optimized out

}

We added this optimization to the compiler because a lot of C# programmers always specify field initializers, even to the default value provided by the runtime, to make their code more explicit. Now with this optimization folks who want to write explicit code don’t get a performance penalty.

This optimization works if the field initializer is a constant expression which evaluates to the default value for the type of the field. The default value for a field can be written in several different ways, depending on the type of the field. For example, this optimization will kick in all of the following cases:

    private int i1 = 0; // the default value for integral types is zero

    private int i2 = new int(); // ... the parameterless constructor also yields

                                    // the default value for value types

    private int i3 = default(int); // ... the new default(T)syntax in VS 2005

    private int i4 = 1 - 1; // ... but also constant expressions

    private string s1 = null; // ... null is the default value for reference types

    private string s2 = default(string); // ... which can also be written as default(T)

However, we did not implement this optimization for static fields, which led to Brad’s blog posting. Thus if you have a field initializer for a static field, which initializes the static field to its default value, you still get a static constructor, or .cctor in CLR speak) which is a significant performance penalty. So this begs the question, why did we not add this optimization for static fields?

There is a subtle difference between instance field initializers and static field initializers. When an instance field initializer begins execution the value of the field being initialized is guaranteed to be the default value. This is a result of 2 things - instance field initializers run as the first thing after the object is allocated, even before the call to your base class constructor, and during an instance field initializer, you cannot reference other instance fields of the object being created. This guarantee, that a field’s value is the default before the initializer is run, does not hold for static fields. This is illustrated by this nasty example:

class C

{

    public C () {}

   

    private static int x = (C.y = 5); // Ugh! assigns y = 5

    private static int y = 0; // ... always does work! changes y from 5 back to 0!!!

}

Here, when the field initializer for C.y executes, the value of C.y is 5, so optimizing out the assignment would be a bug in the compiler. You could argue that the compiler could detect this kind of pathological code, however determining when to allow this optimization for static fields, is actually not a solvable problem. One of the things which make this analysis difficult is that the assignment to C.y could occur in a method which is called by any class’s static constructor. The other issue, is that the order of static constructor calls is not guaranteed by the runtime.

There is, however, one case where the compiler can determine that optimizing out the static field initializers is correct. That is when the class has no explicit static initializer, and all the static field initializers on the class initialize to the default value. So optimizing out all the field initializers and static initializers would be correct in this case:

class C

{

    public C () {}

   

    private static int x = 0; // OK, to optimize these out because

    private static int y = 0; // ... all the field intializers for C assign the default value

}

I’m hoping to get this optimization into the Beta 2 release of the C# compiler. OK, that’s it for today