翻译 [译] Ruby 2.6 的 JIT 功能,编译和解释型语言的相关说明

lanzhiheng · 2019年02月04日 · 7299 次阅读

一篇阐述编译以及解释相关概念的文章,对于理解计算机语言的编译以及解释的过程有一定的好处,同时还会了解到 JIT 到底是个什么东西。原文链接:https://medium.com/@mich_berr/just-in-time-for-ruby-2-6-an-explanation-of-compiled-and-interpreted-languages-4fd021e7a58


Ruby2.6

Ruby2.6 在几个星期之前发布了,新特性是一个崭新的 Just-In-Time(JIT) 编译器。你可以点击这里读到关于关于新特性的更多细节,但如果你在寻找更多的背景知识,那么我想一个阐述编译型语言,解释型语言,以及 JIT 是如何工作的相关指引会对你有所帮助。

首先,什么是编程语言?所有的编程语言都是机器代码的抽象。机器代码是由位 (许多的 0 和 1) 组成的,可以通过你机器的硬件进行存储和执行。让人类去阅读和编写二进制是非常低效的事情,这也是我们发明编程语言的理由。

解释和编译的不同之处主要在于人类编写的程序是怎么转换成机器可以执行的指令集的。有的时候编程语言并不能明确地划分为是到底是编译型的还是解释型的;编译和解释是把你写的代码转换成机器可读代码的两种不同的策略。我们会在讨论 JIT 的时候了解到编译和解释之间的界线变得越发模糊。

编译

简单来说,编译就是把高级的编程语言转换成机器能够识别的语言。

让我们拿 C 语言来做个例子,它是一门有代表性的编译型语言。为了运行 C 语言写的程序,必须使用像 gcc 或者 clang 这样的编译器,把 C 语言的源代码编译成能适应你计算机的机器码。需要重点指出,不同的电脑可能会有不同的CPU 架构,意味着一台电脑处理 0 和 1 序列的方式跟另一台电脑是不一样。一个编译器会把源代码转换成特定架构的机器代码。一旦你的代码被编译完,它可以按你希望的那样运行在任何具有相同架构的系统上。不过如果你更新了源代码,或者想要把你的程序运行在具有不同架构的机器上的时候,你就需要对代码进行重新编译了。

编译器的一个很好的类比就是人类的翻译人员。跟翻译人员把西班牙语的书翻译成英文书籍那样,一个编译器就是把人类编写的源代码翻译成机器代码。一旦一本书被翻译完,任何懂英语的读者都能够阅读它。然而如果原来西班牙语版本的书籍有所改动,英文版将需要重新翻译相关的部分并再次发布。

解释

不像编译器可以在程序运行之前预先把源代码翻译成机器码,解释器是一行接着一行,一边翻译一边执行。继续之前的类比,计算机解释器就像一个口译人员。他们充当西班牙语以及英语谈话者交流的桥梁,一句句实时地翻译出来。

我们用 Ruby 作为“解释型”语言的一个例子。Ruby1.8 以及更早的版本,那个时候的 Ruby 解释器 (MRI), 它的行为就像上面描述的那样。它读取每一行 Ruby 代码,解析并 token 化,然后使用一个树型的数据结构来执行它。而从版本 1.9 开始,Ruby 切换到一个包含 YARV(Yet Another Ruby Virtual Machine) 的实现。在这个实现里,Ruby 会被预编译成字节码,这样命名是因为它们会占用一个字节的内存空间。一个非常简单的例子,2+3 将会把加法运算转换成字节码的形式,并且接收23作为参数,一旦 Ruby 代码被转换成字节码,这些字节码将会由虚拟机一行一行地执行。把源代码转换成字节码对提升运行速度有重大意义。

Python 当然也会利用字节码,你可以直接在 Python 程序产生的.pyc文件里面看到它。这些文件的作用就像是一个缓存;如果 Python 代码再次运行,却没有作任何修改,可以跳过编译的步骤直接执行相关的字节码文件。当 Ruby 编程成字节码之后,它的字节码只是存储在内存中而不是持久化到文件。

Just-In-Time 编译

我刚刚描述了一些当代的一些解释型语言如今是怎么包含字节码编译这一步骤的,但还有另一种方式,它会使编译与解释之间的界线开始模糊起来,被称为 Just-In-Time 编译。

最流行的 Just-In-Time 编译器就是 Java 虚拟机 (JVM) 了。Java 是一门静态类型的编程语言,它可以直接被编译成机器代码,但是这通常都会通过 JVM 来完成转换。在近期的案例中,Java 代码会被 Javac 编译器编译成 Java 字节码,这些字节码会由 JVM 来翻译并执行。然而 JVM 并不会一句句地去翻译字节码。相反,在这个时候它会尝试去组合像函数那样的有意义的代码块。而要决定怎么去组合这些代码块将会稍微有点耗时,不过最终会使执行更加高效。这个过程被称之为 Just-In-Time 编译,因为它的行为就像是一个编译器,区别就是它会在运行时进行编译。使用 JVM 的好处是它既保留了编译型语言的性能特征又让 Java 可以像解释型语言那样移植到不同的机器上。Java 是世界上最流行的编程语言,很大一部分原因要归功于它的 JVM。

