.“.NET研究”Net Discovery系列之-深入理解平台机制与性能影响 (中)

简介:   上一篇文章中Aicken为大家介绍了.Net平台的垃圾回收机制与其对性能的影响,这一篇中将继续为大家介绍.Net平台的另一批黑马—JIT。有关JIT的机制分析  ● 机制分析以C#为例,在C#代码运行前,一般会经过两次编译,第一阶段是C#代码向MSIL的编译,第二阶段是IL向本地代码的编译。

  上一篇文章中Aicken为大家介绍了.Net平台的垃圾回收机制与其对性能的影响,这一篇中将继续为大家介绍.Net平台的另一批黑马—JIT。有关JIT的机制分析

  ● 机制分析以C#为例,在C#代码运行前,一般会经过两次编译,第一阶段是C#代码向MSIL的编译,第二阶段是IL向本地代码的编译。第一阶段的编译成果是生成托管模块,第二阶段的编译成果是生成本地代码以供运行,从这里各位同学可以看出,第一阶段生成的MSIL是不能直接运行的。必须指出的是JIT在第一次编译IL后,会修改对应方法相应的内存地址入口,下一次需要执行这个方法时,CLR会直接访问对应的内存地址,而不会经过JIT了。

  以Load()方法为例,假如Load()方法中调用了两次同类型中的方法:

 
 
Void Load(){A.a1( " First " );A.a1( " Second " );}
  
static class A{Public void a1( 上海企业网站制作tyle="color: #0000ff;">string str){}
  Public
void a2( string str){}
  Public
void a3( string str){}}

  运行时,操作系统会根据托管模块中各种头信息,装载相应的运行时框架,Load()被加载,由于是第一次加载,这会触发对Load()的即时编译,JIT会检测Load()中引用的所有类型,并结合元数据遍历这些类型中定义的所有方法实现,并用一个特殊的HashTable(仅用于理解)储存这些类型方法与其对应的入口地址(在未被JIT前,这个入口地址为一个预编译代理(PreJitStub),这个代理负责触发JIT编译),根据这些地址,就可以找到对应的方法实现。在初始化时,HashTable中各个方法指向的并不是对应的内存入口地址,而是一个JIT预编译代理,这个函数负责将方法编译为本地代码。注意,这里JIT还没有进行编译,只是建立了方法表!

图2方法表、方法描述、预编译代理关系

  图2中所示的MS核心引擎指的是一个叫做MSCorEE的DLL,即Microsoft .NET Runtime Execution Engine,它是一个桥接DLL,连同mscorwks.dll主要完成以下工作:

  1.查找程序集中包含的对应类型清单,并调用元数据遍历出包含的方法。

  2.结合元数据获得这个方法的IL。

  3.分配内存。

  4.编译IL本地代码,并保存在第3步所分配的内存中。

  5.将类型表(就是指上文中提到的HashTable)中方法地址修改为第3步所分配的内存地址。

  6.跳转至本地代码中执行。所以随着程序的运行时间增加,越来越多的方法的IL被编译为本地代码,JIT的调用次数也会不断减少。下面借助WinDbg来证实以上的说法,加载WinDbg的过程略。以下测试源代码可以从这里下载http://files.cnblogs.com/isline/IsLine.JITTester.rar

 
 
namespace JITTester{
public partial class Form1 :
Form{
public Form1()
{
InitializeComponent();
}
private void Form1_Load( object sender, EventArgs e){}
private void GO_Click( object sender, EventArgs e)
{
new A().a1();lb_msg.Text = " 调用完毕! " ;}}
class A{ 上海徐汇企业网站制作>public void a1() { }
public C a2 = new C();}
class B{ public void b1() { }
public 上海闵行企业网站制作"color: #0000ff;">void b2() { }}
上海闵行企业网站设计与制作
class C{ public void c1() { }
public void c2() { }}}

使用name2ee命令遍历所有已加载模块,如下图:

图3 查看类型信息

  回车后注意高亮区域的信息:

图4 JIT前A类型的信息

  高亮区域显示的是,这说明虽然运行和程序,但未点击按钮时,A类型未被JIT,因为它还没有入口地址。这一点体现了即时、按需编译的思想。同样,!name2ee *!JITTester.B和!name2ee *!JITTester.C命令会得到同样的结果。好,现在继续,Detach Debuggee进程,并回到程序中点击GO按钮

图5 点击按钮

  然后重新附加进程,这时程序已经调用了new A().a1()方法,并重新执行令!name2ee *!JITTester.A ,注意高亮部分

图6 JIT后A类型的信息

  和图4中的信息比较,图6中的方法表地址已经变为JIT后的内存地址,这时图2中的Stub槽将被一条强制跳转语句替换,跳转目标与该地址有关。这一点说明JIT在大多情况下,只编译一次代码。同样命令查看B类型:

图7 JIT后B类型的信息

  该类型未被调用,所以还未被上海企业网站设计与制作JIT。C类型:

