【问题标题】:Why does JIT'ed code consume so much more memory than either compiled or interpreted code?为什么 JIT 的代码比编译或解释的代码消耗更多的内存?
【发布时间】:2012-01-10 01:36:05
【问题描述】:

C 等编译后的代码占用的内存很少。

Python等解释代码会消耗更多内存,这是可以理解的。

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

相反,经过 JIT 处理的程序(例如 PyPy)消耗的内存是等效解释程序(例如 Python)的数倍。为什么?

【问题讨论】:

  • 每个人都在谈论 JIT 是 Pypy 内存使用量增加的原因,但这并不是故事的全部。虽然 Pypy 的一些内存结构更紧凑(例如所有 int 的列表),但 Pypy 有多种可以与之一起使用的垃圾收集器,它们肯定会影响正在使用的内存量。 Pypy 中当前默认的垃圾收集器出于速度考虑不进行引用计数。因此,对象在内存中的保留时间比在 CPython 中的时间长,因此程序在 Pypy 中的内存占用量更大。

标签: compiler-construction programming-languages jit pypy


【解决方案1】:

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

大多数 JIT 还会保留大量有关字节码(甚至是机器码)的元数据,以允许他们确定需要 JIT 的内容以及可以保留的内容。跟踪 JIT(例如 LuaJIT)还会创建跟踪快照,用于在运行时微调代码,执行循环展开或分支重新排序等操作。

有些还保留常用代码段的缓存或快速查找缓冲区以加速 JIT 代码的创建(LuaJIT 通过 DynAsm 执行此操作,如果正确完成,它实际上可以帮助减少内存使用,就像 dynasm 的情况一样) .

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

【讨论】:

  • 另外,JIT 是实时编译,所以它必须权衡优化以换取速度。离线编译器可以花 5 秒时间优化一个函数。 JIT 不是那么多。
  • @RaymondChen:完全依赖于 JIT,如果它写得正确,你不需要权衡 那个 太多的优化,它只是真正昂贵的分析技术放弃了。
  • 此外,JIT 通常是专门的编译器。 “及时”编译的全部意义在于编译器可以等待并查看运行时正在使用的内容并专门为此进行优化。这意味着(取决于 JIT)对于内存中的给定字节码部分,任何时候都可能有多个不同版本的机器码,以及原始字节码,以防出现不适合的新情况任何已编译的版本。
  • 添加 JIT 本身的大小。有些(如 Mono)很小,但有些(如 HotSpot)又大又笨重。
【解决方案2】:

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

编译成 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 编译器的存在与否是一个因素,但并不总是很重要。

【讨论】:

  • 我不同意你的句子“如果程序的主要内存使用是代码本身,那么 JIT 编译器会增加巨大的内存开销......” - 这对于设计良好的热点 JIT 来说是错误的编译器。我也不同意你的句子“如果主要的内存使用是程序数据结构,那么 [...] PyPy 使用的内存比 CPython 少得多”——PyPy 不是那么好。
  • @Atom JIT 编译器在运行时将代码转换为代码。专门的 JIT 编译器必须还保留原始代码(非专门的 JIT 编译器并不是很有趣,因为它可以实现为提前编译器,获得没有在运行时运行它,以及花更多时间进行优化的能力)。因此,它必须至少增加 O(n) 空间开销,其中 n 是 JIT 编译器编译的代码量。加上 JIT 本身的内存......
  • @Atom 确实,通常 JIT 不会编译大部分程序,所以我可能夸大了情况。但是程序中有多少代码是 JIT 编译的,总是取决于程序。当然,对于任何给定的 JIT 系统,与在非 JIT 环境中执行的相同程序相比,有些程序将支付大量内存开销。
  • @Atom Re PyPy 内存使用情况:PyPy 的类实例实现使用的内存使用情况与您在 CPython 中的任何地方使用 __slots__ 大致相同。见morepypy.blogspot.com.au/2010/11/…。在某些情况下,这是非常显着的内存节省。他们还有一些其他的太空胜利。我不知道整个真实程序的总体内存使用量与 CPython 相比如何,但 PyPy 至少对于图片的某些部分来说肯定是“那么好”。因此“我不会感到惊讶......”,而不是“PyPy 肯定使用更少的内存”。
猜你喜欢
  • 2010-12-31
  • 2012-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-25
  • 2017-09-21
  • 2013-05-04
  • 1970-01-01
相关资源
最近更新 更多