C#编译器优化那点事

使用C#编写程序,给最终用户的程序,是需要使用release配置的,而release配置和debug配置,有一个关键区别,就是release的编译器优化默认是启用的。
优化代码开关即optimize开关,和debug开关一起,有以下几种组合。
640?wx_fmt=png&wxfrom=5&wx_lazy=1

在Visual Sutdio中新建一个C#项目时,
项目的“调试”(Debug)配置的是/optimize-和/debug:full开关,
而“发布”(Release)配置指定的是/optimize+和/debug:pdbonly开关

optimize-/+决定了编译器是否优化代码,optimize-就是不优化了,但是通常,有一些基本的“优化”工作,无论是否指定optimize+,都会执行。

optimize- and optimize+

该项功能主要用于动态语义分析,帮助我们更好地编写代码。

  • 常量计算

    在写程序的时候,有时能看见代码下面划了一道红波浪线,那就是编译器动态检查。常量计算,就是这样,编译器会计算常量,帮助判断其他错误。
    640?wx_fmt=png

  • 简单分支检查

    如果swtich写了两个以上的相同条件,或者分支明显无法访问到,都会弹出提示。
    640?wx_fmt=png

  • 未使用变量

    不多说明,直接看图。
    640?wx_fmt=png

  • 使用未赋值变量

    不多说,看图。
    640?wx_fmt=png

局限

使用变量参与计算,随便写一个算式,就可以绕过一些检查,虽然我们看来是明显有问题的。
640?wx_fmt=png

optimize+ only

首先需要了解c#代码编译的过程,如下图:
640?wx_fmt=png
图片来自http://www.cnblogs.com/rush/p/3155665.html

C# compiler将C#代码生成IL代码的就是所谓的编译器优化。先说重点。
.NET的JIT机制,主要优化在JIT中完成,编译器optimize只做一点简单的工作。(划重点)

探究一下到底干了点啥吧,以下是使用到的工具。

Tools:
Visual studio 2017 community targeting .net core 2.0
IL DASM(vs自带)

使用IL DASM可以查看编译器生成的IL代码,这样就能看到优化的作用了。IL代码的用途与机制不是本文的重点,不明白的同学可以先去看看《C# via CLR》(好书推荐)。

