如何证明.NET CLR JIT每次运行只编译每个方法一次? [英] How to prove that the .NET CLR JIT compiles every method only once per run?

查看:109
本文介绍了如何证明.NET CLR JIT每次运行只编译每个方法一次?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

一个老问题询问C#是否每次都是JIT编译的,著名的Jon Skeet的回答是:不,每个应用程序只编译一次,只要我们谈论的不是NGENed的桌面应用程序即可。

There's an old question asking whether C# is JIT compiled every time and the answer by famous Jon Skeet is: "no, it's compiled only once per application" as long as we're talking about desktop applications which are not NGENed.

我想知道2009年的信息是否仍然正确,我想通过实验和调试来解决这一问题,可能是通过在JITter上放置一个断点并使用WinDbg命令来检查对象和方法。

I want to know if that information from 2009 is still true and I want to figure that out by experiment and debugging, potentially by putting a breakpoint on the JITter and using WinDbg commands to inspect objects and methods.

我知道.NET内存布局考虑了标头(位于地址A- 4)和在实际数据开始之前每个对象的方法表(地址A + 0)(地址A + 4)。因此,每个对象都有可能具有不同的方法表,从而可能具有不同的JITted方法。

I know that the .NET memory layout considers a Header (at address A-4) and a Method Table (at address A+0) per object before the actual data starts (at address A+4). So it would be possible that each object has a different method table and thus could have different JITted methods.

我们有一个并行编程讲习班,培训师声称,方法针对每个线程的每个对象都是JITted。显然这对我没有意义,我能够编写一个反例应用程序。

We had a workshop for parallel programming and one claim by the trainer was that methods are JITted for every object per thread. That clearly didn't make sense to me and I was able to write a counter example application.

不幸的是,出现了以下其他主题,我也想为此编写演示:

Unfortunately, the following other topics came up, for which I also want to write a demonstration:


  • 新的.NET框架

  • 应用程序域

  • 代码访问安全性

链接答案是在.NET 3.5发布时编写的。从那时起,它没有实质性的更改,即它尚未收到.NET 4.0、4.6和4.6的更新。

The linked answer was written when .NET 3.5 was released. It was not substantially changed since then, i.e. it has not received updates for .NET 4.0, 4.6 and 4.6.

关于应用程序域,我个人认为可以卸载一个应用程序域,用于卸载程序集。如果程序集被卸载,它就消失了,IL代码也随之而来。对于被销毁的IL代码,保留本地代码不会带来太大好处。因此,我可以想象创建一个应用程序域并再次加载该程序集可能导致再次JITting该方法。

Regarding application domains, my personal opinion is that I could unload an application domain, which unloads assemblies. If an assembly is unloaded, it's gone and the IL code goes with it. I don't see much benefit in keeping native code for IL code which was destroyed. Therefore, I could imagine that creating an application domain and loading the assembly again might result in JITting the method again.

关于代码访问安全性,我不确定是否由JIT编译器根据当前权限或是否在运行时通过反射来考虑。如果由JIT编译器完成,则编译后的代码将取决于权限集而有所不同。

Regarding code access security, I'm not sure if it is considered by the JIT compiler based on the current permissions or whether it's done by reflection at runtime. If it's done by the JIT compiler, the compiled code will differ, depending on the permission set.

推荐答案

设计原理 JIT的功能是一次编译一个方法(泛型方法的方法实例),然后重用相同的本机代码。当然,实施过程非常复杂,我将尝试简化答案而不影响准确性。从.NET 2.0到最新的.NET 4.6,所有版本的运行时答案都是相同的(我不了解.NET 1.x,可能是相同的。)

The "design principle" of the JIT is to compile a method (an instantiation of method for generic methods) once and and reuse the same native code. Of course, the implementation is extremely complicated and I'll try simplify the answer without compromising accuracy. The answer is the same for all versions of the runtime since .NET 2.0 and up to the latest .NET 4.6 (I don't know about .NET 1.x, probably the same).

有关运行时分析器回调和ETW事件的文献很少。两者都在尝试JIT编译时发生,但不一定成功。在三种情况下,可能会发生这种情况:1-方法无法满足某些安全性要求; 2-方法验证失败,并且3-不能分配内存来保存要发出的本机代码。因此,JIT启动回调和事件可能高估了方法实际上已被编译的次数。同样,JIT完成回调和事件也不准确。在极少数情况下,它们可能低估了成功编译同一方法的次数。值得一提的是,方法数量JITted性能计数器准确报告所有IL方法在一个进程的所有应用程序域中被共同编译的次数。

The runtime profiler callbacks and the ETW events are poorly documented. Both of them occur when JIT compilation is attempted, but not necessarily succeeded. There are three cases where this can happen: 1- the method fails to meet certain security requirements, 2- the verification of the method failed, and 3- memory could not be allocated to hold the native code to be emitted. Therefore, the JIT start callback and event can overestimate the number of times a method has actually been compiled. Similarly, the JIT completion callback and event are inaccurate. There are few rarely occurring cases in which they might underestimate the number of times the same method has been successfully compiled. It's worth mentioning at this point that the # of Methods JITted performance counter reports accurately the number of times all IL methods have been compiled collectively in all appdomains of a process.

对于特定于应用程序域的程序集,方法分别在每个应用程序域中编译。没有共享(即使有时在技术上可行)。对于与应用程序域无关的程序集,运行时尝试一次编译每个方法并与所有应用程序域共享本机代码。

For appdomain-specific assemblies, methods are compiled in each appdomain separately. There is no sharing (even though it can be technically possible sometimes). For appdomain-neutral assemblies, the runtime attempts to compile each method once and share the native code with all appdomains.


如何证明.NET CLR JIT每次运行
只能编译一次所有方法?

How to prove that the .NET CLR JIT compiles every method only once per run?

在某些情况下在某些情况下(例如,使用后台JIT时以及其他极其微妙的情况),无需执行就可以编译方法。因此,说每个方法每次运行都编译一次是不准确的。

Well, in some cases (such as when using background JIT, and other extremely subtle cases), a method can get compiled without ever getting executed. So saying every method is compiled once per run is inaccurate.

您可以参考CoreCLR JIT源代码以获取更多信息(JIT与.NET中使用的相同)。 NET Framework 4.5+,但此答案适用于旧版本,因为JIT触发机制基本相同)。源代码就是证明。

You can refer to the CoreCLR JIT source code for more information (the JIT is the same one used in the .NET framework 4.5+ but this answer applies to older versions since the JIT triggering mechanism is mostly the same). The source code is the proof.


方法针对每个线程的每个对象都采用了JIT方法

methods are JITted for every object per thread

是的,这没有任何意义。编译范围为appdomains。

Yes, that doesn't make any sense. The scope of compilation is appdomains.

这篇关于如何证明.NET CLR JIT每次运行只编译每个方法一次?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