【问题标题】:How do JIT compilers work on servers?JIT 编译器如何在服务器上工作?
【发布时间】:2016-06-25 20:44:22
【问题描述】:

我认为很明显,如果我们只是解释代码,那么编译相同的代码并执行它会更快。我无法理解的是 jit 编译器如何在服务器上实现比解释语言更好的性能(通常),在服务器上你有数千个连接 - 是否需要为每个连接重新编译代码?如果有变化怎么办?提前感谢您帮助我更好地理解这一点。

【问题讨论】:

  • 解释代码或多或少是假装它是运行用户代码的处理器的代码。开销显然比仅在真实处理器上运行用户代码要高一些。 JIT 所做的就是找出从长远来看,编译用户代码的一部分所带来的收益比编译的开销更大,然后才编译。
  • jit 编译比正常编译然后执行要慢,对吧?我现在无法理解的是在服务器上使用 JIT 以及如果代码被更改会发生什么。

标签: performance compiler-optimization interpreter jit


【解决方案1】:

任何编程语言都可以被解释或编译,甚至两者兼而有之。用编程语言编写的程序的性能受许多因素影响,其中只有一个是使用的是解释器、编译器还是 JIT 编译器。动态类型、动态分派和后期绑定等一些特性很昂贵,使用它们的程序会更慢。语言语法本身会影响性能。口译员可以使用静态优化、动态优化、离线配置文件优化、在线配置文件优化或硬件特定优化。

但是,为了简化事情。我们可以如下比较解释器和 JIT 编译器。解释一个函数意味着执行以下步骤:解析每个语句,优化它,发出机器代码,最后执行它。 JIT编译函数意味着:解析所有语句,优化它们,发出机器码,最后执行它们。

有两个重要的区别。首先,解释器解析高级语言语法,而 JIT 编译器解析中级语言语法。因此,JIT 编译器执行此步骤的速度要快得多。其次,JIT 编译器可以通过同时考虑所有语句来更好地优化代码,而纯解释器一次优化一条语句。因此,使用 JIT 编译器时,发出的二进制代码的性能会更好。

如您所见,与解释器相比,JIT 编译器提供了更多的性能机会。无论您是在客户端还是服务器上,这都适用。请注意,一旦函数被 JIT 编译,就不必再次 JIT 编译。因此,只有第一个用户请求会产生编译开销。所有其他人都在全速处理。

【讨论】:

  • 实际上,服务器上的大多数解释型语言(Python、Ruby,甚至是现在的 PHP)在加载时都会编译成字节码并在 VM 中运行,因此大部分优化也已完成。尽管如此,反复运行时,在 VM 中运行它比使用 JIT 慢。
  • You may want to check what interpreted language means。 Python、Ruby 或 PHP(在它们的参考实现中)没有 JIT 编译器,而是在解释字节码的 VM 中运行。这比运行 JIT 编译器生成的本机代码要慢。
  • @StenSoft 是的。以 Python 为例,除了简短的脚本外,CPython 比 PyPy JIT 慢几倍。但是 JIT 编译器同时处理更多的代码,因此可以更好地优化它。另一方面,CPython 的性能主要依赖于有限的窥视孔优化。
【解决方案2】:

我认为很明显,如果我们只花时间解释代码,那么编译和执行相同的代码会更快。

嗯,不(或至少“何时”)?

将源代码转换为 CPU 真正理解的东西需要工作。有两个主要考虑因素:

a) 当这项工作完成时。是否提前/在执行任何操作之前完成工作,以便在程序运行时没有人关心它;还是在每个小片段(例如源代码语句)执行之前完成;或者是其他东西?请注意,这项工作可以(并且通常是)在多个地方拆分并完成(例如,“编译和优化以提前为 VM 生成字节码,然后做更多的工作将字节码转换为 CPU在运行时理解”)。

b) 工作做得如何。转换涉及优化,有些优化相对较快(“窥视孔”),有些优化非常昂贵。例如,假设一个程序员在某处使用int foo = 4; 创建了一个全局变量,然后另一个程序员在其他地方写了return foo * 2;;因此,在优化过程中,您希望搜索数百万行代码,以希望证明全局变量永远不会被修改,因为也许您可以将return foo *2; 转换为更快的return 8;。再举一个例子,仅仅试图以最佳方式计算寄存器分配(哪些变量进入哪些寄存器)是一个已知的“NP-complete”问题。

我无法理解的是 jit 编译器如何在服务器上实现比解释语言更好的性能(通常),在服务器上有数千个连接 - 是否需要为每个连接重新编译代码

纯解释器的性能极差。每次执行每个小片段(源代码的每个语句,或者如果提前预编译的每个字节码)执行时,都会发生将代码转换为 CPU 可以理解的工作,即使这项工作已经完成以前做过数百万次;而且因为它正在处理如此微小的部分,几乎所有优化都是不可能的。

纯 JIT 性能非常差。将代码转换为 CPU 可以理解的内容所涉及的工作在第一次执行一小段(源代码的每个线性语句组,或者如果提前预编译的字节码线性组)被执行时发生,即使这项工作只需要一次;并且因为它在小块上工作,大多数优化是不可能的。

为了避免一些问题;大多数现代 VM 使用“混合解释和 JIT”方法,其中(例如)它们可能会在第一次执行代码时解释代码(以避免 JIT 的费用),但在第二次执行时切换到 JIT(假设如果某些东西是执行两次它可能会执行多次,希望避免“将代码转换为 CPU 理解数百万次(如果它最终被执行数百万次)”的问题)。不过……

“混合解释和 JIT”性能不佳。它仍然无法进行任何昂贵的优化,仍然无法在代码执行之前转换为原生一次(并避免“每次启动程序时转换”问题)等等。

本机代码编译器往往只会为您提供足够的性能。它仍然远非完美。

要了解的重要一点是性能是相对的。如果一件事的性能很差(与完美的优化器相比),而另一件事的性能很差(与完美的优化器相比);那么“坏”与“非常坏”相比是好的。通过更改参考点(例如将所有内容与药物上的猴子进行比较),您可以说提前编译为本机可以提供非常好的性能(与药物上的猴子相比),“混合解释和 JIT”可以提供非常好的性能(比较对一只吸毒的猴子),纯 JIT 提供了良好的性能(与一只吸毒的猴子相比),纯解释提供了足够的性能(与一只吸毒的猴子相比)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-07-17
    • 2021-03-06
    • 1970-01-01
    • 2010-10-06
    • 1970-01-01
    • 2011-02-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多