为什么 .net 使用 JIT 编译器而不是只在目标机器上编译一次代码?

发布于 2024-09-25 10:35:32 字数 63 浏览 8 评论 0原文

标题几乎概括了它,但我想知道为什么像 .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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

淡看悲欢离合 2024-10-02 10:35:32

使用 .NET 或 Java 等中间格式可以获得两件事:

  1. 您可以在任何平台上运行程序,这正是因为代码以中间格式而不是本机代码表示。您只需要为中间格式编写一个解释器即可。
  2. 它允许进行一些在编译时不可能(轻易)实现的运行时优化:例如,您可以利用新 CPU 上的特殊功能,即使这些 CPU 在您编写程序时不存在 - 只有JIT 编译器需要了解这一点。

现在,至于为什么您可能不想在第一次运行时执行编译,然后只是缓存它 - 也可能有几个原因。

如果您在启动之前进行编译,那么用户必须等待第一次运行更长的时间 - 在那个时间点,您无法知道用户实际将使用什么。通过仅在需要时编译您需要的内容,您可以更快地启动,因为您要做的工作更少,并且您不会存储大量用户永远不会使用的代码(对于大型程序来说,这些代码可以有很多代码)。

如果您开始跨会话缓存 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:

  1. You can run the program on any platform, exactly because the code is represented in an intermediate format instead of native code. You just need to write an interpreter for the intermediate format.
  2. It allows for some run-time optimizations which are not (easily) possible at compile-time: for example, you can take advantage of special features on new CPUs, even if those CPUs didn't exist when you wrote your program - only the JIT compiler needs to know about that.

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.

站稳脚跟 2024-10-02 10:35:32

我不编写编译器或运行时,所以我不能肯定地说这一点,但我不相信即时=每次。这只是意味着我们要等到真正需要编译时才进行编译,但是一旦该实例完成编译,就不会再次编译。

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.

憧憬巴黎街头的黎明 2024-10-02 10:35:32

如果将可执行文件移至另一台计算机怎么办?如果它是为第一台机器的特定处理器编译一次,它将无法利用新处理器上可用的(可能)更先进或更高效的指令集。如果您确实想在目标计算机上编译一次,请考虑使用 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.

睫毛上残留的泪 2024-10-02 10:35:32

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.

倾城月光淡如水﹏ 2024-10-02 10:35:32

标题几乎概括了它,但我想知道为什么像 .net 这样的系统每次运行时都会编译代码,而不是只在目标计算机上编译一次?

.NET 确实会在给定目标上缓存将 CIL 编译为本机代码的结果,但这并非微不足道:

  • 从磁盘加载缓存数据和即时重新编译之间需要权衡。
  • 任何形式的运行时代码生成都需要能够即时编译。这不仅是 System.Reflection.Emit 的使用,而且还具体化动态加载程序集的类型,甚至在多态递归期间创建新的值类型。

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?

.NET does cache the results of compiling CIL to native code on a given target but this is not trivial:

  • There is a trade-off between loading cached data from disk and recompiling on-the-fly.
  • Any form of run-time code generation requires the ability to compile on-the-fly. This is not only the use of System.Reflection.Emit but also reification of types from dynamically-loaded assemblies and even the creation of new value types during polymorphic recursion.
十年九夏 2024-10-02 10:35:32

如果直接编译为机器代码,那么您的目标就是特定的系统:

Code --> Machine Code

但是,在 .NET、Java 等中,您的目标是虚拟机。这会生成任何 .NET 兼容虚拟机都可以解释的代码,然后将 JIT 转换为真实的机器代码。

因此,在 .NET 中:

Code --> IL --> VM (executing) --> JIT'd Machine Code

最初看起来更复杂,但请考虑针对多个体系结构:

Code(x86) --> Machine Code(x86)
Code(ppc) --> Machine Code(ppc)
etc... (1x code-set per target)

对比

Code --> IL --> VM(x86) (executing) --> JIT'd x86 Machine Code
                VM(ppc) (executing) --> JIT'd ppc Machine Code
                etc...

我的插图很粗糙,但您会注意到单个 .NET 代码包可以针对多个平台并在多个平台上运行

If you compile directly to machine-code then you've targeted a specific system:

Code --> Machine Code

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:

Code --> IL --> VM (executing) --> JIT'd Machine Code

Initially it looks more complicated, but consider targeting multiple architectures:

Code(x86) --> Machine Code(x86)
Code(ppc) --> Machine Code(ppc)
etc... (1x code-set per target)

Versus

Code --> IL --> VM(x86) (executing) --> JIT'd x86 Machine Code
                VM(ppc) (executing) --> JIT'd ppc Machine Code
                etc...

My illustration is rough, but you'll note that a single package of .NET code can target and run on multiple platforms

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文