除了 JVM 之外,已经有其他的 VM 项目采用了 JIT 编译。PyPy是一个包含 JIT 功能的 Python 解释器,当然现在的 Ruby 也有可选的 JIT 功能了。

折衷

现在你已经对编译型以及解释型语言有个大概的了解了,那么他们各自是如何权衡的呢?

速度

编译型语言通常会比解释型语言快许多。一个编译好的 C 程序可能要比 Python,Ruby 这样的解释型语言快上好几个量级。然而 Java 的 JIT 解决方案也是非常高效的,它的运行速度可以几乎可以媲美 C 语言所写的程序。

可移植性

为了在不同架构的机器上运行你编译好的程序,你需要重新编译它。当一门语言被解释之后,它的指令集可以在具有不同架构的机器上运行 (当然要有相关的虚拟机)。JVM 就是很好的例子,它结合了解释以及编译两门技术,最大程度地兼顾了速度以及可移植性。

动态类型 VS 静态类型

编译器必须要把应用程序转换并组合成机器指令,这是非常死板的。当声明一个变量的时候,编译器需要明确地知道是什么类型的变量以及需要为它分配多少内存空间。这就是为什么编译通常都要求静态类型。作为对比,解释器一行行地执行相关的程序,因此他们的行为更灵活。

在 Ruby 里面,我们可以写出像2 + 3 或者"a" + "b"这样的代码,解释器会在运行时确定对象的类型,不管是整数的相加,还是字符串的拼接,都会为它们调用正确的方法。

Bugs/调试

解释型语言通常更容易调试,因为程序在遇上错误之前会一直执行。解释器将会告知用户具体是哪一行引发的运行时错误,反之,在编译好的程序里面 bug 比较难以发现。

FAQS

为什么把一门语言看成是“编译型语言”或者“解释型语言”是用词不当的?

一门语言是根据他们的语法以及数据结构来确定的。编译及解释是把语言的语法转换成可以在硬件上运行的形式的两种不同的实现方式。“编译”或者“解释”并不是语言的天性;同一门语言可能会同时包含这两种实现方式。

为什么人们把 Python 看作是解释型语言把 C 看作是编译型语言?

他们都是参考了该语言最通用的实现或者是发行版本。然而 Python 也能够被编译,C 语言当然也可以被解释。

什么是虚拟机?

一个虚拟机就是一切行为像计算机那样的抽象,意味着它能够接收一系列的指令,与硬件的实现方式不同,它是通过软件来实现的。虚拟机通常用于在计算机的一个操作系统上运行其他的操作系统。举个例子,你有一个 windows 的笔记本,但是想要仿真一个 Linux 的操作系统。你会利用一个软件,它隐蔽在你物理机的操作系统跟你想要运行的操作系统之间。这被称为一个“系统虚拟机”。

不要跟之前的例子混淆,那些是“程序虚拟机”。比如,Java 的虚拟机 (JVM) 或者 Ruby 的虚拟机 (YARV)。这些都会被看作是虚拟机,因为它们可以接收指令集并运行相关的字节码。虚拟机的好处是对硬件进行抽象并提供了一个平台独立的编程环境。

为什么人们通常认为 Python 和 Ruby 有解释器,而 Java 有虚拟机?解释器是否也符合虚拟机的定义?

解释器和虚拟机的更多是语义上的区别,或者像这个人所阐述的是“社会构建”上的区别。我觉得解释器是符合虚拟机的定义的。严格意义上,Ruby 和 Python 的解释器都包含能够处理相关字节码的虚拟机。而另一方面,JVM 本质上跟 Ruby 或者 Python 的解释器有所不同。我已经在这篇文章中触及了一小部分的原因,不过剩下的知识已超出这篇文章的范围。

为什么解释一门语言之前先把它编译成字节码能使速度加快?

字节码会比源码耗费更少的内存空间,并且更容易被解释器执行。当一门语言要被编译成字节码,解释器必须要解析所有的语句,把他们转换成字节码,然后解析字节码并执行它们。对一个简单的代码片段而言,这个中间的步骤会小幅度地增加了执行的时间。然而,对那些需要重复执行的代码,比如,循环或者是可复用的方法等,字节码的步骤能够大幅度提升速度。

更多资源:

BaseCS,一个关于编程基础概念的很不错的博客https://medium.com/basecs/a-deeper-inspection-into-compilation-and-interpretation-d98952ebc842

Ruby 原理剖析,深度谈论 Ruby 内部机制的书籍,书中有一些 C 语言代码,程序员应该会感到亲切:http://patshaughnessy.net/ruby-under-a-microscope

Bradfield Academy 上的计算机组成方面的课程,关于计算机的组成概念的优秀课程:https://bradfieldcs.com/

Tyler Elliot Bettilyon的 youtube 视频,Tyler 是我在 Bradfield 上的指导员,是星球上把 CS 概念阐述得最好的人类之一https://www.youtube.com/watch?v=KsZLPTRSleI

暂无回复。
需要 登录 后方可回复, 如果你还没有账号请 注册新账号