C#性能理解以及CTS

关于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 界的经典之作,读的过程写点笔记跟大家分享,我也推荐大家看英文版,能够直接领会原意 

作者:张雪飞
出处:https://zhangxuefei.site/p/118
版权说明:欢迎转载,但必须注明出处,并在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

发表评论

电子邮件地址不会被公开。 必填项已用*标注