Search code examples
c#compiler-constructionjit

Does .NET optimize the code before compiling?


I was browsing over my code and I recalled something that the ActionScript compiler does: it simplifies unnecessary/redundant code, then compiles the result. I wanted to know if C# has the same sort of procedure.

If I have my understanding correctly, and assuming that this would be valid ActionScript (I know it's not), the compiler would take this:

byte[] result = ServerWC.UploadValues("http://localhost", srvNvc);
Console.WriteLine(Encoding.ASCII.GetString(result));

and simplify it to this:

Console.WriteLine(Encoding.ASCII.GetString(ServerWC.UploadValues("http://localhost", srvNvc)));

before compiling. Which compiler, C#->IL, or IL->machine, does the optimization take place? Do all compilers work this way?


Solution

  • There's nothing unnecessary in the code you've posted. Your change didn't really do any simplification at all - you only changed a pseudo-imperative block of code into a single expression.

    .NET compilation process is a bit complicated - first, you have the safe, managed C#. Then, you have the safe, managed IL. Finally, you get the unsafe, native x86 assembly (for example).

    IL is basically stack-based. So your code would turn into something like this:

    call UploadValues
    call Encoding::ASCII
    call Encoding.GetString
    call Console::WriteLine
    

    (needless to say, this is grossly oversimplified - but it is a rather high-level "assembly-like" language)

    You can see that even here, there no longer is any local variable, really. It's just a value on the virtual stack. Different compilers are within their rights to implement this differently, of course. For example, you could get something like this:

    call UploadValues
    stloc.0
    ldloc.0
    call Encoding::ASCII
    call Encoding.GetString
    call Console::WriteLine
    

    But it should be rather obvious that this really is an extra step, rather than a missing optimization :)

    The new Roslyn compiler does take advantage of a few things the old one didn't, though. For example, if the same local is used multiple times in the same method, it may be possible for it to avoid the local as well - a great example is today's question VS2015 stloc.0 and ldloc.0 are removed from compilation instructions.

    However, whatever happens here doesn't necessarily affect the performance of the resulting code. The next step, JIT compilation from IL to x86, is the part that does most of the optimizations. And that includes things like deciding that the locals and virtual stack in IL will be represented as, for example, values in a register.

    As for ActionScript, I assume the same applies from AS3 on. The difference might have been there in the old, interpreted version, but when it made the jump to a full blown JITted virtual machine, that difference probably disappeared.