首页域名资讯 正文

高效动态语言虚拟机的设计(一)

2024-10-05 9 0条评论

高效动态语言虚拟机的设计(一)

  • 作者:新网
  • 来源:新网
  • 浏览:100
  • 2018-05-09 14:43:06

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

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

< div>  V8 (Javascript)   Tamarin (ActionScript 3)   Lua 5.0   CPython (Python 2.7.2)   做了一定分析和比对,获得了一些灵感,在这里写下来作一个分享的讨论。   Ok,先从计算机是如何将一个高级语言的代码,转变成可以执行的程序说起。   计算机程序语言的机制   众所周知,计算机能够执行的代码是机器码,也就是所谓的二进制。那么一段高级语言的代码要想能够被计算机执行,必须经过这样的一个过程:   编译 (将源代码编译成目标代码:目标代码是机器码片段的集合,每一段机器码都有一个名字,也就是这段代码的符号)   连接 (将多个目标文件中的符号连接在一起,形成一个大的可执行机器码,这样计算机(大部分时候是 操作系统)就可以加载、执行代码了)   编译器的设计   在早期,编译的过程是直译式的,编译器直接将源代码解析成Token流,再将Token流分析成AST(抽象语法树),然后直接根据抽象语法树中的语法元素生成目标机器的汇编代码,最后再通过汇编器(Assembler)汇编成目标文件。   然而现代的编译器都会有一种中间代码,然后将编译器分成两半(前端和后端)。   前端是语言相关的,负责将原始的语言编译成中间代码;   后端是目标机器相关的,负责将中间代码翻译成目标机器的机器码。   这样做的好处就在于,编译器变得更加可移植了。   当出现一种新的语言时,只要实现一个这个语言的前端,就可以工作在不同的平台和cpu上;   当出现一个新的平台时,只要实现一个后端,就可以支持所有的语言。   GCC就是这样设计的一个范例。   从上一节的内容,我们可以看出,假设语言都实现到了生成中间语言这一步,那么虚拟机的实现可以有2种方式:   Interpreting (解释执行)   也就是通过类似while() { switch() {} }的循环,分析中间语言的每条指令,动态解释执行   Binary Translation (这个名词大家可能比较陌生,它还有一个大家更加熟知的名字:JIT)   顾名思义,就是虚拟机实现了从中间语言,到可执行文件的转换的功能,在运行时,将中间语言转换成了可执行文件,最终执行   这两种方式各有特色:   Interpreting   启动非常快速,执行性能相对较差,通常应用于一些对性能本身不是很敏感的语言   Binary Translation   有一个翻译的过程,启动的时候会相对比较慢,但是执行的性能非常好,有时甚至比c代码还要快速(听起来很科幻?)   我们来横向比较一下这几个虚拟机的实现方式:   虚拟机语言语言特性虚拟机实现方式速度   V8JavaScript较丰富Binary Translation非常快   TamarinActionScript 3(EcmaScript 4)较丰富Interpreting + Binary Translation非常快   Lua 5.0Lua 5.0较少Interpreting较快   CPythonPython 2.7很丰富Interpreting较慢   从这里,我们可以看到使用Jit方式执行的虚拟机明显比较快,而采用解释执行的虚拟机明显较慢。   然而同样是采用解释执行的虚拟机,lua也要比 python更快,这不仅仅是由于语言更简单导致的,同时也跟lua虚拟机的实现有关。   V8是一朵奇葩   V8虚拟机可以说是所有的虚拟机里面设计最特别的一款,所有其他的虚拟机都会首先将源代码编译成一种中间代码,如:   虚拟机中间代码指令数   Tamarinabc (Adobe Byte Code)200+   LuaLua Byte Code35   PythonPython Byte Code100+   然而,v8虚拟机的方式很特别,它在进行jit的时候直接从ast生成目标平台的汇编代码,并使用内置的宏汇编器生成可执行代码,这样就大大减少了jit过程所消耗的时间。   Lua的指令为什么那么少?   细心的读者一定发现了,Lua虚拟机的指令要比其他的虚拟机少很多,甚至不再同一个数量级上,那为什么那么设计呢?  最近在做Python相关的一些东西,发现Python的性能实在是非常差,所以就深入到Python内部,看了一下它的实现,并对比了几个比较流行的虚拟机的实现,包括:   V8 (Javascript)   Tamarin (ActionScript 3)   Lua 5.0   CPython (Python 2.7.2)   做了一定分析和比对,获得了一些灵感,在这里写下来作一个分享的讨论。   Ok,先从计算机是如何将一个高级语言的代码,转变成可以执行的程序说起。   计算机程序语言的机制   众所周知,计算机能够执行的代码是机器码,也就是所谓的二进制。那么一段高级语言的代码要想能够被计算机执行,必须经过这样的一个过程:   编译 (将源代码编译成目标代码:目标代码是机器码片段的集合,每一段机器码都有一个名字,也就是这段代码的符号)   连接 (将多个目标文件中的符号连接在一起,形成一个大的可执行机器码,这样计算机(大部分时候是操作系统)就可以加载、执行代码了)   编译器的设计   在早期,编译的过程是直译式的,编译器直接将源代码解析成Token流,再将Token流分析成AST(抽象语法树),然后直接根据抽象语法树中的语法元素生成目标机器的汇编代码,最后再通过汇编器(Assembler)汇编成目标文件。   然而现代的编译器都会有一种中间代码,然后将编译器分成两半(前端和后端)。   前端是语言相关的,负责将原始的语言编译成中间代码;   后端是目标机器相关的,负责将中间代码翻译成目标机器的机器码。   这样做的好处就在于,编译器变得更加可移植了。   当出现一种新的语言时,只要实现一个这个语言的前端,就可以工作在不同的平台和cpu上;   当出现一个新的平台时,只要实现一个后端,就可以支持所有的语言。   GCC就是这样设计的一个范例。   从上一节的内容,我们可以看出,假设语言都实现到了生成中间语言这一步,那么虚拟机的实现可以有2种方式:   Interpreting (解释执行)   也就是通过类似while() { switch() {} }的循环,分析中间语言的每条指令,动态解释执行   Binary Translation (这个名词大家可能比较陌生,它还有一个大家更加熟知的名字:JIT)   顾名思义,就是虚拟机实现了从中间语言,到可执行文件的转换的功能,在运行时,将中间语言转换成了可执行文件,最终执行   这两种方式各有特色:   Interpreting   启动非常快速,执行性能相对较差,通常应用于一些对性能本身不是很敏感的语言   Binary Translation   有一个翻译的过程,启动的时候会相对比较慢,但是执行的性能非常好,有时甚至比c代码还要快速(听起来很科幻?)   我们来横向比较一下这几个虚拟机的实现方式:   虚拟机语言语言特性虚拟机实现方式速度   V8JavaScript较丰富Binary Translation非常快   TamarinActionScript 3(EcmaScript 4)较丰富Interpreting + Binary Translation非常快   Lua 5.0Lua 5.0较少Interpreting较快   CPythonPython 2.7很丰富Interpreting较慢   从这里,我们可以看到使用Jit方式执行的虚拟机明显比较快,而采用解释执行的虚拟机明显较慢。   然而同样是采用解释执行的虚拟机,lua也要比python更快,这不仅仅是由于语言更简单导致的,同时也跟lua虚拟机的实现有关。   V8是一朵奇葩   V8虚拟机可以说是所有的虚拟机里面设计最特别的一款,所有其他的虚拟机都会首先将源代码编译成一种中间代码,如:   虚拟机中间代码指令数   Tamarinabc (Adobe Byte Code)200+   LuaLua Byte Code35   PythonPython Byte Code100+   然而,v8虚拟机的方式很特别,它在进行jit的时候直接从ast生成目标平台的汇编代码,并使用内置的宏汇编器生成可执行代码,这样就大大减少了jit过程所消耗的时间。   Lua的指令为什么那么少?   细心的读者一定发现了,Lua虚拟机的指令要比其他的虚拟机少很多,甚至不再同一个数量级上,那为什么那么设计呢?

文章版权及转载声明

本文作者:亿网 网址:https://edns.com/ask/post/149426.html 发布于 2024-10-05
文章转载或复制请以超链接形式并注明出处。