C#性能理解以及CTS-阿里云开发者社区

开发者社区> 开发与运维> 正文

C#性能理解以及CTS

简介: 关于C#/.NET性能 在上次的例子里面,第二次执行Console.WriteLine()方法时,会完全跳过JITCompiler编译。因为第一次已经完全编译为了本地CPU指令并且返回了指令在内容里的入口地址,所以这一次会直接跳转到该方法的内存地址处执行代码,当然也会比第一次的性能要高。

关于C#/.NET性能

在上次的例子里面,第二次执行Console.WriteLine()方法时,会完全跳过JITCompiler编译。因为第一次已经完全编译为了本地CPU指令并且返回了指令在内容里的入口地址,所以这一次会直接跳转到该方法的内存地址处执行代码,当然也会比第一次的性能要高。

C#性能较之C/C++低在哪?

在托管环境中,完成代码的编译会经历两个过程:

1.源码首先被编译为IL代码

2.执行代码时,IL会经历第二次编译为本地CPU指令,这个过程需要分配更多的内存和占用CPU资源。

怎么客观地看待这个部分?

1.首先必须承认二次编译的确损害了性能,并且分配了动态内存。分配动态内存意味着当程序终止时,编译的代码会被丢弃。再次运行程序或启动两个程序的实例,JIT编译器会再次将IL编译为本地CPU指令。非托管语言(C/C++)编译器会针对具体的CPU平台编译,调用时直接执行。

2.对大多数程序而言,都会反复多次调用同一个方法。这些方法仅仅在第一次被调用时会进行二次编译,从而降低了性能。其实,可能有时在一个方法里面执行的时间比调用一个方法的时间长的多,所以即使是第一次调用需要消耗性能,可能相对于在整个方法执行的时间来说只是占用了一小部分比例。

3.微软做了很多优化的工作来保证将性能的损耗降到最低。例如,在IL编译为本地CPU指令时,编译器能够比非托管编译器知道更多关于执行环境的信息。下面列举托管编译器胜过非托管编译器的几个方面:

①JIT编译器能够判定程序是否运行在Intel Pentium 4 CPU上,并且利用Pentium 4CPU提供的任何专用的指令的优势来生成本地CPU指令。通常,非托管程序会针对最普遍CPU情况编译,从而避免使用CPU提供的专用指令来提升性能。

②JIT编译器能够确定当一个测试在机器上运行时始终为false的情形。例如:if(numberOfCPUs>1){...}.如果宿主机器只有一个CPU,那么这段代码能够让JIT编译器不生成任何CPU指令。这种情形能够让本地CPU指令得到一些微调,从而让代码更小执行更快。

认识IL

IL是基于栈的,所有IL指令通过push操作到执行栈,并且通过pop操作将结果出栈。由于IL没有提供操作寄存器的指令,很容易让人们创造出针对CLR的语言和编译器。IL指令也是无类型的。例如,IL提供的Add指令(将最后两个操作push到栈)并没有两个针对32位和64位的Add指令。当Add指令执行时,它会决定栈上操作的类型并执行适合的操作。

IL的优点

1.从底层的CPU抽象出来

2.健壮性和安全性:IL编译为本地CPU指令时,CLR执行了一个审核过程,检查高级IL代码确保它们是安全的,例如方法被调用时,参数的个数和类型是否正确;如果有返回值,那么检查方法是否有return语句等等。托管模块的元数据包含了审核过程需要的所有方法和类型信息。在Windows中,每一个进程有它自己的虚拟地址空间,这很有必要,因为你不能信任一个程序的代码。程序完全有可能从一个未验证的内存地址读取或写入,放到独立的地址空间增强了程序的健壮性和稳定性。而且这样可以在一个Windows虚拟地址空间运行多个托管程序。

3.由于进程会占用Windows的资源,进程越多占用的越多。多个托管程序可以运行在一个操作系统进程从而提升了性能。

CLR怎么实现在一个进程运行多个托管程序?

每一个托管程序在一个应用程序域执行。默认情况下,每一个托管的exe文件运行具有一个应用程序域的独立地址空间,CLR的宿主进程能够运行多个应用程序域。

了解不安全代码

微软C#编译器默认生成的是安全代码——代码是经过安全审核的。C#编译器也允许开发这写不安全的代码——直接操作内存地址以及在内存中的字节。这个功能在我们与非托管代码交互时非常有用。所有方法在包含不安全代码时,必须标记unsafe。当JIT编译器试图编译一段不安全代码时,它会检查该代码是否通过设置System.Security.Permissions.SecurityPermissionFlag’s SkipVerification标志授权。如果设置了该标志位,CLR会信任该段代码并期望直接地址和字节操作不会引起任何损害。如果没有设置会抛异常。

认识公共类型系统CTS

1.CTS:描述类型的定义及行为。

2.CTS规范声明了一个类型能够包含0个或多个成员:字段,方法,属性,事件。

3.CTS也规定类型及其成员的可访问性,下面列举出来:

Private:同一个类里面可以访问

Family:能够被派生类里面的成员访问,而不用考虑是否在同一个程序集里里面。C#里面用Protected修饰

Family and assembly:能够被在同一个程序集里面的派生类成员访问。C#及VB没有提供该访问控制修饰,IL汇编语言是可以的。

Assembly:同一个程序集可以访问。C#里面用internal修饰

Family or assembly:能够被派生类访问(不管是否在同一个程序集),也能够被同一个程序集的任何类型访问(不用考虑是否是派生类)。C#用protected internal修饰

public:没有限制。

4.CTS定义了类型继承的规则,虚方法,对象生命周期等等。

5.CTS规定所有类型必须从System.Object继承。Object是定义在System命名空间下的,是所有类型的根,这样可以保证所有的类型具有一个最小化的行为集合。System.Object定义下面几种行为:①两个实例的比较   ②获取实例的哈希代码   ③查询实例的真实类型   ④执行类型的浅复制  ⑤获取实例对象当前状态的字符串表示

注   《CLR via C#》(Jeffrey Richter著)——.NET 界的经典之作,读的过程写点笔记跟大家分享,我也推荐大家看英文版,能够直接领会原意 

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:
开发与运维
使用钉钉扫一扫加入圈子
+ 订阅

集结各类场景实战经验,助你开发运维畅行无忧

其他文章