编译后的代码如C
占用很少的内存。
解释代码之类的Python
会消耗更多的内存,这是可以理解的。
使用 JIT,程序在运行时(有选择地)编译成机器代码。那么,JIT 程序的内存消耗不应该介于编译程序和解释程序之间吗?
相反,经过 JIT 处理的程序(例如PyPy
)消耗的内存是等效的解释程序(例如 )的几倍Python
。为什么?
编译后的代码如C
占用很少的内存。
解释代码之类的Python
会消耗更多的内存,这是可以理解的。
使用 JIT,程序在运行时(有选择地)编译成机器代码。那么,JIT 程序的内存消耗不应该介于编译程序和解释程序之间吗?
相反,经过 JIT 处理的程序(例如PyPy
)消耗的内存是等效的解释程序(例如 )的几倍Python
。为什么?
跟踪 JIT 编译器会占用更多内存,因为它们不仅需要保留 VM 的字节码,还需要保留直接可执行的机器代码。然而,这只是故事的一半。
大多数 JIT 还将保留大量有关字节码(甚至机器码)的元数据,以允许他们确定需要 JIT 的内容以及可以保留的内容。跟踪 JIT(例如 LuaJIT)还创建跟踪快照,用于在运行时微调代码,执行循环展开或分支重新排序等操作。
有些还保留常用代码段的缓存或快速查找缓冲区以加速 JIT 代码的创建(LuaJIT 通过 DynAsm 执行此操作,如果正确完成,它实际上可以帮助减少内存使用,就像 dynasm 的情况一样)。
内存使用很大程度上取决于所使用的 JIT 引擎及其编译的语言的性质(强类型与弱类型)。一些 JIT 采用了先进的技术,例如基于 SSA 的寄存器分配器和变量活跃度分析,这些优化也有助于消耗内存,以及循环变量提升等更常见的事情。
请注意您所说的内存使用情况。
编译为 C 的代码对编译的机器代码本身使用的内存相对较少。
我希望给定算法的 Python 字节码实际上小于类似算法的编译 C 代码,因为 Python 字节码操作的级别要高得多,因此完成给定任务的操作通常更少。但是一个 Python 程序也会在内存中拥有 Python 解释器的编译代码,这本身就是一个相当庞大和复杂的程序。另外,一个典型的 Python 程序将比典型的 C 程序在内存中拥有更多的标准库(如果 C 程序是静态链接的,它可以去掉它实际上不使用的所有函数,如果它是动态链接的,那么它共享编译后的代码与内存中使用它的任何其他进程)。
然后,PyPy 拥有 JIT 编译器的机器代码,以及从 Python 字节码生成的机器代码(它不会消失,它也必须保留)。因此,您的直觉(即 JITed 系统“应该”消耗介于编译语言和完全解释语言之间的内存)无论如何都不正确。
但除此之外,您还拥有程序运行的数据结构所使用的实际内存。这变化很大,与程序是提前编译、解释还是解释和 JIT 无关。一些编译器优化会减少内存使用(无论是提前应用还是及时应用),但许多实际上会权衡内存使用以提高速度。无论如何,对于处理任何大量数据的程序,它将完全使代码本身使用的内存相形见绌。
当你说:
相反,经过 JIT 处理的程序(例如 PyPy)消耗的内存是等效的解释程序(例如 Python)的数倍。为什么?
你在想什么节目?如果您实际上已经进行了任何比较,那么我从您的问题中猜测它们将介于 PyPy 和 CPython 之间。我知道 PyPy 的许多数据结构实际上比 CPython 的小,但同样,这与 JIT 无关。
如果程序的主要内存使用是代码本身,那么 JIT 编译器会增加巨大的内存开销(对于编译器本身和已编译的代码),并且根本无法通过以下方式“赢回”内存使用优化。如果主要的内存使用是程序数据结构,那么无论是否启用了 JIT,我都会发现 PyPy 使用的内存比 CPython 少得多。
您的“为什么?”并没有一个直接的答案。因为您问题中的陈述并非完全正确。哪个系统使用更多内存取决于许多因素;JIT 编译器的存在与否是一个因素,但它并不总是很重要。