Reputation: 554
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?
Upvotes: 2
Views: 743
Reputation: 18023
Why don't you just try it? In Reflector I can see that such optimization is not performed here:
private void m1()
{
byte[] result = ServerWC.UploadValues("http://localhost", this.srvNvc);
Console.WriteLine(Encoding.ASCII.GetString(result));
}
private void m2()
{
Console.WriteLine(Encoding.ASCII.GetString(ServerWC.UploadValues("http://localhost", this.srvNvc)));
}
The IL code of the Release build:
.method private hidebysig instance void m1() cil managed
{
.maxstack 2
.locals init (
[0] uint8[] result)
L_0000: ldstr "http://localhost"
L_0005: ldarg.0
L_0006: ldfld object Test.Program::srvNvc
L_000b: call uint8[] Test.ServerWC::UploadValues(string, object)
L_0010: stloc.0
L_0011: call class [mscorlib]System.Text.Encoding [mscorlib]System.Text.Encoding::get_ASCII()
L_0016: ldloc.0
L_0017: callvirt instance string [mscorlib]System.Text.Encoding::GetString(uint8[])
L_001c: call void [mscorlib]System.Console::WriteLine(string)
L_0021: ret
}
.method private hidebysig instance void m2() cil managed
{
.maxstack 8
L_0000: call class [mscorlib]System.Text.Encoding [mscorlib]System.Text.Encoding::get_ASCII()
L_0005: ldstr "http://localhost"
L_000a: ldarg.0
L_000b: ldfld object Test.Program::srvNvc
L_0010: call uint8[] Test.ServerWC::UploadValues(string, object)
L_0015: callvirt instance string [mscorlib]System.Text.Encoding::GetString(uint8[])
L_001a: call void [mscorlib]System.Console::WriteLine(string)
L_001f: ret
}
The compiler can make optimizations, though. For example, non-reached statements (after a return or throwing an exception) are not compiled. Similarly, the JIT does optimizations as well. For example, if you have a generic type or method, and you compare the generic value to null, this part of code will not be JITted if the actual constructed type is a value type.
Your example is different though. C# is an imperative language, and here you told explicitly that a local variable must be created. You can even debug the variable (in release mode, too), which means, it has been created. In your construction it is possible to call the method without using a local variable, but you instructed the compiler to create one. (A non-used variable is optimized away by the compiler though).
Upvotes: 1
Reputation: 63772
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.
Upvotes: 1