按照优化的类型进行了简单的分类。

  • 从未使用变量

    代码如下:

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  8IL_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)也消失了。
所以,整个没有使用的变量,在设置为优化的时候,就直接消失了,就像从来没有写过一样。

  • 空try catch语句

    代码如下:

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 .trycatch [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 .trycatch [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 .tryfinally{    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 .tryfinally{IL_0002:  call       valuetype [System.Runtime]System.DateTime [System.Runtime]System.DateTime::get_Now()IL_0007:  box        [System.Runtime]System.DateTimeIL_000c:  call       void [System.Console]System.Console::WriteLine(object)IL_0011:  endfinally}  // end handlerIL_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;          
          elsegoto 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  8IL_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型比较变量消失了。

  • 空指令删除

    看第一个例子,很明显,代码中没有了nop字段,程序更加紧凑了。

编译器版本不同,对应的优化手段也不尽相同,以上只列出了一些,应该还有一些没有讲到的,欢迎补充。

延伸阅读:.NET中的优化(转载自http://blog.jobbole.com/84712/)

在.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.Compiler­Services.MethodImpl属性和MethodImplOptions中指定的选项。NoOptimization选项可以关闭优化,NoInlining阻止方法被内联,AggressiveInlining (.NET 4.5)选项推荐(不仅仅是提示)即时编译器将一个方法内联。

结语

话说整点这个东西有点什么用呢?
要说是有助于更好理解.NET的运行机制会不会有人打我...
说点实际的,有的童鞋在写延时程序时,timer.Interval = 10 * 60 * 1000,作为强迫症患者,生怕这么写不好,影响程序执行。但是,这种写法完全不会对程序的执行有任何影响,我认为还应该推荐,因为增加了程序的可读性,上面的代码段就是简单的10分钟,一看就明白,要是算出来反而可读性差。另外,分支简化也有助于我们专心依照业务逻辑去编写代码,而不需要过多考虑代码的分支问题。其他的用途各位看官自行发挥啦。

原文地址: http://www.cnblogs.com/podolski/p/8987595.html


.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com

640?wx_fmt=jpeg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/321406.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何在Visual Studio 2017中使用C# 7+语法

前言之前不知看过哪位前辈的博文有点印象C# 7控制台开始支持执行异步方法&#xff0c;然后闲来无事&#xff0c;搞着&#xff0c;搞着没搞出来&#xff0c;然后就写了这篇博文&#xff0c;不喜勿喷&#xff0c;或许对您有帮助。在Visual Studio 2017配置支持C# 7语法心想都VS20…

11、mybatis的功能架构分析

1、Mybatis功能架构 1&#xff09;API接口层&#xff1a;提供给外部使用的接口API&#xff0c;开发人员通过这些本地API来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。 2&#xff09;数据处理层&#xff1a;负责具体的SQL查找、SQL解析、SQL执…

【动态规划】方格取数 (ssl 1010)

方格取数方格取数方格取数 Description 设有N*N的方格图(N<10,我们将其中的某些方格中填入正整数,而其他的方格中则放入数字0。如下图所示&#xff08;见样例&#xff09;&#xff1a; 某人从图的左上角的A 点出发&#xff0c;可以向下行走&#xff0c;也可以向右走&#…

ASP.NET Core 使用UrlFirewall对请求进行过滤

一. 前言UrlFirewall 是一个开源、轻便的对http请求进行过滤的中间件&#xff0c;可使用在webapi或者网关&#xff08;比如Ocelot&#xff09;,由我本人编写&#xff0c;并且开源在github&#xff1a;https://github.com/stulzq/UrlFirewall 欢迎star.二.UrlFirewall 介绍UrlFi…

1、设计模式和原则总述

目录 一、什么是设计模式&#xff1f;有什么用&#xff1f; 二、设计模式 三、设计原则 一、什么是设计模式&#xff1f;有什么用&#xff1f; 设计模式是一套代码设计的经验总结&#xff0c;使用设计模式可以提高代码的重用性、可靠性&#xff0c;提交代码内聚&#xff0c;降…

Visual Studio 15.7预览版4改进Git、C++支持

对于即将到来的Visual Studio 2017 15.7&#xff0c;微软已经发布了多个新的预览版本。这些版本的变更很有限&#xff0c;似乎离正式发布不远了。通常&#xff0c;变更的涉及面很广——因此&#xff0c;不管是用什么语言&#xff0c;开发人员都可以看到一些好处。第一次&#x…

P2827-蚯蚓【队列】

前言 早年一直拿堆过不了&#xff0c;结果发现要用队列 正题 题目链接:https://www.luogu.com.cn/problem/P2827 题目大意 有nnn条蚯蚓&#xff0c;每次选取最长的一条&#xff0c;切成⌊x∗p⌋\lfloor x*p\rfloor⌊x∗p⌋和x−⌊x∗p⌋x-\lfloor x*p\rfloorx−⌊x∗p⌋的两…

.net core DI 注册 Lazy 类型

当我们在 .net core (2.1) 中运行如下代码注入 Lazy<T> 变量的时候&#xff1a;public AccountService(Lazy<IHttpContextAccessor> httpContextAccessor) { }可能会遇到这样的错误提示&#xff1a;InvalidOperationException: Unable to resolve service for type…

【深搜】 棋盘 【NOIp普及组 2017 第三题】 (luogu 3956/ssl 2851)

棋盘棋盘棋盘 题目大意&#xff1a; 有一个M*M的棋盘&#xff0c;要从&#xff08;1&#xff0c;1&#xff09;到&#xff08;m,m&#xff09;&#xff0c;中间有n个有颜色的格子&#xff0c;只能踩在有颜色的格子上&#xff0c;跳到不同颜色的格子要花费1元&#xff0c;可以将…

别跟我谈EF抵抗并发,敢问你到底会不会用EntityFramework

前言一直以来写的博文都是比较温婉型的博文&#xff0c;今天这篇博文算是一篇批判性博文&#xff0c;有问题欢迎探讨&#xff0c;如标题&#xff0c;你到底会不会用EntityFramework啊。你到底会不会用EntityFramework啊面试过三年至六年的同行&#xff0c;作为过面试者到如今作…

【动态规划】 多米诺骨牌 (ssl 1632/luogu 1282)

多米诺骨牌多米诺骨牌多米诺骨牌 Description Input 输入文件的第一行是一个正整数n(1≤n≤1000)&#xff0c;表示多米诺骨牌数。接下来的n行表示n个多米诺骨牌的点数。每行有两个用空格隔开的正整数&#xff0c;表示多米诺骨牌上下方块中的点数a和b&#xff0c;且1≤a&#…

为什么 web 开发人员需要迁移到. NET Core, 并使用 ASP.NET Core MVC 构建 web 和 API

2018 .NET开发者调查报告: .NET Core 是怎么样的状态&#xff0c;这里我们看到了还有非常多的.net开发人员还在观望&#xff0c;本文给大家一个建议。这仅代表我的个人意见, 我有充分的理由推荐.net 程序员使用. net core而不是. net Framework。有些人可能不同意我的观点, 但是…

【动态规划】 摆花 【NOIp普及组 2012 第三题】 (ssl 2360/luogu 1077)

摆花摆花摆花 题目大意 有n种花&#xff0c;每种花有ai支&#xff0c;取m支&#xff0c;有多少种取法&#xff1f;&#xff08;同一种花取第1&#xff0c;第3支和取第1&#xff0c;第2支算一种取法&#xff09; 解题方法&#xff1a; 用f[i][j]来表示前i种选j支的方案数&…

使用SonarCloud对.NET Core项目进行静态代码分析

本文将介绍如何使用SonarCloud进行.NET Core项目的静态代码分析。SonarCloud是SonarQube提供的基于云的版本&#xff0c;特别针对于开源项目是免费的。首先&#xff0c;在sonarcloud.io创建一个账号&#xff0c;你可以使用Github/BitBucket/Microsoft Live账户进行注册&#xf…

Build 2018,给你带来全新的开发者体验, .NET Core 3.0带来桌面支持

Build 2018 主旨演讲的主题是 Azure 云和 AI、物联网、AR等技术&#xff0c;以及开发者相关内容的宣布。在今天的Build大会上&#xff0c;微软宣布目前已有超过7亿台设备运行Windows 10系统。去年这个数字为5亿。Office 365目前每个月有1.35亿活跃的商业用户&#xff0c;去年为…

《你必须掌握的Entity Framework 6.x与Core 2.0》书籍出版

前言到目前为止写过刚好两百来篇博客&#xff0c;看过我博客的读者应该大概知道我每一篇博客都沿袭着一贯的套路&#xff0c;从前言到话题最终到总结&#xff0c;本文依然是一如既往的套路&#xff0c;但是不是介绍技术&#xff0c;也可说是介绍技术&#xff0c;不过是介绍书中…

.NET Core玩转机器学习

ML.NET 专门为.NET开发者提供了一套跨平台的开源的机器学习框架。ML.NET支持.NET开发者不需要过度专业的机器学习开发经验&#xff0c;就能轻松地训练自己的模型&#xff0c;并且嵌入到自己的应用中。一切尽在.NET之中。ML.NET早期是由Microsoft Research开发&#xff0c;近十年…

【记忆化搜索】【线性化DP】滑雪 (ssl 1202/luogu 1434/pku 1088)

滑雪滑雪滑雪 ssl 1202 luogu 1434 pku 1088 题目大意&#xff1a; 有一个N*M的矩阵&#xff0c;每个位置都有一个数&#xff0c;可以从大的数走向小的数&#xff0c;问可走的路最长是多少 原题 Michael喜欢滑雪百这并不奇怪&#xff0c; 因为滑雪的确很刺激。可是为了获…

微软Build 2018展示Visual Studio功能:跨系统云编程

5 月 8 日凌晨消息&#xff0c;微软 Build 2018 开发者大会在今天正式来开帷幕。很明显整场发布会被分为了上下两个部分&#xff0c;上半场如果说是聊一些技术实现和愿景&#xff0c;那下半场就应该说是实战说明了。跨平台云端编程了解下微软在现场展示了 Visual Studio&#x…

利用Skywalking-netcore监控你的应用性能

SkywalkingSkyWalking开源项目由吴晟于2015年创建&#xff0c;同年10月在GitHub上作为个人项目开源。SkyWalking项目的核心目标&#xff0c;是针对微服务、Cloud Native、容器化架构&#xff0c;提供应用性能监控&#xff08;APM&#xff09;和分布式调用链追踪能力。2017年11月…