1、CLR首次加载代码造成的性能损失
四、CLR执行程序集中代码介绍了CLR在首次执行一个类的时,会初始化一个内部结构,然后当目标方法被首次调用时,JITComplier函数(JIT编译器)会验证IL代码并将IL代码编译成本地CPU指令并存储到动态内存中,这意味着一旦应用程序终止,编译好的代码也会被丢弃,所以,当再次运行应用程序,或者同时启动应用程序的两个实例(使用两个不同的操作系统的进程),JIT编译器必须再次将IL编译成本机指令.对于某些应用程序,这可能会增加内存的负担.
相比之下,本机(native)应用程序的只读代码页可由应用程序正在运行的所有实例共享.
2、CLR首次加载代码造成的性能损失的严重程度
对于大多数应用程序,JIT编译造成的损失并不严重,大多数应用程序都在反复的调用相同的方法。应用程序运行期间,这些方法只会对性能造成一次性的影响.除此之外,在方法内部花费的时间可能比花在首次调用方法,JIT编译和优化IL所花费的时间更多.
3、CLR加载代码时JIT编译器进行的代码优化
CLR首次加载程序集代码时,JIT将IL编译成本地代码时,会对其进行代码优化,这类似与非托管C++编译器的后端所做的事情.这可能也会花费加多的时间生成优化代码.
(1)、编译器开关/optimize和/debug对代码的影响
/optimize开关:
C#编译器生成的未优化IL代码,将包含许多NOP(空操作)指令,还将包含许多跳转到下一行代码的分支指令.Visual Stdio利用这些指令在调试提供"编辑并继续"功能.另外,利用这些额外的指令,还可在控制流程指令(比如for,while,do,if,else,try,catch和finally)上设置断点,使代码更容易调试.相反,如果生成优化的IL代码,C#编译器会删除多余的NOP和分支指令,而在控制流程被优化之后,代码就不能再调试器中进行单步调试了。代码若在调试器中执行,一些函数求值可能无法进行.但是,优化过的IL代码变得更小,结果EXE/DLL文件也更小.
/debug(+/full/pdbonly)开关:
编译器会生成Program Database(PDB)文件,PDB文件帮助调试器查找局部变量并将IL指令映射到源代码.
/debug:full开关告诉JIT编译器你打算调试程序集,那么JIT编译器会记录每条IL指令所生成的本机代码.这样依赖,就可利用Visual Studio的“即时”调试功能,将调试器连接到正在运行的进程,并方便地对源代码进行调试.
不打开/debug:full开关,JIT编译器默认不记录IL与本机代码的联系,这使JIT编译器运行的稍快,占用内存也稍少.如果进程用Visual Stdio的“即时”调试功能,会强迫JIT编译器记录IL与本机代码的联系(无论编译器的开关设置是什么)除非在Visual Stdio中关闭了"在模块加载时取消JIT优化(权限托管)"操作步骤如下:
工具-选项-调试
(2)、Visual Stdio中新建C#项目时,编译器开关的默认设置
通过VS新建项目时,项目的调试(Debug)配置的是/optimize-和debug:full开关(IL代码和本地代码均未优化-方便调试),而"发布"(Release)配置置顶的是/optimize+和/debug:pdbpnly开关(IL代码和本地代码均优化-文件变小,占用内存小)