none
A peculiar .net compiler optimization issue RRS feed

  • General discussion

  • Recently, i found a strange issue when trouble shouting my project. The short story is like this: i wrote a library and tested it with NUnit, all things work fine, however, when it's deployed into a real testing environment, a strange exception was thrown out, it seems that the static constructor caused this issue. So i tried to write a simpler demo to reproduce it. Fortunately, i caught it again.
    here is the demo code:

    class Program
    {
    static void Main(string[] args)
    {
    Console.WriteLine("Optimization test...");
    ClassA.Initialize();
    Console.WriteLine("IN class B");
    ClassB inst = new ClassB();
    Console.WriteLine("Dingo!");
    Console.ReadKey();
    }
    }

    class ClassA
    {
    private static bool initialized = false;

    public static void Initialize()
    {
    if (initialized)
    return;
    Console.WriteLine("Initialization ClassA...");

    initialized = true;
    }

    public static int SomeMethodOrProperty()
    {
    Console.WriteLine("Invoking some static method or property in ClassA...");
    if (!initialized)
    throw new ApplicationException("ClassA is not initialized yet");
    return 10;
    }
    }

    class ClassB
    {
    static int number = ClassA.SomeMethodOrProperty();
    }



    When i build the program in "Debug" mode, it works fine. And then i tried to build it in "Release" mode (Code optimization is turned on), it fails when invoking ClassA.SomeMethodOrProperty() in ClassB , a stranger issue is: when i tried to start this program from visual studio(F5 to debug the program in "Release" mode ), it also works. although it fails when pressing "Ctrl+F5".
    So i guess there must be something wrong when .Net is optimizing the code. then i tried to modify the class ClassB:
    Case 1:
    class ClassB
    {
    int number = ClassA.SomeMethodOrProperty();
    }

    it also works very well.
    Case 2:
    class ClassB
    {
    static int number = 0;
    static ClassB()
    {
    number = ClassA.SomeMethodOrProperty();
    }
    }

    it still works...seems the optimization trick is in the static constructor of ClassB
    Case 3:
    class ClassB
    {
    static int number = ClassA.SomeMethodOrProperty();
    static ClassB()
    {
    }
    }

    Works again, unbelievable, huh?

    although i don't know the exact reason why .Net compiler works so differently when "ClassB" is organized slightly differently in code but identical in logic, I am sure the optimizer reset the static variables when invoking from ClassB.

    Then i tried a bit further, run the original code (the "bad" one which may throw exception) in release mode, then I attach the process and debug it from visual studio. it works again even the release binary file is not recompiled. So i can conclude that the JIT compiler make the wrong optimization in runtime. but how and why? I don't know it by now, i will dig this issue further.

    this issue seems also in later versions, for i just tried in visual studio .net express 2008.


    Liang(China)
    Thursday, December 18, 2008 8:13 AM

All replies

  • Due to the lack of a static constructor in your ClassB the compiler slaps on the beforeFieldInit flag meaning the exact moment of initalization is left up to the JIT.  And aparently it chooses the run the Initalizer for number first thing even before ClassA is initalized wreaking havoc in your code.

    MSDN Magazine had an article in 2005 that deals with statics which also talks about this behavior.

    Thursday, December 18, 2008 3:39 PM
  • Thanks Ray,
    Your explanation really helps, i am clear about the actual reason now.

    When using implicit type constructor in ClassB, JIT will call it in the very beginning, even ClassA is not initialized. at this point, type constructor will get the exception by ClassA and wrap it into a TypeInitializationException. And the TypeInitializationException is not thrown out immediately by JIT, instead, it will record this exception. When the first method is called by the code, that is the ClassB constructo, JIT will not call type constructor again, but just throw the recorded TypeInitializationException. That is why this demo failed in release build.
    Liang(China)
    Friday, December 19, 2008 3:19 AM
  • Agreed.  Your code criticially depends on the order in which the types are loaded and their statics are initialized.  You lose in Release mode, the static inside B is initialized before Main() starts running.  You'll get the same behavior in the debugger with Tools + Options, Deubgging, General, turn off "Suppress JIT optimization on module load".
    Hans Passant.
    Friday, December 19, 2008 2:30 PM
    Moderator