使用C#编写程序,给最终用户的程序,是须要使用release配置的,而release配置和debug配置,有一个关键区别,就是release的编译器优化默认是启用的。
优化代码开关即optimize开关,和debug开关一块儿,有如下几种组合。
html
在Visual Sutdio中新建一个C#项目时,
项目的“调试”(Debug)配置的是/optimize-和/debug:full开关,
而“发布”(Release)配置指定的是/optimize+和/debug:pdbonly开关c#
optimize-/+决定了编译器是否优化代码,optimize-就是不优化了,可是一般,有一些基本的“优化”工做,不管是否指定optimize+,都会执行。ide
该项功能主要用于动态语义分析,帮助咱们更好地编写代码。函数
在写程序的时候,有时能看见代码下面划了一道红波浪线,那就是编译器动态检查。常量计算,就是这样,编译器会计算常量,帮助判断其余错误。
工具
若是swtich写了两个以上的相同条件,或者分支明显没法访问到,都会弹出提示。
性能
很少说明,直接看图。
优化
很少说,看图。
spa
使用变量参与计算,随便写一个算式,就能够绕过一些检查,虽然咱们看来是明显有问题的。
.net
首先须要了解c#代码编译的过程,以下图:
图片来自http://www.cnblogs.com/rush/p/3155665.htmldebug
C# compiler将C#代码生成IL代码的就是所谓的编译器优化。先说重点。
.NET的JIT机制,主要优化在JIT中完成,编译器optimize只作一点简单的工做。(划重点)
探究一下到底干了点啥吧,如下是使用到的工具。
Tools:
Visual studio 2017 community targeting .net core 2.0
IL DASM(vs自带)
按照优化的类型进行了简单的分类。
using System; using System.Threading.Tasks; namespace CompileOpt { class Program { static void Main(string[] args) { int x = 3; Console.WriteLine("sg"); } } }
未优化的时候
.method private hidebysig static void Main(string[] args) cil managed { .entrypoint // 代码大小 15 (0xf) .maxstack 1 .locals init (int32 V_0) IL_0000: nop IL_0001: ldc.i4.3 IL_0002: stloc.0 IL_0003: ldstr "sg" IL_0008: call void [System.Console]System.Console::WriteLine(string) IL_000d: nop IL_000e: ret } // end of method Program::Main
使用优化开关优化以后:
.method private hidebysig static void Main(string[] args) cil managed { .entrypoint // 代码大小 11 (0xb) .maxstack 8 IL_0000: ldstr "sg" IL_0005: call void [System.Console]System.Console::WriteLine(string) IL_000a: ret } // end of method Program::Main
.locals init (int32 V_0)
消失了(局部变量,类型为int32)
ldc.i4.3
(将3推送到堆栈上)和stloc.0
(将值从堆栈弹出到局部变量 0)也消失了。
因此,整个没有使用的变量,在设置为优化的时候,就直接消失了,就像历来没有写过同样。
using System; using System.Threading.Tasks; namespace CompileOpt { class Program { static void Main(string[] args) { try { } catch (Exception) { Console.WriteLine(DateTime.Now); } try { } catch (Exception) { Console.WriteLine(DateTime.Now); } finally { Console.WriteLine(DateTime.Now); } } } }
未优化
.method private hidebysig static void Main(string[] args) cil managed { .entrypoint // 代码大小 74 (0x4a) .maxstack 1 IL_0000: nop .try { IL_0001: nop IL_0002: nop IL_0003: leave.s IL_001a } // end .try catch [System.Runtime]System.Exception { IL_0005: pop IL_0006: nop IL_0007: call valuetype [System.Runtime]System.DateTime [System.Runtime]System.DateTime::get_Now() IL_000c: box [System.Runtime]System.DateTime IL_0011: call void [System.Console]System.Console::WriteLine(object) IL_0016: nop IL_0017: nop IL_0018: leave.s IL_001a } // end handler IL_001a: nop .try { .try { IL_001b: nop IL_001c: nop IL_001d: leave.s IL_0034 } // end .try catch [System.Runtime]System.Exception { IL_001f: pop IL_0020: nop IL_0021: call valuetype [System.Runtime]System.DateTime [System.Runtime]System.DateTime::get_Now() IL_0026: box [System.Runtime]System.DateTime IL_002b: call void [System.Console]System.Console::WriteLine(object) IL_0030: nop IL_0031: nop IL_0032: leave.s IL_0034 } // end handler IL_0034: leave.s IL_0049 } // end .try finally { IL_0036: nop IL_0037: call valuetype [System.Runtime]System.DateTime [System.Runtime]System.DateTime::get_Now() IL_003c: box [System.Runtime]System.DateTime IL_0041: call void [System.Console]System.Console::WriteLine(object) IL_0046: nop IL_0047: nop IL_0048: endfinally } // end handler IL_0049: ret } // end of method Program::Main
优化开关开启:
.method private hidebysig static void Main(string[] args) cil managed { .entrypoint // 代码大小 19 (0x13) .maxstack 1 .try { IL_0000: leave.s IL_0012 } // end .try finally { IL_0002: call valuetype [System.Runtime]System.DateTime [System.Runtime]System.DateTime::get_Now() IL_0007: box [System.Runtime]System.DateTime IL_000c: call void [System.Console]System.Console::WriteLine(object) IL_0011: endfinally } // end handler IL_0012: ret } // end of method Program::Main
很明显能够看到,空的try catch直接消失了,可是空的try catch finally代码是不会消失的,可是也不会直接调用finally内的代码(即仍是会生成try代码段)。
using System; using System.Threading.Tasks; namespace CompileOpt { class Program { static void Main(string[] args) { int x = 3; if (x == 3) goto LABEL1; else goto LABEL2; LABEL2: return; LABEL1: return; } } }
未优化的状况下:
.method private hidebysig static void Main(string[] args) cil managed { .entrypoint // 代码大小 22 (0x16) .maxstack 2 .locals init (int32 V_0, bool V_1) IL_0000: nop IL_0001: ldc.i4.3 IL_0002: stloc.0 IL_0003: ldloc.0 IL_0004: ldc.i4.3 IL_0005: ceq IL_0007: stloc.1 IL_0008: ldloc.1 IL_0009: brfalse.s IL_000d IL_000b: br.s IL_0012 IL_000d: br.s IL_000f IL_000f: nop IL_0010: br.s IL_0015 IL_0012: nop IL_0013: br.s IL_0015 IL_0015: ret } // end of method Program::Main
优化:
.method private hidebysig static void Main(string[] args) cil managed { .entrypoint // 代码大小 5 (0x5) .maxstack 8 IL_0000: ldc.i4.3 IL_0001: ldc.i4.3 IL_0002: pop IL_0003: pop IL_0004: ret } // end of method Program::Main
优化的状况下,一些分支会被简化,使得调用更加简洁。
using System; using System.Threading.Tasks; namespace CompileOpt { class Program { static void Main(string[] args) { goto LABEL1; LABEL2: Console.WriteLine("234"); Console.WriteLine("123"); return; LABEL1: goto LABEL2; } } }
未优化:
.method private hidebysig static void Main(string[] args) cil managed { .entrypoint // 代码大小 32 (0x20) .maxstack 8 IL_0000: nop IL_0001: br.s IL_001c IL_0003: nop IL_0004: ldstr "234" IL_0009: call void [System.Console]System.Console::WriteLine(string) IL_000e: nop IL_000f: ldstr "123" IL_0014: call void [System.Console]System.Console::WriteLine(string) IL_0019: nop IL_001a: br.s IL_001f IL_001c: nop IL_001d: br.s IL_0003 IL_001f: ret } // end of method Program::Main
优化后:
.method private hidebysig static void Main(string[] args) cil managed { .entrypoint // 代码大小 21 (0x15) .maxstack 8 IL_0000: ldstr "234" IL_0005: call void [System.Console]System.Console::WriteLine(string) IL_000a: ldstr "123" IL_000f: call void [System.Console]System.Console::WriteLine(string) IL_0014: ret } // end of method Program::Main
一些多层的标签跳转会获得简化,优化器就是人狠话很少。
using System; using System.Threading.Tasks; namespace CompileOpt { class Program { static void Main(string[] args) { for (int i = 0; i < 3; i++) { Console.WriteLine(i); } for (int i = 0; i < 3; i++) { Console.WriteLine(i + 1); } } } }
只显示最关键的变量声明部分,未优化的代码以下:
.method private hidebysig static void Main(string[] args) cil managed { .entrypoint // 代码大小 54 (0x36) .maxstack 2 .locals init (int32 V_0, bool V_1, int32 V_2, bool V_3) IL_0000: nop
优化后:
.method private hidebysig static void Main(string[] args) cil managed { .entrypoint // 代码大小 39 (0x27) .maxstack 2 .locals init (int32 V_0, int32 V_1) IL_0000: ldc.i4.0
很显然,中间的bool型比较变量消失了。
编译器版本不一样,对应的优化手段也不尽相同,以上只列出了一些,应该还有一些没有讲到的,欢迎补充。
在.NET的编译模型中没有连接器。可是有一个源代码编译器(C# compiler)和即时编译器(JIT compiler),源代码编译器只进行很小的一部分优化。好比它不会执行函数内联和循环优化。
从优化能力上来说RyuJIT和Visual C++有什么不一样呢?由于RyuJIT是在运行时完成其工做的,因此它能够完成一些Visual C++不能完成的工做。好比在运行时,RyuJIT可能会断定,在此次程序的运行中一个if语句的条件永远不会为true,因此就能够将它移除。RyuJIT也能够利用他所运行的处理器的能力。好比若是处理器支持SSE4.1,即时编译器就会只写出sumOfCubes函数的SSE4.1指令,让生成打的代码更加紧凑。可是它不能花更多的时间来优化代码,由于即时编译所花的时间会影响到程序的性能。
在当前控制托管代码的能力是颇有限的。C#和VB编译器只容许使用/optimize编译器开关打开或者关闭优化功能。为了控制即时编译优化,你能够在方法上使用System.Runtime.CompilerServices.MethodImpl属性和MethodImplOptions中指定的选项。NoOptimization选项能够关闭优化,NoInlining阻止方法被内联,AggressiveInlining (.NET 4.5)选项推荐(不只仅是提示)即时编译器将一个方法内联。
话说整点这个东西有点什么用呢?
要说是有助于更好理解.NET的运行机制会不会有人打我...
说点实际的,有的童鞋在写延时程序时,timer.Interval = 10 * 60 * 1000
,做为强迫症患者,生怕这么写很差,影响程序执行。可是,这种写法彻底不会对程序的执行有任何影响,我认为还应该推荐,由于增长了程序的可读性,上面的代码段就是简单的10分钟,一看就明白,要是算出来反而可读性差。另外,分支简化也有助于咱们专心依照业务逻辑去编写代码,而不须要过多考虑代码的分支问题。其余的用途各位看官自行发挥啦。