分享

高效动态语言虚拟机的设计(一) – 概述 ? Tony Huang & Tech

 quasiceo 2014-01-20

(一) – 概述

最近在做Python相关的一些东西,发现Python的性能实在是非常差,所以就深入到Python内部,看了一下它的实现,并对比了几个比较流行的虚拟机的实现,包括:

  • V8 (Javascript)
  • Tamarin (ActionScript 3)
  • Lua 5.0
  • CPython (Python 2.7.2)

做了一定分析和比对,获得了一些灵感,在这里写下来作一个分享的讨论。
Ok,先从计算机是如何将一个高级语言的代码,转变成可以执行的程序说起。

计算机程序语言的机制

众所周知,计算机能够执行的代码是机器码,也就是所谓的二进制。那么一段高级语言的代码要想能够被计算机执行,必须经过这样的一个过程:

  1. 编译 (将源代码编译成目标代码:目标代码是机器码片段的集合,每一段机器码都有一个名字,也就是这段代码的符号)
  2. 连接 (将多个目标文件中的符号连接在一起,形成一个大的可执行机器码,这样计算机(大部分时候是操作系统)就可以加载、执行代码了)

编译器的设计

在早期,编译的过程是直译式的,编译器直接将源代码解析成Token流,再将Token流分析成AST(抽象语法树),然后直接根据抽象语法树中的语法元素生成目标机器的汇编代码,最后再通过汇编器(Assembler)汇编成目标文件。
然而现代的编译器都会有一种中间代码,然后将编译器分成两半(前端和后端)。
前端是语言相关的,负责将原始的语言编译成中间代码;
后端是目标机器相关的,负责将中间代码翻译成目标机器的机器码。
这样做的好处就在于,编译器变得更加可移植了。
当出现一种新的语言时,只要实现一个这个语言的前端,就可以工作在不同的平台和cpu上;
当出现一个新的平台时,只要实现一个后端,就可以支持所有的语言。
GCC就是这样设计的一个范例。

跑题了?

你可能会讲,这不应该是一篇关于虚拟机的文章吗?为何要扯那么多编译的事情呢?
看官勿躁,且听我娓娓道来,这是虚拟机的基础知识:)
我们现在重新整理一下思路,看看这个过程,和每个过程的产物:

阶段 产物
编码 源代码
解析 AST(抽象语法树)
前端代码生成 中间语言
后端代码生成 目标文件
连接 可执行文件

虚拟机的实现

从广义上来说,虚拟机的种类繁多,但我们这里特指跨平台的,用于实现语言功能的虚拟机,例如python虚拟机,javascript虚拟机等等。
从上一节的内容,我们可以看出,假设语言都实现到了生成中间语言这一步,那么虚拟机的实现可以有2种方式:

  • Interpreting (解释执行)

    也就是通过类似while() { switch() {} }的循环,分析中间语言的每条指令,动态解释执行

  • Binary Translation (这个名词大家可能比较陌生,它还有一个大家更加熟知的名字:JIT)

    顾名思义,就是虚拟机实现了从中间语言,到可执行文件的转换的功能,在运行时,将中间语言转换成了可执行文件,最终执行

这两种方式各有特色:

  • Interpreting

    启动非常快速,执行性能相对较差,通常应用于一些对性能本身不是很敏感的语言

  • Binary Translation

    有一个翻译的过程,启动的时候会相对比较慢,但是执行的性能非常好,有时甚至比c代码还要快速(听起来很科幻?)

我们来横向比较一下这几个虚拟机的实现方式:

虚拟机 语言 语言特性 虚拟机实现方式 速度
V8 JavaScript 较丰富 Binary Translation 非常快
Tamarin ActionScript 3(EcmaScript 4) 较丰富 Interpreting + Binary Translation 非常快
Lua 5.0 Lua 5.0 较少 Interpreting 较快
CPython Python 2.7 很丰富 Interpreting 较慢

从这里,我们可以看到使用Jit方式执行的虚拟机明显比较快,而采用解释执行的虚拟机明显较慢。
然而同样是采用解释执行的虚拟机,lua也要比python更快,这不仅仅是由于语言更简单导致的,同时也跟lua虚拟机的实现有关。

V8是一朵奇葩

V8虚拟机可以说是所有的虚拟机里面设计最特别的一款,所有其他的虚拟机都会首先将源代码编译成一种中间代码,如:

虚拟机 中间代码 指令数
Tamarin abc (Adobe Byte Code) 200+
Lua Lua Byte Code 35
Python Python Byte Code 100+

然而,v8虚拟机的方式很特别,它在进行jit的时候直接从ast生成目标平台的汇编代码,并使用内置的宏汇编器生成可执行代码,这样就大大减少了jit过程所消耗的时间。

Lua的指令为什么那么少?

哈哈,细心的读者一定发现了,Lua虚拟机的指令要比其他的虚拟机少很多,甚至不再同一个数量级上,那为什么那么设计呢?
且听下回分解~

Filed under: VM

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多