. 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是否在编译前对代码进行优化?

您发布的代码中没有任何不必要的内容。你的改变并没有真正的简化——你只是把一个伪命令式代码块变成了一个表达式。

。. 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#是一种命令式语言,这里您明确地告诉必须创建一个局部变量。您甚至可以调试变量(也是在发布模式下),这意味着它已经被创建。在您的构造中,可以在不使用局部变量的情况下调用该方法,但是您指示编译器创建一个。(未使用的变量会被编译器优化掉)。