. net是否在编译前对代码进行优化?
本文关键字:优化 代码 是否 编译 net | 更新日期: 2023-09-27 18:14:24
我正在浏览我的代码,我想起了ActionScript编译器所做的一些事情:它简化了不必要/冗余的代码,然后编译结果。我想知道c#是否有同样的过程。
如果我有我的理解正确,并假设这将是有效的ActionScript(我知道它不是),编译器将采取这个:
byte[] result = ServerWC.UploadValues("http://localhost", srvNvc);
Console.WriteLine(Encoding.ASCII.GetString(result));
并简化为:
Console.WriteLine(Encoding.ASCII.GetString(ServerWC.UploadValues("http://localhost", srvNvc)));
在编译之前,。哪个编译器,c# ->IL,还是IL->机器,进行优化?所有的编译器都是这样工作的吗?
您发布的代码中没有任何不必要的内容。你的改变并没有真正的简化——你只是把一个伪命令式代码块变成了一个表达式。
。. NET编译过程有点复杂——首先,你有一个安全的、托管的c#。然后是安全的、托管的IL。最后是不安全的、本地的x86程序集(例如)。
IL基本上是基于堆栈的。所以你的代码会变成这样:
call UploadValues
call Encoding::ASCII
call Encoding.GetString
call Console::WriteLine
(不用说,这是非常的过度简化-但它是一种相当高级的"类汇编"语言)
你可以看到,即使在这里,也不再有任何局部变量了。它只是虚拟堆栈上的一个值。当然,不同的编译器有权利以不同的方式实现这一点。例如,您可以得到这样的内容:
call UploadValues
stloc.0
ldloc.0
call Encoding::ASCII
call Encoding.GetString
call Console::WriteLine
但很明显,这确实是一个额外的步骤,而不是一个缺失的优化:)
新的Roslyn编译器确实利用了一些旧的编译器没有的东西。例如,如果在同一方法中多次使用相同的局部,它也可能避免局部-今天的问题VS2015 stloc就是一个很好的例子。0和lloc。0从编译指令中删除。
但是,这里发生的任何事情都不一定影响结果代码的性能。下一步,从IL到x86的JIT编译,是完成大部分优化的部分。这包括决定IL中的本地和虚拟堆栈将被表示为,例如,寄存器中的值。
对于ActionScript,我认为同样适用于AS3。这种差异可能在旧的解释版本中存在,但是当它跳到一个完全成熟的jit虚拟机时,这种差异可能就消失了。
你为什么不试试呢?在Reflector中,我可以看到这里没有执行这样的优化:
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)));
}
发布版本的IL代码:
.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
}
但是,编译器可以进行优化。例如,未到达语句(在返回或抛出异常之后)不会被编译。类似地,JIT也会进行优化。例如,如果您有一个泛型类型或方法,并且您将泛型值与null进行比较,如果实际构造类型是值类型,则这部分代码将不会被jit。
你的例子是不同的。c#是一种命令式语言,这里您明确地告诉必须创建一个局部变量。您甚至可以调试变量(也是在发布模式下),这意味着它已经被创建。在您的构造中,可以在不使用局部变量的情况下调用该方法,但是您指示编译器创建一个。(未使用的变量会被编译器优化掉)。