Compiler construction 为什么JIT';ed代码比编译或解释的代码消耗更多的内存?

Compiler construction 为什么JIT';ed代码比编译或解释的代码消耗更多的内存?,compiler-construction,programming-languages,jit,pypy,Compiler Construction,Programming Languages,Jit,Pypy,像C这样的编译代码占用很少的内存 诸如Python之类的解释代码会消耗更多内存,这是可以理解的 使用JIT,程序在运行时(有选择地)编译成机器代码。那么JIT程序的内存消耗不应该介于编译程序和解释程序之间吗 相反,JIT程序(如pypypy)消耗的内存是等效解释程序(如Python)的数倍。为什么?跟踪JIT编译器需要占用更多的内存,因为它们不仅需要保留VM的字节码,还需要保留直接可执行的机器码。然而,这只是故事的一半 大多数JIT还将保留大量关于字节码(甚至机器码)的元数据,以便确定哪些需要J

C
这样的编译代码占用很少的内存

诸如
Python
之类的解释代码会消耗更多内存,这是可以理解的

使用JIT,程序在运行时(有选择地)编译成机器代码。那么JIT程序的内存消耗不应该介于编译程序和解释程序之间吗


相反,JIT程序(如
pypypy
)消耗的内存是等效解释程序(如
Python
)的数倍。为什么?

跟踪JIT编译器需要占用更多的内存,因为它们不仅需要保留VM的字节码,还需要保留直接可执行的机器码。然而,这只是故事的一半

大多数JIT还将保留大量关于字节码(甚至机器码)的元数据,以便确定哪些需要JIT,哪些可以单独使用。跟踪JIT(如LuaJIT)还创建跟踪快照,用于在运行时微调代码,执行循环展开或分支重新排序等操作

有些还保留常用代码段的缓存或快速查找缓冲区,以加快JIT代码的创建(LuaJIT通过DynAsm实现这一点,它实际上可以帮助减少正确执行时的内存使用,就像DynAsm一样)

内存使用在很大程度上取决于所使用的JIT引擎及其编译语言的性质(强类型与弱类型)。一些JIT采用了先进的技术,如基于SSA的寄存器分配器和变量活性分析,这些优化还有助于消耗内存,以及循环变量提升等更常见的事情。

请注意您所说的内存使用情况

编译成C的代码对编译后的机器代码本身使用的内存相对较少

我希望给定算法的Python字节码实际上比类似算法的编译C代码小,因为Python字节码操作的级别要高得多,所以完成给定任务的字节码通常较少。但是Python程序的内存中也会有Python解释器的编译代码,这本身就是一个相当大和复杂的程序。另外,一个典型的Python程序在内存中的标准库比一个典型的C程序要多得多(如果C程序是静态链接的,那么C程序可以去掉它实际上不使用的所有函数,如果它是动态链接的,那么它将与内存中使用它的任何其他进程共享编译后的代码)

PyPy还有JIT编译器的机器代码,以及由Python字节码生成的机器代码(不会消失,也必须保留)。因此,您的直觉(JIT系统“应该”消耗编译语言和完全解释语言之间的内存)无论如何都是不正确的

但是在所有这些之上,你已经得到了程序运行的数据结构所使用的实际内存。这方面的差异很大,与程序是否提前编译、解释、解释和JIT无关。一些编译器优化将减少内存使用(无论是提前应用还是及时应用),但许多编译器实际上会牺牲内存使用以提高速度。对于那些处理大量数据的程序来说,无论如何,代码本身所使用的内存都会大大减少

当你说:

相反,JIT’ed程序(如PyPy)的消耗量要多几倍 内存大于等效的解释程序(如Python)。为什么?

你在想什么节目?如果你真的做过任何比较,我从你的问题中猜测他们会在PyPy和CPython之间。我知道PyPy的许多数据结构实际上比CPython的小,但同样,这与JIT无关

如果程序的主要内存使用是代码本身,那么JIT编译器会增加巨大的内存开销(对于编译器本身和编译后的代码而言),并且根本无法通过优化“赢回”内存使用。如果主要的内存使用是程序数据结构,那么无论是否启用JIT,发现PyPy使用的内存比Cpyton少得多,我一点也不会感到惊讶



对于你的“为什么?”没有一个直截了当的答案,因为你问题中的陈述并不完全正确。哪个系统使用更多内存取决于许多因素;JIT编译器的存在与否是一个因素,但并不总是很重要。

此外,JIT是实时编译,因此它必须在优化与速度之间进行权衡。离线编译器可以花费5秒钟优化函数。JIT并不是那么重要。@RaymondChen:完全取决于JIT,如果它写得正确,你不需要权衡太多的优化,它实际上只是放弃了昂贵的分析技术。而且,JIT通常是专门的编译器。“及时”编译的全部意义在于,编译器可以等待并查看运行时使用的内容,并专门为此进行优化。这意味着(取决于JIT),内存中给定字节码段的机器代码在任何时候都可能有多个不同版本,以及原始字节码,以防出现不适合任何编译版本的新情况。添加JIT本身的大小。有些(比如Mono)相当小,但有些(比如HotSpot)又大又笨重。每个人都说JIT是Pypy内存使用增加的原因,但这并不是全部。虽然Pypy的一些内存结构更紧凑(例如所有int的列表),但Pypy有各种垃圾收集器,可以与它一起使用,并且