为什么 .net 使用 JIT 编译器而不是只在目标机器上编译一次代码?
标题几乎概括了它,但我想知道为什么像 .net 这样的系统每次运行时都会编译代码,而不是只在目标机器上编译一次?
The title pretty much sums it up but I was wondering why systems like .net compile code every time it is run instead of just compiling it once on the target machine?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
使用 .NET 或 Java 等中间格式可以获得两件事:
现在,至于为什么您可能不想在第一次运行时执行编译,然后只是缓存它 - 也可能有几个原因。
如果您在启动之前进行编译,那么用户必须等待第一次运行更长的时间 - 在那个时间点,您无法知道用户实际将使用什么。通过仅在需要时编译您需要的内容,您可以更快地启动,因为您要做的工作更少,并且您不会存储大量用户永远不会使用的代码(对于大型程序来说,这些代码可以有很多代码)。
如果您开始跨会话缓存 JIT 代码,则需要跟踪已编译的内容,然后将其存储到磁盘。对于大型程序,您可能需要从磁盘加载大量本机代码。磁盘 I/O 非常昂贵,因此等待磁盘可能比重新 JIT 花费更长的时间。此外,您需要跟踪缓存的可用时间。如果硬件发生变化,您可能需要重新 JIT 以应用一些新的优化。如果程序发生更改,您将无法使用任何旧的编译代码。如果运行时编译器发生更改,则可能已修复安全错误,并且您需要重新编译以确保该错误不会保留在本机代码中。
基本上,JIT 编译器突然有很多工作要做(包括处理缓存的磁盘 I/O),并且变得更加复杂和缓慢,从而减少了 JIT'ing 的意义。
现在,这并不意味着有时预编译某些程序集不是有利的,正如 Matthew Ferreira 指出的那样,这就是 ngen 工具可以做的事情 - 但在一般情况下,这是不值得这样做的,因为 JIT 编译通常足够快。
There are two things to be gained by using an intermediate format like .NET or Java:
Now, as for why you might not want to perform the compilation on the first run and then just cache that - there can be a couple of reasons for that as well.
If you compile before startup, then the user has to wait a lot longer for that first run - at that point in time, you can't know what the user will actually use. By only compiling what you need, when you need it, you can start much quicker, simply because you have less work to do, and you don't store a lot of code that the user will never use (which for a large program can be a lot of code).
If you start caching the JIT'ted code across sessions, you need to keep track of what has already been compiled and then store it to disk. For a large program, you might have a lot of native code to load from disk. Disk I/O is quite expensive, so it might just take longer to wait for the disk than to re-JIT it. Additionally, you need to keep track of how long that cache is usable. If the hardware changes, you might want to re-JIT in order to apply some new optimizations. If the program changes, you can't use any of your old compiled code. If the run-time compiler changes, then a security bug might have been fixed, and you need to recompile to make sure that bug doesn't remain in your native code.
Basically, the JIT compiler suddenly has a lot more work to do (including disk I/O to deal with the cache) and becomes much more complicated and slow, reducing the point of JIT'ing.
Now, that doesn't mean that it can't sometimes be advantageous to pre-compile certain assemblies, and as Matthew Ferreira points out, that's what the ngen tool can do - but in the general case, it's just not worth doing that, because JIT compilation is often more than fast enough.
我不编写编译器或运行时,所以我不能肯定地说这一点,但我不相信即时=每次。这只是意味着我们要等到真正需要编译时才进行编译,但是一旦该实例完成编译,就不会再次编译。
I don't write compilers or run-times, so I can't say this for certain, but I don't believe just-in-time = every time. It just means we wait until we really need to compile, but once the compile is done for that instance, it won't be compiled again.
如果将可执行文件移至另一台计算机怎么办?如果它是为第一台机器的特定处理器编译一次,它将无法利用新处理器上可用的(可能)更先进或更高效的指令集。如果您确实想在目标计算机上编译一次,请考虑使用 ngen.exe 在应用程序安装过程中。
What if you moved the executable to another machine? If it was compiled once for the first machine's specific processor, it would not be able to take advantage of a (possibly) more advanced or efficient instruction set available on the new processor. If you really want to compile once on the target machine, consider using ngen.exe during your application's installation.
JIT 编译的另一个优点:10 或 15 年前编写的代码只需使用最先进的 JIT 运行即可获得显着的性能提升,而无需对代码进行任何更改。
Another advantage of JIT compilation: code that was written 10 or 15 years ago can get a significant performance boost by simply being run with a state-of-the art JIT, without any changes to the code at all.
.NET 确实会在给定目标上缓存将 CIL 编译为本机代码的结果,但这并非微不足道:
.NET does cache the results of compiling CIL to native code on a given target but this is not trivial:
System.Reflection.Emit
but also reification of types from dynamically-loaded assemblies and even the creation of new value types during polymorphic recursion.如果直接编译为机器代码,那么您的目标就是特定的系统:
但是,在 .NET、Java 等中,您的目标是虚拟机。这会生成任何 .NET 兼容虚拟机都可以解释的代码,然后将 JIT 转换为真实的机器代码。
因此,在 .NET 中:
最初看起来更复杂,但请考虑针对多个体系结构:
对比
我的插图很粗糙,但您会注意到单个 .NET 代码包可以针对多个平台并在多个平台上运行
If you compile directly to machine-code then you've targeted a specific system:
However, in .NET, Java, etc. what you are targeting is the virtual machine. This produces code which any .NET compliant VM can interpret, and in turn JIT into real machine code.
So, in .NET:
Initially it looks more complicated, but consider targeting multiple architectures:
Versus
My illustration is rough, but you'll note that a single package of .NET code can target and run on multiple platforms