图8 JIT后C类型的信息

  由于实例化A类型时和C类型相关,所以C类型已经JIT了。这就是一个类型被JIT的全部过程。

  ● 性能影响分析通过以上的分析,大家已经能够了解,即时编译这个过程是在运行时发生的,这会不会对性能产生影响呢?事实上答案是虽然是肯定的,但这种开销物有所值,并且如上所说的,JIT在第一次编译IL后,会修改对应方法相应的内存地址入口(绕口啊~~),下一次需要执行这个方法时,CLR会直接访问对应的内存地址,而不会经过JIT了。

  1.JIT所造成的性能开销并不显著。

  2.JIT遵循计算机体系理论中两个经典理论:局部性原理与8020原则。局部性原理指出,程序总是趋向于使用最近使用过的数据和指令,这包括空间的和时间的,将局部性原理引申可以得出,程序总是趋向于使用最近使用过的数据和指令,以及这些正在使用的数据和指令临近的数据和指令(凭印象写的,但不曲解原意);而8020原则指出,系统大多数时间总是花费80%的时间去执行那20%的代码。根据这两个原则,JIT在运行时会实时的向前、后优化代码,这样的工作只有在运行时才可以做到。

  3.JIT只编译需要的那一段代码,而不是全部,这样节约了不必要的内存开销。

  4.JIT会根据运行时环境,即时的优化IL代码,即同样的IL代码运行在不同CPU上,JIT编译出的本地代码是不同的,这些不同代码面向自己的CPU做出了优化。

  5.JIT会对代码的运行情况进行检测,并对那些特殊的代码经行重新编译,在运行过程中不断优化。此外你可以利用NGen.exe创建托管程序集的本机映像,运行该程序集时,就会自动使用该本机映像而不是JIT它们。这听起来似乎很美妙,但是你必须做好以下准备:

  1.当FrameWork版本、CPU类型、操作系统版本发生变化时,.Net会恢复JIT机制。

  2.NGe上海网站建设n.exe工具并不能避免发布IL,事实上,即使使用NGen.exe工具,CLR依然会使用到元数据和IL。

  3.忽略了局部性原理(上一节中提到的),系统会加载整个映像文件到内存中,并很可能重定位文件,修正内存地址引用。

  4.NGen.exe生成的代码无法在运行时进行优化,无法直接访问静态资源,也无法在应用程序域之间共享程序集。所以,除非你已十分清楚程序性能是由于首次编译造成的性能问题,否则尽量不要人工生成本地代码。

  JIT很优秀,它不但有编译的本事,还会根据内存资源情况换出使用率低的代码,节省资源,这对于一些基于.Net平台的电子产品是很重要的。基于B/S模式运行的系统,如果使用率较高,可以基本忽略JIT带来的性能损失,因为根据局部性原理与8020原则,常用的模块都是编译完毕的,只有那些不常用的模块,在第一次使用时会被编译,并损失用一些时间。

  相关文章:.Net Discovery系列-深入理解平台机制与性能影响(上)

                      .Net Discovery系列之-深入理解平台机制与性能影响(下)

目录
相关文章
|
2月前
|
开发者 API Windows
从怀旧到革新:看WinForms如何在保持向后兼容性的前提下,借助.NET新平台的力量实现自我进化与应用现代化,让经典桌面应用焕发第二春——我们的WinForms应用转型之路深度剖析
【8月更文挑战第31天】在Windows桌面应用开发中,Windows Forms(WinForms)依然是许多开发者的首选。尽管.NET Framework已演进至.NET 5 及更高版本,WinForms 仍作为核心组件保留,支持现有代码库的同时引入新特性。开发者可将项目迁移至.NET Core,享受性能提升和跨平台能力。迁移时需注意API变更,确保应用平稳过渡。通过自定义样式或第三方控件库,还可增强视觉效果。结合.NET新功能,WinForms 应用不仅能延续既有投资,还能焕发新生。 示例代码展示了如何在.NET Core中创建包含按钮和标签的基本窗口,实现简单的用户交互。
52 0
|
2月前
分享一份 .NET Core 简单的自带日志系统配置,平时做一些测试或个人代码研究,用它就可以了
分享一份 .NET Core 简单的自带日志系统配置,平时做一些测试或个人代码研究,用它就可以了
|
4月前
|
机器学习/深度学习 JSON 测试技术
CNN依旧能战:nnU-Net团队新研究揭示医学图像分割的验证误区,设定先进的验证标准与基线模型
在3D医学图像分割领域,尽管出现了多种新架构和方法,但大多未能超越2018年nnU-Net基准。研究发现,许多新方法的优越性未经严格验证,揭示了验证方法的不严谨性。作者通过系统基准测试评估了CNN、Transformer和Mamba等方法,强调了配置和硬件资源的重要性,并更新了nnU-Net基线以适应不同条件。论文呼吁加强科学验证,以确保真实性能提升。通过nnU-Net的变体和新方法的比较,显示经典CNN方法在某些情况下仍优于理论上的先进方法。研究提供了新的标准化基线模型,以促进更严谨的性能评估。
121 0
|
5月前
|
机器学习/深度学习 算法 数据可视化
MATLAB基于深度学习U-net神经网络模型的能谱CT的基物质分解技术研究
MATLAB基于深度学习U-net神经网络模型的能谱CT的基物质分解技术研究
|
5月前
|
数据采集 存储 监控
.NET智慧手术室管理平台源码
术前访视记录单、手术风险评估表、手术安全核查表、自费药品或耗材、麻醉知情同意书、麻醉记录单、分娩镇痛记录单、麻醉复苏单、术后镇痛记录单、术后访视记录单、压伤风险评估量表、手术清点记录单、护理记录单、输血护理记录单。
81 0
|
26天前
|
开发框架 前端开发 JavaScript
ASP.NET MVC 教程
ASP.NET 是一个使用 HTML、CSS、JavaScript 和服务器脚本创建网页和网站的开发框架。
27 7
|
25天前
|
存储 开发框架 前端开发
ASP.NET MVC 迅速集成 SignalR
ASP.NET MVC 迅速集成 SignalR
38 0
|
2月前
|
开发框架 前端开发 .NET
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
36 0
|
2月前
|
开发框架 前端开发 安全
ASP.NET MVC 如何使用 Form Authentication?
ASP.NET MVC 如何使用 Form Authentication?
|
2月前
|
开发框架 .NET
Asp.Net Core 使用X.PagedList.Mvc.Core分页 & 搜索
Asp.Net Core 使用X.PagedList.Mvc.Core分页 & 搜索
88 0