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.
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?
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.
/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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With