Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does the C# language compiler perform any actual optimizations on its own?

Based on random Internet comments, I've always believed that the C# compiler does simple optimizations to the IL (removing always-true if-statements, simple inlining, etc), then later the JIT performs the real, complex optimizations.

As just one example, on the documentation for the /optimize compiler flag, it says

The /optimize option enables or disables optimizations performed by the compiler to make your output file smaller, faster, and more efficient.

which implies that at least some optimizations are applied by the language compiler.


However, playing around with Try Roslyn, this appears to be not true. It looks like the C# compiler does literally no optimizations at all.

Examples

Input:

bool y = true;
if (y)
    Console.WriteLine("yo");

Decompiled output:

if (true)
{
    Console.WriteLine("yo");
}

Input:

static void DoNothing() { }

static void Main(string[] args)
{
    DoNothing();
    Console.WriteLine("Hello world!");
}

Decompiled output:

private static void DoNothing()
{
}
private static void Main(string[] args)
{
    NormalProgram.DoNothing();
    Console.WriteLine("Hello world!");
}

Input:

try
{
    throw new Exception();
}
catch (Exception)
{
    Console.WriteLine("Hello world!");
}

Decompiled output:

try
{
    throw new Exception();
}
catch (Exception)
{
    Console.WriteLine("Hello world!");
}

As you can see, the C# language compiler appears to do no optimizations at all.

Is this true? If so, why does the documentation claim that /optimize will make your executable smaller?

like image 615
BlueRaja - Danny Pflughoeft Avatar asked Jan 29 '16 19:01

BlueRaja - Danny Pflughoeft


2 Answers

The only optimization I can think of that the C# compiler performs that you could actually see with a decompiler is to add an empty static constructor.

They are generally pretty uninteresting, just more compact IL. You can only see them when you look at the IL, a decent decompiler won't show it. Unoptimized code has the typical artifacts of the code-generator of a recursive-decent compiler, redundant stores that are followed immediately by a load of the same variable, branches to the next address. The optimizer knows how to eliminate them. The standard example are the NOPs that are emitted to aid in debugging, they let you set a breakpoint on a curly brace. Removed by the optimizer.

Nothing that would provide an easily observable perf improvement, although you could be lucky that the more compact IL just happens to give the jitter optimizer enough time to eliminate a critical memory store. That doesn't happen very often.

like image 84
Hans Passant Avatar answered Nov 12 '22 15:11

Hans Passant


/optimize flag says to compiler "Hey there is this code try to optimize as possible" but this doesn't mean that is the Holy Grail flag optimization.
/optimize flag does many things like remove unused variables, replace declared variables but not used (like your example), sum number in code (eg. int a = 2+2; becomes int a = 4; and many other things that you cand find here

like image 1
Tinwor Avatar answered Nov 12 '22 14:11

Tinwor