.NET 会在编译前优化代码吗?
Does .NET optimize the code before compiling?
我在浏览我的代码时想起了 ActionScript 编译器所做的事情:它简化了 unnecessary/redundant 代码,然后编译结果。我想知道 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->machine?所有的编译器都是这样工作的吗?
您发布的代码中没有任何不必要的内容。您的更改根本没有真正进行任何简化 - 您只是将伪命令式代码块更改为单个表达式。
.NET 编译过程有点复杂 - 首先,您拥有安全的托管 C#。然后,您将拥有安全的托管 IL。最后,你得到了不安全的原生 x86 程序集(例如)。
IL 基本上是基于堆栈的。所以你的代码会变成这样:
call UploadValues
call Encoding::ASCII
call Encoding.GetString
call Console::WriteLine
(不用说,这是 严重 过度简化的 - 但它是一种相当高级的 "assembly-like" 语言)
你可以看到,即使在这里,也不再有任何局部变量,真的。它只是虚拟堆栈上的一个值。当然,不同的编译器有权以不同的方式实现这一点。例如,你可以得到这样的东西:
call UploadValues
stloc.0
ldloc.0
call Encoding::ASCII
call Encoding.GetString
call Console::WriteLine
但很明显,这确实是一个额外的步骤,而不是一个缺失的优化:)
不过,新的 Roslyn 编译器确实利用了一些旧编译器所没有的东西。例如,如果在同一个方法中多次使用同一个本地,它也有可能避免本地 - 今天的问题 .
就是一个很好的例子
但是,这里发生的任何事情都不一定会影响结果代码的性能。下一步,从 IL 到 x86 的 JIT 编译,是执行大部分优化的部分。这包括决定将 IL 中的局部变量和虚拟堆栈表示为寄存器中的值等。
至于 ActionScript,我假设从 AS3 开始也是如此。差异可能存在于旧的解释版本中,但是当它跳转到一个完整的 JITted 虚拟机时,这种差异可能就消失了。
你为什么不试试呢?在 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
}
不过,编译器可以进行优化。例如,不编译未到达的语句(在 return 或抛出异常之后)。同样,JIT 也进行优化。例如,如果您有泛型类型或方法,并且将泛型值与 null 进行比较,如果实际构造的类型是值类型,则不会对这部分代码进行 JITted。
不过您的示例有所不同。 C# 是一种命令式语言,在这里您明确告知必须创建局部变量。您甚至可以调试变量(也可以在发布模式下),这意味着它已经创建。在您的构造中,可能 调用该方法而不使用局部变量,但您指示 编译器创建一个。 (尽管编译器优化了未使用的变量)。
我在浏览我的代码时想起了 ActionScript 编译器所做的事情:它简化了 unnecessary/redundant 代码,然后编译结果。我想知道 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->machine?所有的编译器都是这样工作的吗?
您发布的代码中没有任何不必要的内容。您的更改根本没有真正进行任何简化 - 您只是将伪命令式代码块更改为单个表达式。
.NET 编译过程有点复杂 - 首先,您拥有安全的托管 C#。然后,您将拥有安全的托管 IL。最后,你得到了不安全的原生 x86 程序集(例如)。
IL 基本上是基于堆栈的。所以你的代码会变成这样:
call UploadValues
call Encoding::ASCII
call Encoding.GetString
call Console::WriteLine
(不用说,这是 严重 过度简化的 - 但它是一种相当高级的 "assembly-like" 语言)
你可以看到,即使在这里,也不再有任何局部变量,真的。它只是虚拟堆栈上的一个值。当然,不同的编译器有权以不同的方式实现这一点。例如,你可以得到这样的东西:
call UploadValues
stloc.0
ldloc.0
call Encoding::ASCII
call Encoding.GetString
call Console::WriteLine
但很明显,这确实是一个额外的步骤,而不是一个缺失的优化:)
不过,新的 Roslyn 编译器确实利用了一些旧编译器所没有的东西。例如,如果在同一个方法中多次使用同一个本地,它也有可能避免本地 - 今天的问题
但是,这里发生的任何事情都不一定会影响结果代码的性能。下一步,从 IL 到 x86 的 JIT 编译,是执行大部分优化的部分。这包括决定将 IL 中的局部变量和虚拟堆栈表示为寄存器中的值等。
至于 ActionScript,我假设从 AS3 开始也是如此。差异可能存在于旧的解释版本中,但是当它跳转到一个完整的 JITted 虚拟机时,这种差异可能就消失了。
你为什么不试试呢?在 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
}
不过,编译器可以进行优化。例如,不编译未到达的语句(在 return 或抛出异常之后)。同样,JIT 也进行优化。例如,如果您有泛型类型或方法,并且将泛型值与 null 进行比较,如果实际构造的类型是值类型,则不会对这部分代码进行 JITted。
不过您的示例有所不同。 C# 是一种命令式语言,在这里您明确告知必须创建局部变量。您甚至可以调试变量(也可以在发布模式下),这意味着它已经创建。在您的构造中,可能 调用该方法而不使用局部变量,但您指示 编译器创建一个。 (尽管编译器优化了未使用的变量)。